Removed source code from this repository to avoid confusion master
authorKristoffer Grönlund <kristoffer.gronlund@purplescout.se>
Sat, 9 Jan 2010 13:36:18 +0000 (14:36 +0100)
committerKristoffer Grönlund <kristoffer.gronlund@purplescout.se>
Sat, 9 Jan 2010 13:36:18 +0000 (14:36 +0100)
62 files changed:
AUTHORS [deleted file]
COPYING [deleted file]
ICONS_LICENSE [deleted file]
Makefile [deleted file]
README.md
data/26x26/jamaendo.png [deleted file]
data/40x40/jamaendo.png [deleted file]
data/64x64/jamaendo.png [deleted file]
data/album.png [deleted file]
data/bg.png [deleted file]
data/bg.xcf [deleted file]
data/icon_artist.png [deleted file]
data/icon_download.png [deleted file]
data/icon_favorite.png [deleted file]
data/icon_license.png [deleted file]
data/jamaendo-26.base64 [deleted file]
data/jamaendo.desktop [deleted file]
data/jamaendo.png [deleted file]
data/jamaendo.xcf [deleted file]
data/logo_2600_710.png [deleted file]
debian/changelog [deleted file]
debian/compat [deleted file]
debian/control [deleted file]
debian/copyright [deleted file]
debian/jamaendo.menu [deleted file]
debian/optify [deleted file]
debian/postinst [deleted file]
debian/pycompat [deleted file]
debian/rules [deleted file]
helldon/__init__.py [deleted file]
jamaendo/__init__.py [deleted file]
jamaendo/api.py [deleted file]
jamaui/__init__.py [deleted file]
jamaui/albumlist.py [deleted file]
jamaui/colors.py [deleted file]
jamaui/console.py [deleted file]
jamaui/draw.py [deleted file]
jamaui/favorites.py [deleted file]
jamaui/featured.py [deleted file]
jamaui/fetcher.py [deleted file]
jamaui/listbox.py [deleted file]
jamaui/ossohelper.py [deleted file]
jamaui/player.py [deleted file]
jamaui/playerwindow.py [deleted file]
jamaui/playlists.py [deleted file]
jamaui/postoffice.py [deleted file]
jamaui/radios.py [deleted file]
jamaui/refresh.py [deleted file]
jamaui/search.py [deleted file]
jamaui/settings.py [deleted file]
jamaui/showalbum.py [deleted file]
jamaui/showartist.py [deleted file]
jamaui/showplaylist.py [deleted file]
jamaui/songposition.py [deleted file]
jamaui/ui.py [deleted file]
jamaui/util.py [deleted file]
scripts/jamaendo [deleted file]
scripts/player [deleted file]
setup.py [deleted file]
tests/__init__.py [deleted file]
tests/testicle [deleted file]
welcome [deleted file]

diff --git a/AUTHORS b/AUTHORS
deleted file mode 100644 (file)
index b02d8c1..0000000
--- a/AUTHORS
+++ /dev/null
@@ -1,13 +0,0 @@
-Written by:
-
-Kristoffer Grönlund <kristoffer.gronlund@purplescout.se>
-
-Some icons by Joseph Wain, under a Creative Commons Attribution license:
-See more at http://glyphish.com/
-and http://creativecommons.org/licenses/by/3.0/us/
-
-Based on Panucci, by The Panucci Audiobook and Podcast Player Project:
-
-Thomas Perl <thp@thpinfo.com>
-Nick <me@nikosapi.org>
-Matthew Taylor <luv2code@gmail.com>
diff --git a/COPYING b/COPYING
deleted file mode 100644 (file)
index 10926e8..0000000
--- a/COPYING
+++ /dev/null
@@ -1,675 +0,0 @@
-                    GNU GENERAL PUBLIC LICENSE
-                       Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                            Preamble
-
-  The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
-  The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works.  By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users.  We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors.  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
-them 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 prevent others from denying you
-these rights or asking you to surrender the rights.  Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received.  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.
-
-  Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
-  For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software.  For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
-  Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so.  This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software.  The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable.  Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products.  If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
-  Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary.  To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                       TERMS AND CONDITIONS
-
-  0. Definitions.
-
-  "This License" refers to version 3 of the GNU General Public License.
-
-  "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
-  "The Program" refers to any copyrightable work licensed under this
-License.  Each licensee is addressed as "you".  "Licensees" and
-"recipients" may be individuals or organizations.
-
-  To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy.  The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
-  A "covered work" means either the unmodified Program or a work based
-on the Program.
-
-  To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy.  Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
-  To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies.  Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
-  An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License.  If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
-  1. Source Code.
-
-  The "source code" for a work means the preferred form of the work
-for making modifications to it.  "Object code" means any non-source
-form of a work.
-
-  A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
-  The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form.  A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
-  The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities.  However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work.  For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
-  The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
-  The Corresponding Source for a work in source code form is that
-same work.
-
-  2. Basic Permissions.
-
-  All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met.  This License explicitly affirms your unlimited
-permission to run the unmodified Program.  The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work.  This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
-  You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force.  You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright.  Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
-  Conveying under any other circumstances is permitted solely under
-the conditions stated below.  Sublicensing is not allowed; section 10
-makes it unnecessary.
-
-  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
-  No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
-  When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
-  4. Conveying Verbatim Copies.
-
-  You may convey 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;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
-  You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
-  5. Conveying Modified Source Versions.
-
-  You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
-    a) The work must carry prominent notices stating that you modified
-    it, and giving a relevant date.
-
-    b) The work must carry prominent notices stating that it is
-    released under this License and any conditions added under section
-    7.  This requirement modifies the requirement in section 4 to
-    "keep intact all notices".
-
-    c) You must license the entire work, as a whole, under this
-    License to anyone who comes into possession of a copy.  This
-    License will therefore apply, along with any applicable section 7
-    additional terms, to the whole of the work, and all its parts,
-    regardless of how they are packaged.  This License gives no
-    permission to license the work in any other way, but it does not
-    invalidate such permission if you have separately received it.
-
-    d) If the work has interactive user interfaces, each must display
-    Appropriate Legal Notices; however, if the Program has interactive
-    interfaces that do not display Appropriate Legal Notices, your
-    work need not make them do so.
-
-  A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit.  Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
-  6. Conveying Non-Source Forms.
-
-  You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
-    a) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by the
-    Corresponding Source fixed on a durable physical medium
-    customarily used for software interchange.
-
-    b) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by a
-    written offer, valid for at least three years and valid for as
-    long as you offer spare parts or customer support for that product
-    model, to give anyone who possesses the object code either (1) a
-    copy of the Corresponding Source for all the software in the
-    product that is covered by this License, on a durable physical
-    medium customarily used for software interchange, for a price no
-    more than your reasonable cost of physically performing this
-    conveying of source, or (2) access to copy the
-    Corresponding Source from a network server at no charge.
-
-    c) Convey individual copies of the object code with a copy of the
-    written offer to provide the Corresponding Source.  This
-    alternative is allowed only occasionally and noncommercially, and
-    only if you received the object code with such an offer, in accord
-    with subsection 6b.
-
-    d) Convey the object code by offering access from a designated
-    place (gratis or for a charge), and offer equivalent access to the
-    Corresponding Source in the same way through the same place at no
-    further charge.  You need not require recipients to copy the
-    Corresponding Source along with the object code.  If the place to
-    copy the object code is a network server, the Corresponding Source
-    may be on a different server (operated by you or a third party)
-    that supports equivalent copying facilities, provided you maintain
-    clear directions next to the object code saying where to find the
-    Corresponding Source.  Regardless of what server hosts the
-    Corresponding Source, you remain obligated to ensure that it is
-    available for as long as needed to satisfy these requirements.
-
-    e) Convey the object code using peer-to-peer transmission, provided
-    you inform other peers where the object code and Corresponding
-    Source of the work are being offered to the general public at no
-    charge under subsection 6d.
-
-  A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
-  A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling.  In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage.  For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product.  A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
-  "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source.  The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
-  If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information.  But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
-  The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed.  Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
-  Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
-  7. Additional Terms.
-
-  "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law.  If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
-  When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it.  (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.)  You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
-  Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
-    a) Disclaiming warranty or limiting liability differently from the
-    terms of sections 15 and 16 of this License; or
-
-    b) Requiring preservation of specified reasonable legal notices or
-    author attributions in that material or in the Appropriate Legal
-    Notices displayed by works containing it; or
-
-    c) Prohibiting misrepresentation of the origin of that material, or
-    requiring that modified versions of such material be marked in
-    reasonable ways as different from the original version; or
-
-    d) Limiting the use for publicity purposes of names of licensors or
-    authors of the material; or
-
-    e) Declining to grant rights under trademark law for use of some
-    trade names, trademarks, or service marks; or
-
-    f) Requiring indemnification of licensors and authors of that
-    material by anyone who conveys the material (or modified versions of
-    it) with contractual assumptions of liability to the recipient, for
-    any liability that these contractual assumptions directly impose on
-    those licensors and authors.
-
-  All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10.  If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term.  If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
-  If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
-  Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
-  8. Termination.
-
-  You may not propagate or modify a covered work except as expressly
-provided under this License.  Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
-  However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
-  Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-  Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License.  If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
-  9. Acceptance Not Required for Having Copies.
-
-  You are not required to accept this License in order to receive or
-run a copy of the Program.  Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance.  However,
-nothing other than this License grants you permission to propagate or
-modify any covered work.  These actions infringe copyright if you do
-not accept this License.  Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
-  10. Automatic Licensing of Downstream Recipients.
-
-  Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License.  You are not responsible
-for enforcing compliance by third parties with this License.
-
-  An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations.  If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
-  You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License.  For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
-  11. Patents.
-
-  A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based.  The
-work thus licensed is called the contributor's "contributor version".
-
-  A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version.  For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
-  Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
-  In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement).  To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
-  If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients.  "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
-  If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
-  A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License.  You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
-  Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
-  12. No Surrender of Others' Freedom.
-
-  If 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 convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all.  For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
-  13. Use with the GNU Affero General Public License.
-
-  Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work.  The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
-  14. Revised Versions of this License.
-
-  The Free Software Foundation may publish revised and/or new versions of
-the GNU 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 that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation.  If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
-  If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
-  Later license versions may give you additional or different
-permissions.  However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
-  15. Disclaimer of Warranty.
-
-  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.
-
-  16. Limitation of Liability.
-
-  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-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.
-
-  17. Interpretation of Sections 15 and 16.
-
-  If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
-                     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
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    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 3 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, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-  If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
-    <program>  Copyright (C) <year>  <name of author>
-    This program 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, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
-  You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-<http://www.gnu.org/licenses/>.
-
-  The GNU 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.  But first, please read
-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
-
diff --git a/ICONS_LICENSE b/ICONS_LICENSE
deleted file mode 100644 (file)
index 5fa3c99..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-Created by Joseph Wain (see http://penandthink.com) at and probably downloaded from http://glyphish.com
-
-This work is licensed under the Creative Commons Attribution 3.0 United States License. To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/us/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
-
-You are free to share it and to remix it remix under the following conditions:
-
-* You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
-* For any reuse or distribution, you must make clear to others the license terms of this work. The best way to do this is with a link to http://creativecommons.org/licenses/by/3.0/us/
-* Any of the above conditions can be waived if you get permission from the copyright holder (send me an email!).
-* Apart from the remix rights granted under this license, nothing in this license impairs or restricts the author's moral rights.
-
-ATTRIBUTION -- a note reading "icons by Joseph Wain / glyphish.com" or similar, plus a link back to glyphish.com from your app's website, is the preferred form of attribution. Also acceptable would be, like, a link from within your iPhone application, or from the iTunes store page, but those aren't as useful to other people. If none of these work for you, please contact hello@glyphish.com and we can work something out.
-
-USE WITHOUT ATTRIBUTION -- If attribution is not possible, workable or desirable for your application, contact hello@glyphish.com for commercial non-attributed licensing terms.
\ No newline at end of file
diff --git a/Makefile b/Makefile
deleted file mode 100644 (file)
index b48fe15..0000000
--- a/Makefile
+++ /dev/null
@@ -1,71 +0,0 @@
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-
-PREFIX ?= /usr
-DESTDIR ?= /
-
-JAMAENDOBIN = scripts/jamaendo
-JAMAENDOVERSION = `grep -m 1 VERSION jamaui/ui.py | cut -d "'" -f 2`
-#MESSAGESPOT = data/messages.pot
-
-
-PYTHON ?= /usr/bin/python2.5
-
-all:
-       @echo "Possible make targets:"
-       @echo "    install - install the package"
-       @echo "    clean - remove the build files"
-       @echo "    distclean - remove build files + dist target"
-
-install: python-install post-install install-schemas
-
-python-install:
-       $(PYTHON) setup.py install --optimize 2 --root=$(DESTDIR) --prefix=$(PREFIX)
-
-post-install:
-       gtk-update-icon-cache -f -i $(DESTDIR)$(PREFIX)/share/icons/hicolor/
-       update-desktop-database $(DESTDIR)$(PREFIX)/share/applications/
-
-clean:
-       rm -rf build
-       rm -f jamaendo/*.pyc jamaendo/*.pyo
-       rm -f jamaui/*.pyc jamaui/*.pyo
-#      make -C data/po clean
-
-distclean: clean
-       rm -rf dist
-
-# See: http://wiki.maemo.org/Uploading_to_Extras#Debian_tooling
-build-package:
-       dpkg-buildpackage -rfakeroot -sa -kkristoffer.gronlund@purplescout.se -i -I.git
-
-#messagespot:
-#      xgettext -k_ --from-code utf-8 --language Python \
-#        -o $(MESSAGESPOT) scripts/jamaendo jamaendo/*.py jamaui/*.py
-#      sed -i \
-#        -e 's/SOME DESCRIPTIVE TITLE/Jamaendo translation template/g' \
-#        -e 's/THE PACKAGE'"'"'S COPYRIGHT HOLDER/Jamaendo Contributors/g' \
-#        -e 's/YEAR/2010/g' \
-#        -e 's/FIRST AUTHOR <EMAIL@ADDRESS>/Nick Nobody <me@nikosapi.org>/g' \
-#        -e 's/PACKAGE VERSION/Jamaendo '$(JAMAENDOVERSION)'/g' \
-#        -e 's/-Bugs-To: /-Bugs-To: kegie+jamaendo@ovi.com/g' \
-#        -e 's/PACKAGE/Jamaendo/g' $(MESSAGESPOT)
-
-#gen_gettext: messagespot
-#      make -C data/po generators
-#      make -C data/po update
index ec9365f..ef65470 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,44 +1,8 @@
-# Jamaendo
+# Source code
 
-A media player for the Nokia N900 and other Maemo 5 (Fremantle) phones.
+> Where did all the source code go?
 
-[Jamaendo][jamaendo] plays music from the music catalog of [JAMENDO][jamendo].
+The main development branch was moved to github, at
 
-JAMENDO is an online platform that distributes musical works under
-Creative Commons licenses.
+    http://github.com/krig/jamaendo
 
-Jamaendo is written by Kristoffer Grönlund.
-
-## Panucci
-
-[Jamaendo][jamaendo] is based on [Panucci][panucci], an audiobook and podcast player for
-Maemo. Panucci is written by 
-
- [jamendo]: http://www.jamendo.com/ "Jamendo"
- [jamaendo]: http://jamaendo.garage.maemo.org/ "Jamaendo"
- [panucci]: http://panucci.garage.maemo.org/ "Panucci"
-
-## Installation
-
-To build the packages, you need a scratchbox environment set up. See
-the Nokia/Maemo documentation for information on how to do this. To
-build the package, go to the Jamaendo source directory and type
-
-    make build-package
-
-This will (hopefully) generate a .deb file for you to install on your
-Maemo 5 device.
-
-## Optification
-
-If you're running a newer release of the Fremantle platform, this step
-may not (I hope not!) be necessary, but either way: The package that
-the build process produces is not entirely optified. It is compatible
-with the `maemo-optify` tool, however. All you need to do is run
-`maemo-optify-deb` on the generated .deb file.
-
-## License
-
-The backend code that speaks to jamendo.com is in the jamaendo module. This module is licensed under a modified New BSD License (modified to remove attribution clauses). See `jamaendo/api.py` for details.
-
-The code that implements the UI and the rest of the application is licensed under a GPL v3 license.
diff --git a/data/26x26/jamaendo.png b/data/26x26/jamaendo.png
deleted file mode 100644 (file)
index 8fb2a16..0000000
Binary files a/data/26x26/jamaendo.png and /dev/null differ
diff --git a/data/40x40/jamaendo.png b/data/40x40/jamaendo.png
deleted file mode 100644 (file)
index 2f8c072..0000000
Binary files a/data/40x40/jamaendo.png and /dev/null differ
diff --git a/data/64x64/jamaendo.png b/data/64x64/jamaendo.png
deleted file mode 100644 (file)
index 1afa270..0000000
Binary files a/data/64x64/jamaendo.png and /dev/null differ
diff --git a/data/album.png b/data/album.png
deleted file mode 100644 (file)
index ee206f8..0000000
Binary files a/data/album.png and /dev/null differ
diff --git a/data/bg.png b/data/bg.png
deleted file mode 100644 (file)
index d8a60c6..0000000
Binary files a/data/bg.png and /dev/null differ
diff --git a/data/bg.xcf b/data/bg.xcf
deleted file mode 100644 (file)
index 5f520bb..0000000
Binary files a/data/bg.xcf and /dev/null differ
diff --git a/data/icon_artist.png b/data/icon_artist.png
deleted file mode 100644 (file)
index 6819c8c..0000000
Binary files a/data/icon_artist.png and /dev/null differ
diff --git a/data/icon_download.png b/data/icon_download.png
deleted file mode 100644 (file)
index bbf77ea..0000000
Binary files a/data/icon_download.png and /dev/null differ
diff --git a/data/icon_favorite.png b/data/icon_favorite.png
deleted file mode 100644 (file)
index d1c7069..0000000
Binary files a/data/icon_favorite.png and /dev/null differ
diff --git a/data/icon_license.png b/data/icon_license.png
deleted file mode 100644 (file)
index 7311dd8..0000000
Binary files a/data/icon_license.png and /dev/null differ
diff --git a/data/jamaendo-26.base64 b/data/jamaendo-26.base64
deleted file mode 100644 (file)
index 7d9c11c..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-begin-base64 644 data/jamaendo-26.png
-iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAAXNSR0IArs4c
-6QAAAAZiS0dEAG0AJQBuDG3A1wAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0
-SU1FB9oBAQw5E3mN7jwAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJ
-TVBXgQ4XAAAF9klEQVRIx8WWW2xcdxHGf3P27NXr2Ous7fi6pRinJrEDbmUo
-1IUm6i2kmKA0ICRAEAIICfWBloTwwE0tLULIghapgKhakIhUp1VLSkJFpOKk
-aZUmrlDrJiF2Gttbb727Xt/W612fc/7DQ+LEdRIFeIB5/es/3zefZuYb+B+F
-XO0h05cIo1QBt4noJ0A6VLkOISZggc4ojIrKm4i+DPIiMAEU4ttG9JpAmacT
-NkIX8FmB7aCNIHINwqpoVpDnVXWviByObxspXRUo29ccVWWniOwA2gDrP1RI
-Qc+p8kcReTS+bSR9GVC2L1Gh6I8E+Qqw6spJcIEFVQUIIeKXK8tfVPRpgV3x
-baMplhhn+hJ+4D6Qr10BpAA8pqp3qur1qtoMNCPyPlRvA30YmFzxJyQq2xV2
-ZfqaowCS7WsW4A5VeVyExEX2qvNGrGcsCe5e3XXq3RVyW2QRWjBMI7ND36hy
-po7sVlP8MpiYoHIhSxb4lqL7fA9srywHvi8ityxJqZAXy/+4P9b141jXS7Ms
-EMQjgCGARzlCDSEqKRLCEA9G76kJVtx1xiuensHNrQMvDCoIERUqgf2S7Ut0
-qvI3EWIXGBvE/muwauv9TnCPtTC1sMZzTBkWGo6G8/OZ/Jq3XnxrhzGmbNN9
-m37z2t7X7kqdSnWDWDWtFa9u6P5D0DgnbscUlhppQaHHBjYvAwHFtYK1T46d
-/mrD+MnXv17MF9ucklMhIiZUGcqubq7KzM3M3ZTPzq869OtDdZMjuRoEcRfd
-wOwrs/cEwz0vrO0cdtSUguABhIGtlsKt72kt0Uyk9ruH8rl8Y/Zctnt+prDG
-skUKc4V4cjDZefbE292OcUOuccmN5+IbNncc7Li7/aVwVbjgqiujJ02r5asa
-Wz4ZArfaonxweYMKcjy4qme+oT01EI6FH4pWR8siqyLB7Gj2o/1PHb5zdnI2
-ZAdsHM+hvqEut+H2DSfOHB9qMep5jutgjHFFom8CLcv4r7VVqZHlC0N851gg
-UJ+ow++358ZOJltHp8YanaLT4LgOxvLEiNFFb5F0MrO675F9O2ayMxX53FyZ
-Cm7D2voDauaLGG+5ULaNp77lE2lZIZ3OTF9/9JmjPxw9NXaH53l2IOQvqiCO
-66CWjVrgei65bC40NzNbZwfsQnWierD1xtZ9HTd37Jt7J7llBRC2OEwDVRdk
-A1+kYexksuvsG2fvNkbtpramsx+4qeXvarTmL08c2OJ5HkEN4HgOLetaUlt3
-fOZJT00qm8yUIuWRwYAJpHRxNi4r1qqtng4JdF2qyvvQTHbqzEKp6PcH/F5t
-ovadts62fx7Zf6TZ8Rxsz4/YlusaNzA6PFp14E8HNy4WS8V0KlNZW1/73KZP
-b3wb17v5vD4XlRq3cc0xRS4CiZaa19Ql1WDcQrHgGzg80Dl4fLBlanIqhg/j
-GkfjdfEUQiTzbmb1sf5jXSKoPxjIh6Oh+mL6kRt8aMdyIOCojccLqrpTkKCi
-qFf0VwafbU/c0NM/PDjSncvmwgiBjo+098/PFcqHTg2ttywZ2HLvpw6mU+nK
-fD5f4bf982XlZcny6Nyw3/R+wRgTuVSNeiLynGQebahG5Vng40s1YZVnNfDJ
-3w1P3Ds5M1mKVzfUDLd3rB8cT45HRoZH6mNVsXPtne1DRHEpYhHCONPHmD39
-zc3G5H4i4jVecgZ9A2GzZHobfKh8SZFfCkTPsxBFgmnbrustb/v9U3bo/QuE
-KVFCmMdiERcHBzAAhVyvNZ/71RcR5wcITXJpWkvAbhXzmACkf95cLUqvKp8T
-8C21uiCgvKr4HlbxvYIvWMCyDYAsTAoQwuJGER5QkY3yXqP0EParyLdr7h8Z
-u7gT0j9tWo/yC2DT5c6qBsgBI4pk5TyRmEACNA7iW+FHBuhH+E7N98YGLrPy
-9ENN64BdwFYuyPhfRF7heYGf1ewZ+8dVj5OJBxurgR4R2Qn6YRD/v3ksOAKv
-K/pb4M+1e5IT1zy3Jh5stAViKtwiyOeBj6HUIfhWZPcQUqocFWEv8LKiudo9
-SZf/V/wLutHIlKZ+E9wAAAAASUVORK5CYII=
-====
diff --git a/data/jamaendo.desktop b/data/jamaendo.desktop
deleted file mode 100644 (file)
index deaa4ef..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-[Desktop Entry]
-Name=Jamaendo
-GenericName=Jamendo.com Free Music Player
-Comment=Streams music licensed under Creative Commons from jamendo.com
-Exec=jamaendo
-Icon=jamaendo
-Terminal=false
-Type=Application
-Categories=Audio;GTK;
-X-HildonDesk-ShowInToolbar=true
diff --git a/data/jamaendo.png b/data/jamaendo.png
deleted file mode 100644 (file)
index 8fb2a16..0000000
Binary files a/data/jamaendo.png and /dev/null differ
diff --git a/data/jamaendo.xcf b/data/jamaendo.xcf
deleted file mode 100644 (file)
index 207b2a2..0000000
Binary files a/data/jamaendo.xcf and /dev/null differ
diff --git a/data/logo_2600_710.png b/data/logo_2600_710.png
deleted file mode 100644 (file)
index cce480b..0000000
Binary files a/data/logo_2600_710.png and /dev/null differ
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644 (file)
index 2f76872..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-jamaendo (0.2-3) unstable; urgency=low
-
-  * Background loading support
-  * Fixed missing package dependency
-  * Playlist removal supported
-  * Bugfixes and tweaks
-
- -- Kristoffer Grönlund <kristoffer.gronlund@purplescout.se>  Sat, 09 Jan 2010 04:39:10 +0100
-
-jamaendo (0.2-2) unstable; urgency=low
-
-  * Initial background loading support (search window)
-  * Fixed log append issue (/tmp is only 1MB)
-  * Added icon credits in about box
-
- -- Kristoffer Grönlund <kristoffer.gronlund@purplescout.se>  Fri, 08 Jan 2010 00:39:20 +0100
-
-jamaendo (0.2-1) unstable; urgency=low
-
-  * Second preview
-  * Images in album/track lists
-  * Improved 'featured' - now with tags and more
-  * Using pycurl instead of urllib to fetch
-  * Various other bugfixes
-
- -- Kristoffer Gronlund <kristoffer.gronlund@purplescout.se>  Sun,  3 Jan 2010 22:21:44 +0100
-
-jamaendo (0.1-1) unstable; urgency=low
-
-  * First preview
-
- -- Kristoffer Gronlund <kristoffer.gronlund@purplescout.se>  Fri, 01 Jan 2010 14:44:08 +0100
diff --git a/debian/compat b/debian/compat
deleted file mode 100644 (file)
index 7ed6ff8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-5
diff --git a/debian/control b/debian/control
deleted file mode 100644 (file)
index b1e0140..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-Source: jamaendo
-Maintainer: Kristoffer Gronlund <kristoffer.gronlund@purplescout.se>
-Section: user/multimedia
-Priority: optional
-Standards-Version: 3.7.3
-Build-Depends: debhelper (>= 5.0.37.2), python2.5-dev
-XS-Python-Version: >= 2.5
-XB-Python-Version: ${python:Versions}
-XSBC-Bugtracker: http://github.com/krig/jamaendo/issues
-Homepage: http://jamaendo.garage.maemo.org/
-
-Package: jamaendo
-Architecture: all
-Depends: ${python:Depends}, python2.5-gtk2, python2.5-gstreamer, python2.5-dbus, python2.5-hildon, python-lxml, python2.5-osso, python-pycurl, python-simplejson
-Description: Streaming media player that plays music from jamendo.com
-XB-Maemo-Display-Name: Jamaendo Player
-XB-Maemo-Icon-26:
-  iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAAXNSR0IArs4c                                                          
-  6QAAAAZiS0dEAG0AJQBuDG3A1wAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0                                                          
-  SU1FB9oBAQw5E3mN7jwAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJ                                                          
-  TVBXgQ4XAAAF9klEQVRIx8WWW2xcdxHGf3P27NXr2Ous7fi6pRinJrEDbmUo                                                          
-  1IUm6i2kmKA0ICRAEAIICfWBloTwwE0tLULIghapgKhakIhUp1VLSkJFpOKk
-  aZUmrlDrJiF2Gttbb727Xt/W612fc/7DQ+LEdRIFeIB5/es/3zefZuYb+B+F
-  XO0h05cIo1QBt4noJ0A6VLkOISZggc4ojIrKm4i+DPIiMAEU4ttG9JpAmacT
-  NkIX8FmB7aCNIHINwqpoVpDnVXWviByObxspXRUo29ccVWWniOwA2gDrP1RI
-  Qc+p8kcReTS+bSR9GVC2L1Gh6I8E+Qqw6spJcIEFVQUIIeKXK8tfVPRpgV3x
-  baMplhhn+hJ+4D6Qr10BpAA8pqp3qur1qtoMNCPyPlRvA30YmFzxJyQq2xV2
-  ZfqaowCS7WsW4A5VeVyExEX2qvNGrGcsCe5e3XXq3RVyW2QRWjBMI7ND36hy
-  po7sVlP8MpiYoHIhSxb4lqL7fA9srywHvi8ityxJqZAXy/+4P9b141jXS7Ms
-  EMQjgCGARzlCDSEqKRLCEA9G76kJVtx1xiuensHNrQMvDCoIERUqgf2S7Ut0
-  qvI3EWIXGBvE/muwauv9TnCPtTC1sMZzTBkWGo6G8/OZ/Jq3XnxrhzGmbNN9
-  m37z2t7X7kqdSnWDWDWtFa9u6P5D0DgnbscUlhppQaHHBjYvAwHFtYK1T46d
-  /mrD+MnXv17MF9ucklMhIiZUGcqubq7KzM3M3ZTPzq869OtDdZMjuRoEcRfd
-  wOwrs/cEwz0vrO0cdtSUguABhIGtlsKt72kt0Uyk9ruH8rl8Y/Zctnt+prDG
-  skUKc4V4cjDZefbE292OcUOuccmN5+IbNncc7Li7/aVwVbjgqiujJ02r5asa
-  Wz4ZArfaonxweYMKcjy4qme+oT01EI6FH4pWR8siqyLB7Gj2o/1PHb5zdnI2
-  ZAdsHM+hvqEut+H2DSfOHB9qMep5jutgjHFFom8CLcv4r7VVqZHlC0N851gg
-  UJ+ow++358ZOJltHp8YanaLT4LgOxvLEiNFFb5F0MrO675F9O2ayMxX53FyZ
-  Cm7D2voDauaLGG+5ULaNp77lE2lZIZ3OTF9/9JmjPxw9NXaH53l2IOQvqiCO
-  66CWjVrgei65bC40NzNbZwfsQnWierD1xtZ9HTd37Jt7J7llBRC2OEwDVRdk
-  A1+kYexksuvsG2fvNkbtpramsx+4qeXvarTmL08c2OJ5HkEN4HgOLetaUlt3
-  fOZJT00qm8yUIuWRwYAJpHRxNi4r1qqtng4JdF2qyvvQTHbqzEKp6PcH/F5t
-  ovadts62fx7Zf6TZ8Rxsz4/YlusaNzA6PFp14E8HNy4WS8V0KlNZW1/73KZP
-  b3wb17v5vD4XlRq3cc0xRS4CiZaa19Ql1WDcQrHgGzg80Dl4fLBlanIqhg/j
-  GkfjdfEUQiTzbmb1sf5jXSKoPxjIh6Oh+mL6kRt8aMdyIOCojccLqrpTkKCi
-  qFf0VwafbU/c0NM/PDjSncvmwgiBjo+098/PFcqHTg2ttywZ2HLvpw6mU+nK
-  fD5f4bf982XlZcny6Nyw3/R+wRgTuVSNeiLynGQebahG5Vng40s1YZVnNfDJ
-  3w1P3Ds5M1mKVzfUDLd3rB8cT45HRoZH6mNVsXPtne1DRHEpYhHCONPHmD39
-  zc3G5H4i4jVecgZ9A2GzZHobfKh8SZFfCkTPsxBFgmnbrustb/v9U3bo/QuE
-  KVFCmMdiERcHBzAAhVyvNZ/71RcR5wcITXJpWkvAbhXzmACkf95cLUqvKp8T
-  8C21uiCgvKr4HlbxvYIvWMCyDYAsTAoQwuJGER5QkY3yXqP0EParyLdr7h8Z
-  u7gT0j9tWo/yC2DT5c6qBsgBI4pk5TyRmEACNA7iW+FHBuhH+E7N98YGLrPy
-  9ENN64BdwFYuyPhfRF7heYGf1ewZ+8dVj5OJBxurgR4R2Qn6YRD/v3ksOAKv
-  K/pb4M+1e5IT1zy3Jh5stAViKtwiyOeBj6HUIfhWZPcQUqocFWEv8LKiudo9
-  SZf/V/wLutHIlKZ+E9wAAAAASUVORK5CYII=
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644 (file)
index bc4e00f..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-This package was debianized by Kristoffer Gronlund <kristoffer.gronlund@purplescout.se> on
-Fri, 01 Jan 2010 14:44:08 +0100.
-
-Upstream Website:
-    http://jamaendo.garage.maemo.org/
-
-Upstream Author:
-    Kristoffer Gronlund <kristoffer.gronlund@purplescout.se>
-
-Copyright:
-    Copyright (C) 2010 Kristoffer Gronlund <kristoffer.gronlund@purplescout.se>
-    Copyright (C) 2008 Thomas Perl <thpinfo.com>
-
-
-Copyright (c) 2010 Kristoffer Gronlund
-
-Jamaendo 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 3 of the License, or
-(at your option) any later version.
-
-Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-
diff --git a/debian/jamaendo.menu b/debian/jamaendo.menu
deleted file mode 100644 (file)
index a829205..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-?package(jamaendo):\
-  needs="X11"\
-  section="Apps/Sound"\
-  title="Jamaendo"\
-  command="jamaendo"\
-  icon=/usr/share/jamaendo/jamaendo.png
diff --git a/debian/optify b/debian/optify
deleted file mode 100644 (file)
index 01aeeba..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-none
-
diff --git a/debian/postinst b/debian/postinst
deleted file mode 100644 (file)
index 98f4128..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/sh
-
-if [ -x /usr/bin/gtk-update-icon-cache ]; then
-  /usr/bin/gtk-update-icon-cache -f /usr/share/icons/hicolor
-fi
-
-#if ps -e | grep gconfd-2 > /dev/null; then
-#  killall gconfd-2
-#fi
-
-#GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source` \
-#      gconftool-2 --makefile-install-rule /etc/gconf/schemas/panucci.schemas
-#killall gconfd-2
-
-#DEBHELPER#
-
diff --git a/debian/pycompat b/debian/pycompat
deleted file mode 100644 (file)
index 0cfbf08..0000000
+++ /dev/null
@@ -1 +0,0 @@
-2
diff --git a/debian/rules b/debian/rules
deleted file mode 100755 (executable)
index 40a190c..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/usr/bin/make -f
-# -*- makefile -*-
-# Sample debian/rules that uses debhelper.
-# This file was originally written by Joey Hess and Craig Small.
-# As a special exception, when this file is copied by dh-make into a
-# dh-make output file, you may use that output file without restriction.
-# This special exception was added by Craig Small in version 0.37 of dh-make.
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-CHANGELOG      = debian/changelog
-DOCS           = #AUTHORS README
-
-PYTHON         = /usr/bin/python2.5
-SETUP          = ${PYTHON} setup.py
-
-DEBPACKAGE     = jamaendo
-DESTDIR                = debian/${DEBPACKAGE}
-PREFIX         = usr
-
-configure: configure-stamp
-configure-stamp:
-       dh_testdir
-       touch configure-stamp
-# Add here commands to configure the package.
-
-build: build-stamp
-build-stamp: configure-stamp
-       dh_testdir
-       ${SETUP} build
-       touch build-stamp
-
-clean:
-       dh_testdir
-       dh_testroot
-       rm -f build-stamp configure-stamp
-       ${SETUP} clean --all
-       rm -rf build dist
-       dh_clean
-       make clean
-
-install: build
-       dh_testdir
-       dh_testroot
-       dh_clean -k
-       dh_installdirs
-       ${SETUP} install --no-compile -O2 --root='$(DESTDIR)' --prefix='$(PREFIX)'
-#      PREFIX=$(PREFIX) DESTDIR=$(DESTDIR) $(MAKE) gen_gettext dbus-service-install copy-schemas
-
-binary-arch:
-
-binary-indep: build install
-       dh_testdir
-       dh_testroot
-       dh_installchangelogs $(CHANGELOG)
-       dh_installdocs $(DOCS)
-       dh_installmenu
-       dh_installman
-       dh_compress
-       dh_fixperms
-       dh_python
-       dh_installdeb
-       dh_gencontrol
-       dh_md5sums
-       dh_builddeb
-
-binary: binary-indep
-
-.PHONY: build clean binary-indep binary-arch binary install configure
-
diff --git a/helldon/__init__.py b/helldon/__init__.py
deleted file mode 100644 (file)
index 9f095c6..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-import gtk
-import cairo
-
-gtk.HILDON_SIZE_AUTO = -1
-gtk.HILDON_SIZE_AUTO_WIDTH = -1
-gtk.HILDON_SIZE_FINGER_HEIGHT = 32
-gtk.HILDON_SIZE_THUMB_HEIGHT = 48
-
-BUTTON_ARRANGEMENT_VERTICAL = 1
-
-def hildon_gtk_window_set_progress_indicator(wnd, onoff):
-    pass
-
-def transparent_expose(widget, event):
-    bgimg = 'data/bg.png'
-    if bgimg:
-        background, mask = gtk.gdk.pixbuf_new_from_file(bgimg).render_pixmap_and_mask()
-        #self.realize()
-        widget.window.set_back_pixmap(background, False)
-        #widget.window.clear()
-    #cr = widget.window.cairo_create()
-    #cr.set_operator(cairo.OPERATOR_CLEAR)
-    # Ugly but we don't have event.region
-    #region = gtk.gdk.region_rectangle(event.area)
-    #cr.region(region)
-    #cr.fill()
-    return False
-
-class Program(gtk.Window):
-    instance = None
-
-    def __init__(self):
-        gtk.Window.__init__(self, type=gtk.WINDOW_TOPLEVEL)
-        self.set_app_paintable(True)
-        self._vbox = gtk.VBox()
-        self._title = gtk.Label("Jamaendo")
-        self._title.set_alignment(0.1, 0.5)
-        self._backbtn = gtk.Button("Quit")
-        self._backbtn.set_alignment(1.0, 0.5)
-        self._hbox = gtk.HBox()
-        self._hbox.pack_start(self._title, True)
-        self._hbox.pack_start(self._backbtn, False)
-        self._notebook = gtk.Notebook()
-        self._notebook.set_size_request(800, 445)
-        self._notebook.set_show_tabs(False)
-        self._notebook.set_app_paintable(True)
-        self._notebook.connect("expose-event", transparent_expose)
-        self._vbox.pack_start(self._hbox, False)
-        self._vbox.pack_start(self._notebook, True)
-        self.add(self._vbox)
-        self.show_all()
-        Program.instance = self
-        self._backbtn.connect('clicked', self.on_back)
-
-        bgimg = 'data/bg.png'
-        if bgimg:
-            background, mask = gtk.gdk.pixbuf_new_from_file(bgimg).render_pixmap_and_mask()
-            self.set_app_paintable(True)
-            self.realize()
-            self.window.set_back_pixmap(background, False)
-            self.window.clear()
-
-        self._stack = []
-
-    def add_window(self, wnd):
-        pass
-
-    def push_stackable(self, wnd):
-        while self._notebook.get_n_pages() > 0:
-            p = self._notebook.get_nth_page(0)
-            self._stack.append(p)
-            self._notebook.remove_page(0)
-            p.hide()
-
-        idx = self._notebook.append_page(wnd)
-        self._notebook.set_current_page(idx)
-        wnd.show_all()
-        wnd._nb_index = idx
-        if self._stack:
-            self._backbtn.set_label("<<<")
-        else:
-            self._backbtn.set_label("Quit")
-        self._notebook.window.clear()
-
-    def popped_stackable(self, wnd=None):
-        pass
-
-    def pop_stackable(self):
-        while self._notebook.get_n_pages() > 0:
-            p = self._notebook.get_nth_page(0)
-            self._notebook.remove_page(0)
-            p.hide()
-        if len(self._stack):
-            tail = self._stack.pop()
-            self.push_stackable(tail)
-
-    def on_back(self, *args):
-        self.pop_stackable()
-        if self._stack:
-            self._backbtn.set_label("<<<")
-        else:
-            self._backbtn.set_label("Quit")
-        if self._notebook.get_n_pages() == 0:
-            gtk.main_quit()
-
-    def size(self):
-        return len(self._stack)+1
-
-    def pop(self, sz):
-        ret = [x for x in self._stack] + [self._notebook.get_nth_page(0)]
-        while self._stack:
-            self.pop_stackable()
-        return ret
-
-    def push_list(self, windows):
-        for window in windows:
-            self.push_stackable(window)
-
-    #        windows = stack.pop(sz)
-    #    windows.remove(player)
-    #    windows.append(player)
-    #    stack.push_list(windows)
-
-
-class StackableWindow(gtk.EventBox):
-    def __init__(self):
-        gtk.EventBox.__init__(self)
-        self.set_app_paintable(True)
-        #self.connect("expose-event", transparent_expose)
-        self._nb_index = 0
-        Program.instance.push_stackable(self)
-        self.connect('destroy', self.on_destroy)
-    def set_app_menu(self, menu):
-        pass
-
-    def set_title(self, title):
-        Program.instance._title.set_text(title)#_notebook.set_tab_label_text(self, title)
-
-    def on_destroy(self, *args):
-        Program.instance.popped_stackable(self)
-
-    def get_stack(self):
-        return Program.instance
-
-class AppMenu(object):
-    def __init__(self):
-        pass
-
-    def append(self, widget):
-        pass
-
-    def show_all(self):
-        pass
-
-class GtkButton(gtk.Button):
-    def __init__(self, size):
-        gtk.Button.__init__(self)
-
-
-class Button(gtk.Button):
-    def __init__(self, size, layout):
-        gtk.Button.__init__(self)
-
-    def set_text(self, title, subtitle):
-        self.set_label(title)
-
-class PannableArea(gtk.ScrolledWindow):
-    def __init__(self):
-        gtk.ScrolledWindow.__init__(self)
-        self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
diff --git a/jamaendo/__init__.py b/jamaendo/__init__.py
deleted file mode 100644 (file)
index 342be29..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010, Kristoffer Gronlund
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#     * Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above copyright
-#       notice, this list of conditions and the following disclaimer in the
-#       documentation and/or other materials provided with the distribution.
-#     * Neither the name of Jamaendo nor the
-#       names of its contributors may be used to endorse or promote products
-#       derived from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from api import set_cache_dir, \
-    JamendoAPIException, \
-    Artist, Album, Track, Radio, Tag, \
-    get_album_cover, \
-    get_album_cover_async, \
-    get_images_async, \
-    get_artist, \
-    get_albums, \
-    get_album, \
-    get_tracks, \
-    get_track, \
-    get_radio_tracks, \
-    search_artists, \
-    search_albums, \
-    search_tracks, \
-    top_artists, \
-    top_albums, \
-    top_tracks, \
-    top_tags, \
-    albums_of_the_week, \
-    new_releases, \
-    tracks_of_the_week, \
-    get_radio, \
-    starred_radios, \
-    favorite_albums, \
-    get_tag_tracks
-
diff --git a/jamaendo/api.py b/jamaendo/api.py
deleted file mode 100644 (file)
index 8bc2353..0000000
+++ /dev/null
@@ -1,952 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010, Kristoffer Gronlund
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#     * Redistributions of source code must retain the above copyright
-#       notice, this list of conditions and the following disclaimer.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# An improved, structured jamendo API wrapper for the N900 with cacheing
-# Image / cover downloads.. and more?
-from __future__ import with_statement
-import urllib, threading, os, time, simplejson, re
-import logging, hashlib
-import pycurl, StringIO
-
-_CACHEDIR = None
-_COVERDIR = None
-_GET2 = '''http://api.jamendo.com/get2/'''
-_MP3URL = _GET2+'stream/track/redirect/?id=%d&streamencoding=mp31'
-_OGGURL = _GET2+'stream/track/redirect/?id=%d&streamencoding=ogg2'
-_TORRENTURL = _GET2+'bittorrent/file/redirect/?album_id=%d&type=archive&class=mp32'
-
-try:
-    log = logging.getLogger(__name__)
-except:
-    class StdoutLogger(object):
-        def info(self, s, *args):
-            print s % (args)
-        def debug(self, s, *args):
-            pass#print s % (args)
-    log = StdoutLogger()
-
-# These classes can be partially constructed,
-# and if asked for a property they don't know,
-# makes a query internally to get the full story
-
-_ARTIST_FIELDS = ['id', 'name', 'image']
-_ALBUM_FIELDS = ['id', 'name', 'image', 'artist_name', 'artist_id', 'license_url']
-_TRACK_FIELDS = ['id', 'name', 'album_image', 'artist_id', 'artist_name', 'album_name', 'album_id', 'numalbum', 'duration']
-_RADIO_FIELDS = ['id', 'name', 'idstr', 'image']
-_TAG_FIELDS = ['id', 'name']
-
-_APILOCK = threading.Lock()
-
-def curlGET(url):
-    c = pycurl.Curl()
-    s = StringIO.StringIO()
-    c.setopt(pycurl.FOLLOWLOCATION, 1)
-    c.setopt(pycurl.URL, url)
-    c.setopt(pycurl.WRITEFUNCTION, s.write)
-    try:
-        c.perform()
-    finally:
-        c.close()
-    s.seek(0)
-    return s.read()
-
-class LazyQuery(object):
-    def set_from_json(self, json):
-        for key, value in json.iteritems():
-            if key == 'id':
-                assert(self.ID == int(value))
-            else:
-                if key.endswith('_id'):
-                    value = int(value)
-                setattr(self, key, value)
-
-    def load(self):
-        """Not automatic for now,
-        will have to do artist.load()
-
-        This is filled in further down
-        in the file
-        """
-        raise NotImplemented
-
-    def _needs_load(self):
-        return True
-
-    def _set_from(self, other):
-        raise NotImplemented
-
-    def _needs_load_impl(self, *attrs):
-        for attr in attrs:
-            if getattr(self, attr) is None:
-                return True
-        return False
-
-    def _set_from_impl(self, other, *attrs):
-        for attr in attrs:
-            self._set_if(other, attr)
-
-    def _set_if(self, other, attrname):
-        if getattr(self, attrname) is None and getattr(other, attrname) is not None:
-            setattr(self, attrname, getattr(other, attrname))
-
-    def __repr__(self):
-        try:
-            return u"%s(%s)"%(self.__class__.__name__,
-                              u", ".join(("%s:%s"%(k,repr(v))) for k,v in self.__dict__.iteritems() if not k.startswith('_')))
-        except UnicodeEncodeError:
-            #import traceback
-            #traceback.print_exc()
-            return u"%s(?)"%(self.__class__.__name__)
-
-class Artist(LazyQuery):
-    def __init__(self, ID, json=None):
-        self.ID = int(ID)
-        self.name = None
-        self.image = None
-        self.albums = None # None means not downloaded
-        if json:
-            self.set_from_json(json)
-
-    def _needs_load(self):
-        return self._needs_load_impl('name', 'albums')
-
-    def _set_from(self, other):
-        return self._set_from_impl(other, 'name', 'image', 'albums')
-
-    def get_data(self):
-        return {'name':self.name, 'image':self.image}
-
-class Album(LazyQuery):
-    def __init__(self, ID, json=None):
-        self.ID = int(ID)
-        self.name = None
-        self.image = None
-        self.artist_name = None
-        self.artist_id = None
-        self.license_url = None
-        self.tracks = None # None means not downloaded
-        if json:
-            self.set_from_json(json)
-
-    def torrent_url(self):
-        return _TORRENTURL%(self.ID)
-
-
-    def _needs_load(self):
-        return self._needs_load_impl('name', 'image', 'artist_name', 'artist_id', 'license_url', 'tracks')
-
-    def _set_from(self, other):
-        return self._set_from_impl(other, 'name', 'image', 'artist_name', 'artist_id', 'license_url', 'tracks')
-
-    def get_data(self):
-        return {'name':self.name, 'image':self.image,
-                'artist_name':self.artist_name,
-                'artist_id':self.artist_id,
-                'license_url':self.license_url}
-
-class Track(LazyQuery):
-    def __init__(self, ID, json=None):
-        self.ID = int(ID)
-        self.name = None
-        self.artist_id = None
-        self.artist_name = None
-        self.album_image = None
-        self.album_name = None
-        self.album_id = None
-        self.numalbum = None
-        self.duration = None
-        if json:
-            self.set_from_json(json)
-
-    def mp3_url(self):
-       return _MP3URL%(self.ID)
-
-    def ogg_url(self):
-       return _OGGURL%(self.ID)
-
-    def get_data(self):
-        return {'name':self.name,
-                'artist_id':self.artist_id,
-                'artist_name':self.artist_name,
-                'album_image':self.album_image,
-                'album_name':self.album_name,
-                'album_id':self.album_id,
-                'numalbum':self.numalbum,
-                'duration':self.duration}
-
-    def _needs_load(self):
-        return self._needs_load_impl('name', 'artist_name', 'artist_id', 'album_name', 'album_id', 'numalbum', 'duration')
-
-    def _set_from(self, other):
-        return self._set_from_impl(other, 'name', 'album_image', 'artist_name', 'artist_id', 'album_name', 'album_id', 'numalbum', 'duration')
-
-class Radio(LazyQuery):
-    def __init__(self, ID, json=None):
-        self.ID = int(ID)
-        self.name = None
-        self.idstr = None
-        self.image = None
-        if json:
-            self.set_from_json(json)
-
-    def _needs_load(self):
-        return self._needs_load_impl('name', 'idstr', 'image')
-
-    def _set_from(self, other):
-        return self._set_from_impl(other, 'name', 'idstr', 'image')
-
-class Tag(LazyQuery):
-    def __init__(self, ID, json=None):
-        self.ID = int(ID)
-        self.name = None
-        if json:
-            self.set_from_json(json)
-
-    def _needs_load(self):
-        return self._needs_load_impl('name')
-
-    def _set_from(self, other):
-        return self._set_from_impl(other, 'name')
-
-_artists = {} # id -> Artist()
-_albums = {} # id -> Album()
-_tracks = {} # id -> Track()
-_radios = {} # id -> Radio()
-
-
-# cache sizes per session (TODO)
-_CACHED_ARTISTS = 100
-_CACHED_ALBUMS = 200
-_CACHED_TRACKS = 500
-_CACHED_RADIOS = 10
-# cache sizes, persistant
-_CACHED_COVERS = 2048
-
-# TODO: cache queries?
-
-class Ratelimit(object):
-    rate_limit = 1.0 # seconds between queries
-    last_query = time.time() - 1.5
-
-    @classmethod
-    def ratelimit(cls):
-        now = time.time()
-        if (now - cls.last_query) < cls.rate_limit:
-            time.sleep(cls.rate_limit - (now - cls.last_query))
-        cls.last_query = time.time()
-
-_ratelimit = Ratelimit.ratelimit
-
-class Query(object):
-    def __init__(self):
-        pass
-
-    def _geturl(self, url):
-        _ratelimit()
-        log.info("%s", url)
-        try:
-            ret = simplejson.loads(curlGET(url))
-        except Exception, e:
-            return None
-        return ret
-
-    def __str__(self):
-        return "#%s" % (self.__class__.__name__)
-
-    def execute(self):
-        raise NotImplemented
-
-class CoverFetcher(threading.Thread):
-    def __init__(self):
-        threading.Thread.__init__(self)
-        self.setDaemon(True)
-        self.cond = threading.Condition()
-        self.work = []
-
-    def _retrieve(self, url, fname):
-        BROKEN = 'http://imgjam.com/radios/default/default.100.png'
-        if url == BROKEN:
-            return None
-        f = open(fname, 'wb')
-        c = pycurl.Curl()
-        c.setopt(pycurl.FOLLOWLOCATION, 1)
-        c.setopt(pycurl.URL, str(url))
-        c.setopt(pycurl.WRITEFUNCTION, f.write)
-        try:
-            c.perform()
-        except:
-            fname = None
-        finally:
-            c.close()
-            f.close()
-        log.debug("Coverfetch: %s -> %s", url, fname)
-        return fname
-
-    def _fetch_cover(self, albumid, size):
-        try:
-            coverdir = _COVERDIR if _COVERDIR else '/tmp'
-            to = os.path.join(coverdir, '%d-%d.jpg'%(albumid, size))
-            if not os.path.isfile(to):
-                url = _GET2+'image/album/redirect/?id=%d&imagesize=%d'%(albumid, size)
-                to = self._retrieve(url, to)
-            return to
-        except Exception, e:
-            return None
-
-    def _fetch_image(self, url):
-        try:
-            h = hashlib.md5(url).hexdigest()
-            coverdir = _COVERDIR if _COVERDIR else '/tmp'
-            to = os.path.join(coverdir, h+'.jpg')
-            if not os.path.isfile(to):
-                to = self._retrieve(url, to)
-            return to
-        except Exception, e:
-            return None
-
-    def request_cover(self, albumid, size, cb):
-        self.cond.acquire()
-        self.work.insert(0, (albumid, size, cb))
-        self.cond.notify()
-        self.cond.release()
-
-    def request_images(self, urls, cb):
-        """cb([(url, image)])"""
-        self.cond.acquire()
-        self.work = [('image', url, cb) for url in urls] + self.work
-        self.cond.notify()
-        self.cond.release()
-
-    def run(self):
-        while True:
-            work = []
-            self.cond.acquire()
-            while True:
-                work = self.work
-                if work:
-                    self.work = []
-                    break
-                self.cond.wait()
-            self.cond.release()
-
-            multi = len(work) > 1
-            for job in work:
-                if job[0] == 'image':
-                    self.process_image(job[1], job[2])
-                else:
-                    self.process_cover(*job)
-
-    def process_cover(self, albumid, size, cb):
-        cover = self._fetch_cover(albumid, size)
-        if cover:
-            cb(albumid, size, cover)
-
-    def process_image(self, url, cb):
-        image = self._fetch_image(url)
-        if image:
-            cb([(url, image)])
-
-class CoverCache(object):
-    """
-    cache and fetch covers
-    TODO: background thread that
-    fetches and returns covers,
-    asynchronously, LIFO
-    """
-    def __init__(self):
-        self._covers = {} # (albumid, size) -> file
-        self._images = {}
-        self._fetcher = CoverFetcher()
-        self._fetcher.start()
-        if _COVERDIR and os.path.isdir(_COVERDIR):
-            self.prime_cache()
-
-    def prime_cache(self):
-        coverdir = _COVERDIR
-        covermatch = re.compile(r'(\d+)\-(\d+)\.jpg')
-
-        prev_covers = os.listdir(coverdir)
-
-        if len(prev_covers) > _CACHED_COVERS:
-            import random
-            dropn = len(prev_covers) - _CACHED_COVERS
-            todrop = random.sample(prev_covers, dropn)
-            log.warning("Deleting from cache: %s", todrop)
-            for d in todrop:
-                m = covermatch.match(d)
-                if m:
-                    try:
-                        os.unlink(os.path.join(coverdir, d))
-                    except OSError, e:
-                        log.exception('unlinking failed')
-
-        for fil in os.listdir(coverdir):
-            fl = os.path.join(coverdir, fil)
-            m = covermatch.match(fil)
-            if m and os.path.isfile(fl):
-                self._covers[(int(m.group(1)), int(m.group(2)))] = fl
-
-    def fetch_cover(self, albumid, size):
-        coverdir = _COVERDIR
-        if coverdir:
-            to = os.path.join(coverdir, '%d-%d.jpg'%(albumid, size))
-            if not os.path.isfile(to):
-                url = _GET2+'image/album/redirect/?id=%d&imagesize=%d'%(albumid, size)
-                urllib.urlretrieve(url, to)
-                self._covers[(albumid, size)] = to
-            return to
-        return None
-
-    def get_cover(self, albumid, size):
-        cover = self._covers.get((albumid, size), None)
-        if not cover:
-            cover = self.fetch_cover(albumid, size)
-        return cover
-
-    def get_async(self, albumid, size, cb):
-        cover = self._covers.get((albumid, size), None)
-        if cover:
-            cb(albumid, size, cover)
-        else:
-            def cb2(albumid, size, cover):
-                self._covers[(albumid, size)] = cover
-                cb(albumid, size, cover)
-            self._fetcher.request_cover(albumid, size, cb2)
-
-    def get_images_async(self, url_list, cb):
-        found = []
-        lookup = []
-        for url in url_list:
-            image = self._images.get(url, None)
-            if image:
-                found.append((url, image))
-            else:
-                lookup.append(url)
-        if found:
-            cb(found)
-
-        if lookup:
-            def cb2(results):
-                for url, image in results:
-                    self._images[url] = image
-                cb(results)
-            self._fetcher.request_images(lookup, cb2)
-
-_cover_cache = CoverCache()
-
-def set_cache_dir(cachedir):
-    global _CACHEDIR
-    global _COVERDIR
-    _CACHEDIR = cachedir
-    _COVERDIR = os.path.join(_CACHEDIR, 'covers')
-
-    try:
-        os.makedirs(_CACHEDIR)
-    except OSError:
-        pass
-
-    try:
-        os.makedirs(_COVERDIR)
-    except OSError:
-        pass
-
-    _cover_cache.prime_cache()
-
-def get_album_cover(albumid, size=100):
-    with _APILOCK:
-        return _cover_cache.get_cover(albumid, size)
-
-def get_album_cover_async(cb, albumid, size=100):
-    with _APILOCK:
-        _cover_cache.get_async(albumid, size, cb)
-
-def get_images_async(cb, url_list):
-    with _APILOCK:
-        _cover_cache.get_images_async(url_list, cb)
-
-class CustomQuery(Query):
-    def __init__(self, url):
-        Query.__init__(self)
-        self.url = url
-
-    def execute(self):
-        return self._geturl(self.url)
-
-    def __str__(self):
-        return self.url
-
-class GetQuery(Query):
-    queries = {
-        'artist' : {
-            'url' : _GET2+'+'.join(_ARTIST_FIELDS)+'/artist/json/?',
-            'params' : 'artist_id=%d',
-            'constructor' : Artist
-            },
-        'artist_list' : {
-            'url' : _GET2+'+'.join(_ALBUM_FIELDS)+'/artist/json/?',
-            'params' : 'artist_id=%s',
-            'constructor' : Album
-            },
-        'album' : {
-            'url' : _GET2+'+'.join(_ALBUM_FIELDS)+'/album/json/?',
-            'params' : 'album_id=%d',
-            'constructor' : Album
-            },
-        'album_list' : {
-            'url' : _GET2+'+'.join(_ALBUM_FIELDS)+'/album/json/?',
-            'params' : 'album_id=%s',
-            'constructor' : Album
-            },
-        'albums' : {
-            'url' : _GET2+'+'.join(_ALBUM_FIELDS)+'/album/json/?',
-            'params' : 'artist_id=%d',
-            'constructor' : [Album]
-            },
-        'track' : {
-            'url' : _GET2+'+'.join(_TRACK_FIELDS)+'/track/json/track_album+album_artist?',
-            'params' : 'id=%d',
-            'constructor' : Track
-            },
-        'track_list' : {
-            'url' : _GET2+'+'.join(_TRACK_FIELDS)+'/track/json/track_album+album_artist?',
-            'params' : 'id=%s',
-            'constructor' : Track
-            },
-        'tracks' : {
-            'url' : _GET2+'+'.join(_TRACK_FIELDS)+'/track/json/track_album+album_artist?',
-            'params' : 'order=numalbum_asc&album_id=%d',
-            'constructor' : [Track]
-            },
-        'radio' : {
-            'url' : _GET2+'+'.join(_TRACK_FIELDS)+'/track/json/radio_track_inradioplaylist+track_album+album_artist/?',
-            'params' : 'order=random_asc&radio_id=%d&n=16',
-            'constructor' : [Track]
-            },
-        'favorite_albums' : {
-            'url' : _GET2+'+'.join(_ALBUM_FIELDS)+'/album/json/album_user_starred/?',
-            'params' : 'user_idstr=%s',
-            'constructor' : [Album]
-            },
-        'tag' : {
-            'url' : _GET2+'+'.join(_TRACK_FIELDS)+'/track/json/track_album+album_artist?',
-            'params' : 'tag_id=%d&n=50&order=rating_desc',
-            'constructor' : [Track]
-            },
-        }
-
-    def __init__(self, what, ID):
-        Query.__init__(self)
-        self.ID = ID
-        info = GetQuery.queries[what]
-        self.url = info['url']
-        self.params = info['params']
-        self.constructor = info['constructor']
-
-    def construct(self, data):
-        constructor = self.constructor
-        if isinstance(constructor, list):
-            constructor = constructor[0]
-        if isinstance(data, list):
-            return [constructor(int(x['id']), json=x) for x in data]
-        else:
-            return constructor(int(data['id']), json=data)
-
-    def execute(self):
-        js = self._geturl(self.url + self.params % (self.ID))
-        if not js:
-            return None
-        return self.construct(js)
-
-    def __str__(self):
-        return self.url + self.params % (self.ID)
-
-class SearchQuery(GetQuery):
-    def __init__(self, what, query=None, order=None, user=None, count=20):
-        GetQuery.__init__(self, what, None)
-        self.query = query
-        self.order = order
-        self.count = count
-        self.user = user
-
-    def execute(self):
-        params = {}
-        if self.query:
-            params['searchquery'] = self.query
-        if self.order:
-            params['order'] = self.order
-        if self.count:
-            params['n'] = self.count
-        if self.user:
-            params['user_idstr'] = self.user
-        js = self._geturl(self.url +  urllib.urlencode(params))
-        if not js:
-            return None
-        return self.construct(js)
-
-    def __str__(self):
-        params = {'searchquery':self.query, 'order':self.order, 'n':self.count}
-        return self.url +  urllib.urlencode(params)
-
-class JamendoAPIException(Exception):
-    def __init__(self, url):
-        Exception.__init__(self, url)
-
-def _update_cache(cache, new_items):
-    if not isinstance(new_items, list):
-        new_items = [new_items]
-    for item in new_items:
-        old = cache.get(item.ID)
-        if old:
-            old._set_from(item)
-        else:
-            cache[item.ID] = item
-        if isinstance(item, Artist) and item.albums:
-            for album in item.albums:
-                _update_cache(_albums, album)
-        elif isinstance(item, Album) and item.tracks:
-            for track in item.tracks:
-                _update_cache(_tracks, track)
-    # enforce cache limits here!
-    # also, TODO: save/load cache between sessions
-    # that will require storing a timestamp with
-    # each item, though..
-    # perhaps,
-    # artists: 1 day - changes often
-    # albums: 2-5 days - changes less often (?)
-    # tracks: 1 week - changes rarely, queried often
-
-def get_artist(artist_id):
-    """Returns: Artist"""
-    with _APILOCK:
-        a = _artists.get(artist_id, None)
-        if not a:
-            q = GetQuery('artist', artist_id)
-            a = q.execute()
-            if not a:
-                raise JamendoAPIException(str(q))
-            _update_cache(_artists, a)
-            if isinstance(a, list):
-                a = a[0]
-        return a
-
-def get_artists(artist_ids):
-    """Returns: [Artist]"""
-    with _APILOCK:
-        assert(isinstance(artist_ids, list))
-        found = []
-        lookup = []
-        for artist_id in artist_ids:
-            a = _artists.get(artist_id, None)
-            if not a:
-                lookup.append(artist_id)
-            else:
-                found.append(a)
-        if lookup:
-            q = GetQuery('artist_list', '+'.join(str(x) for x in lookup))
-            a = q.execute()
-            if not a:
-                raise JamendoAPIException(str(q))
-            _update_cache(_artists, a)
-            lookup = a
-        return found + lookup
-
-def get_album_list(album_ids):
-    """Returns: [Album]"""
-    with _APILOCK:
-        assert(isinstance(album_ids, list))
-        found = []
-        lookup = []
-        for album_id in album_ids:
-            a = _albums.get(album_id, None)
-            if not a:
-                lookup.append(album_id)
-            else:
-                found.append(a)
-        if lookup:
-            q = GetQuery('album_list', '+'.join(str(x) for x in lookup))
-            a = q.execute()
-            if not a:
-                raise JamendoAPIException(str(q))
-            _update_cache(_albums, a)
-            lookup = a
-        return found + lookup
-
-def get_albums(artist_id):
-    """Returns: [Album]
-    Parameter can either be an artist_id or a list of album ids.
-    """
-    if isinstance(artist_id, list):
-        return get_album_list(artist_id)
-    with _APILOCK:
-        a = _artists.get(artist_id, None)
-        if a and a.albums:
-            return a.albums
-
-        q = GetQuery('albums', artist_id)
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_albums, a)
-        return a
-
-def get_album(album_id):
-    """Returns: Album"""
-    with _APILOCK:
-        a = _albums.get(album_id, None)
-        if not a:
-            q = GetQuery('album', album_id)
-            a = q.execute()
-            if not a:
-                raise JamendoAPIException(str(q))
-            _update_cache(_albums, a)
-            if isinstance(a, list):
-                a = a[0]
-        return a
-
-def get_track_list(track_ids):
-    """Returns: [Track]"""
-    with _APILOCK:
-        assert(isinstance(track_ids, list))
-        found = []
-        lookup = []
-        for track_id in track_ids:
-            a = _tracks.get(track_id, None)
-            if not a:
-                lookup.append(track_id)
-            else:
-                found.append(a)
-        if lookup:
-            q = GetQuery('track_list', '+'.join(str(x) for x in lookup))
-            a = q.execute()
-            if not a:
-                raise JamendoAPIException(str(q))
-            _update_cache(_tracks, a)
-            lookup = a
-        return found + lookup
-
-def get_tracks(album_id):
-    """Returns: [Track]
-    Parameter can either be an album_id or a list of track ids.
-    """
-    if isinstance(album_id, list):
-        return get_track_list(album_id)
-    with _APILOCK:
-        a = _albums.get(album_id, None)
-        if a and a.tracks:
-            return a.tracks
-
-        q = GetQuery('tracks', album_id)
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_tracks, a)
-        return a
-
-def get_track(track_id):
-    """Returns: Track"""
-    with _APILOCK:
-        a = _tracks.get(track_id, None)
-        if not a:
-            q = GetQuery('track', track_id)
-            a = q.execute()
-            if not a:
-                raise JamendoAPIException(str(q))
-            _update_cache(_tracks, a)
-            if isinstance(a, list):
-                a = a[0]
-        return a
-
-def get_radio_tracks(radio_id):
-    """Returns: [Track]"""
-    with _APILOCK:
-        q = GetQuery('radio', radio_id)
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_tracks, a)
-        return a
-
-#http://api.jamendo.com/get2/id+name/track/plain/?tag_id=327&n=50&order=rating_desc
-def get_tag_tracks(tag_id):
-    """Returns: [Track]"""
-    with _APILOCK:
-        q = GetQuery('tag', tag_id)
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_tracks, a)
-        return a
-
-def search_artists(query):
-    """Returns: [Artist]"""
-    with _APILOCK:
-        q = SearchQuery('artist', query, 'searchweight_desc')
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_artists, a)
-        return a
-
-def search_albums(query):
-    """Returns: [Album]"""
-    with _APILOCK:
-        q = SearchQuery('album', query, 'searchweight_desc')
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_albums, a)
-        return a
-
-def search_tracks(query):
-    """Returns: [Track]"""
-    with _APILOCK:
-        q = SearchQuery('track', query=query, order='searchweight_desc')
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_tracks, a)
-        return a
-
-def albums_of_the_week():
-    """Returns: [Album]"""
-    with _APILOCK:
-        q = SearchQuery('album', order='ratingweek_desc')
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_albums, a)
-        return a
-
-def new_releases():
-    """Returns: [Track] (playlist)"""
-    with _APILOCK:
-        q = SearchQuery('track', order='releasedate_desc')
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_tracks, a)
-        return a
-
-def tracks_of_the_week():
-    """Returns: [Track] (playlist)"""
-    with _APILOCK:
-        q = SearchQuery('track', order='ratingweek_desc')
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_tracks, a)
-        return a
-
-def top_artists(order='rating_desc', count=20):
-    """Returns: [Artist]"""
-    with _APILOCK:
-        q = SearchQuery('artist', order=order, count=count)
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_artists, a)
-        return a
-
-def top_albums(order='rating_desc', count=20):
-    """Returns: [Album]"""
-    with _APILOCK:
-        q = SearchQuery('album', order=order, count=count)
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_albums, a)
-        return a
-
-def top_tracks(order='rating_desc', count=20):
-    """Returns: [Track]"""
-    with _APILOCK:
-        q = SearchQuery('track', order=order, count=count)
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_tracks, a)
-        return a
-
-def get_radio(radio_id):
-    """Returns: Radio"""
-    with _APILOCK:
-        q = CustomQuery(_GET2+"id+name+idstr+image/radio/json?id=%d"%(radio_id))
-        js = q.execute()
-        if not js:
-            raise JamendoAPIException(str(q))
-        if isinstance(js, list):
-            ks = js[0]
-        return Radio(radio_id, json=js)
-
-def starred_radios():
-    """Returns: [Radio]"""
-    with _APILOCK:
-        q = CustomQuery(_GET2+"id+name+idstr+image/radio/json?order=starred_desc")
-        js = q.execute()
-        if not js:
-            raise JamendoAPIException(str(q))
-        return [Radio(int(radio['id']), json=radio) for radio in js]
-
-def top_tags(count=50, order='rating_desc'):
-    """Returns: [Tag]"""
-    with _APILOCK:
-        q = CustomQuery(_GET2+"id+name/tag/json?n=%d&order=%s"%(count, order))
-        js = q.execute()
-        if not js:
-            raise JamendoAPIException(str(q))
-        return [Tag(int(tag['id']), json=tag) for tag in js]
-
-def favorite_albums(user):
-    """Returns: [Album]"""
-    with _APILOCK:
-        q = SearchQuery('favorite_albums', user=user, count=20)
-        a = q.execute()
-        if not a:
-            raise JamendoAPIException(str(q))
-        _update_cache(_albums, a)
-        return a
-
-### Set loader functions for classes
-
-def _artist_loader(self):
-    if self._needs_load():
-        artist = get_artist(self.ID)
-        artist.albums = get_albums(self.ID)
-        self._set_from(artist)
-Artist.load = _artist_loader
-
-def _album_loader(self):
-    if self._needs_load():
-        album = get_album(self.ID)
-        album.tracks = get_tracks(self.ID)
-        self._set_from(album)
-Album.load = _album_loader
-
-def _track_loader(self):
-    track = get_track(self.ID)
-    self._set_from(track)
-Track.load = _track_loader
-
-def _radio_loader(self):
-    radio = get_radio(self.ID)
-    self._set_from(radio)
-Radio.load = _radio_loader
diff --git a/jamaui/__init__.py b/jamaui/__init__.py
deleted file mode 100644 (file)
index 483bcd4..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-
-import logging
-import sys
-
-LOG_FILENAME = '/tmp/jamaendo.log'
-LOG_LEVEL = logging.INFO
-LOG_FORMAT = "%(asctime)s %(name)-10s: [%(lineno)4d] %(levelname)-5s %(message)s"
-
-_rootlogger = logging.getLogger()
-_fhandler = logging.FileHandler(LOG_FILENAME, mode='w')
-_shandler = logging.StreamHandler()
-_formatter = logging.Formatter(LOG_FORMAT)
-_fhandler.setFormatter(_formatter)
-_shandler.setFormatter(_formatter)
-
-_rootlogger.addHandler(_fhandler)
-_rootlogger.addHandler(_shandler)
-_rootlogger.setLevel(LOG_LEVEL)
-
diff --git a/jamaui/albumlist.py b/jamaui/albumlist.py
deleted file mode 100644 (file)
index c1dc2ab..0000000
+++ /dev/null
@@ -1,337 +0,0 @@
-import os
-import gtk
-import gobject
-try:
-    import hildon
-except:
-    import helldon as hildon
-import jamaendo
-import util
-import draw
-import colors
-from postoffice import postoffice
-import logging
-
-log = logging.getLogger(__name__)
-
-class _BaseList(gtk.TreeView):
-    """
-    TODO: unify the different lists into one
-    """
-    ICON_SIZE = 50
-
-    def __init__(self):
-        gtk.TreeView.__init__(self)
-        self.__store = None
-        self.default_pixbuf = util.find_resource('album.png')
-        self.connect('destroy', self.on_destroy)
-        self.connect('expose-event', self.on_treeview_expose_event)
-        self.loading_message = "Loading..."
-        self.empty_message = "Empty."
-        self._is_loading = True
-
-    def get_pixbuf(self, img):
-        try:
-            return gtk.gdk.pixbuf_new_from_file_at_size(img,
-                                                        self.ICON_SIZE,
-                                                        self.ICON_SIZE)
-        except gobject.GError:
-            log.error("Broken image in cache: %s", img)
-            try:
-                os.unlink(img)
-            except OSError, e:
-                log.warning("Failed to unlink broken image.")
-            if img != self.default_pixbuf:
-                return self.get_default_pixbuf()
-            else:
-                return None
-
-    def get_default_pixbuf(self):
-        if self.default_pixbuf:
-            return self.get_pixbuf(self.default_pixbuf)
-
-    def on_destroy(self, wnd):
-        pass
-
-    def set_loading(self, loading):
-        self._is_loading = loading
-        self.queue_draw()
-
-    def on_treeview_expose_event(self, treeview, event):
-        if event.window == treeview.get_bin_window():
-            model = treeview.get_model()
-            if (model is not None and model.get_iter_first() is not None):
-                return False
-
-            ctx = event.window.cairo_create()
-            ctx.rectangle(event.area.x, event.area.y,
-                    event.area.width, event.area.height)
-            ctx.clip()
-            x, y, width, height, depth = event.window.get_geometry()
-
-            if self._is_loading:
-                text = self.loading_message
-            else:
-                text = self.empty_message
-
-            desc = colors.get_font_desc('LargeSystemFont')
-            draw.text_box_centered(ctx, treeview, width, height, text, desc)
-
-        return False
-
-class MusicList(_BaseList):
-    def __init__(self):
-        _BaseList.__init__(self)
-        (self.COL_ICON, self.COL_NAME, self.COL_ID, self.COL_IMAGE) = range(4)
-        self.__store = gtk.ListStore(gtk.gdk.Pixbuf, str, int, str)
-
-        self.set_model(self.__store)
-
-        icon = gtk.TreeViewColumn('Icon')
-        self.append_column(icon)
-        cell = gtk.CellRendererPixbuf()
-        icon.pack_start(cell, True)
-        icon.add_attribute(cell, 'pixbuf', self.COL_ICON)
-
-        col = gtk.TreeViewColumn('Name')
-        self.append_column(col)
-        cell = gtk.CellRendererText()
-        col.pack_start(cell, True)
-        col.add_attribute(cell, 'text', self.COL_NAME)
-        self.set_search_column(self.COL_NAME)
-        col.set_sort_column_id(self.COL_NAME)
-
-        postoffice.connect('images', self, self.on_images)
-
-    def get_item_id(self, path):
-        return self.__store.get(self.__store.get_iter(path), self.COL_ID)[0]
-
-    def on_destroy(self, wnd):
-        postoffice.disconnect('images', self)
-
-    def on_images(self, images):
-        for url, image in images:
-            for row in self.__store:
-                if row[self.COL_IMAGE] == url:
-                    pb = self.get_pixbuf(image)
-                    if pb:
-                        row[self.COL_ICON] = pb
-
-    def add_items(self, items):
-        images = [x for x in (self.get_item_image(item) for item in items) if x]
-        for item in items:
-            txt = self.get_item_text(item)
-            self.__store.append([self.get_default_pixbuf(), txt, item.ID, self.get_item_image(item)])
-        if images:
-            postoffice.notify('request-images', images)
-
-    def get_item_text(self, item):
-        if isinstance(item, jamaendo.Album):
-            return "%s - %s" % (item.artist_name, item.name)
-        elif isinstance(item, jamaendo.Track):
-            return "%s - %s" % (item.artist_name, item.name)
-        else:
-            return item.name
-
-    def get_item_image(self, item):
-        ret = None
-        if isinstance(item, jamaendo.Track):
-            ret = item.album_image
-        elif hasattr(item, 'image'):
-            ret = item.image
-        if ret:
-            ret = ret.replace('1.100.jpg', '1.%d.jpg'%(self.ICON_SIZE))
-        return ret
-
-class AlbumList(_BaseList):
-    def __init__(self):
-        _BaseList.__init__(self)
-        (self.COL_ICON, self.COL_NAME, self.COL_ID) = range(3)
-        self.__store = gtk.ListStore(gtk.gdk.Pixbuf, str, int)
-        self.__show_artist = True
-
-        self.set_model(self.__store)
-
-        icon = gtk.TreeViewColumn('Icon')
-        self.append_column(icon)
-        cell = gtk.CellRendererPixbuf()
-        icon.pack_start(cell, True)
-        icon.add_attribute(cell, 'pixbuf', self.COL_ICON)
-
-        col = gtk.TreeViewColumn('Name')
-        self.append_column(col)
-        cell = gtk.CellRendererText()
-        col.pack_start(cell, True)
-        col.add_attribute(cell, 'text', self.COL_NAME)
-        self.set_search_column(self.COL_NAME)
-        col.set_sort_column_id(self.COL_NAME)
-
-        postoffice.connect('album-cover', self, self.on_album_cover)
-
-    def on_destroy(self, wnd):
-        _BaseList.on_destroy(self, wnd)
-        postoffice.disconnect('album-cover', self)
-
-    def on_album_cover(self, albumid, size, cover):
-        if size == self.ICON_SIZE:
-            for row in self.__store:
-                if row[self.COL_ID] == albumid:
-                    row[self.COL_ICON] = self.get_pixbuf(cover)
-
-    def add_album(self, album):
-        if self.__show_artist:
-            txt = "%s - %s" % (album.artist_name, album.name)
-        else:
-            txt = album.name
-        self.__store.append([self.get_default_pixbuf(), txt, album.ID])
-        postoffice.notify('request-album-cover', album.ID, self.ICON_SIZE)
-
-    def get_album_id(self, path):
-        return self.__store.get(self.__store.get_iter(path), self.COL_ID)[0]
-
-    def show_artist(self, show):
-        self.__show_artist = show
-
-class TrackList(_BaseList):
-    def __init__(self, numbers = True):
-        _BaseList.__init__(self)
-        self.track_numbers = numbers
-        self.__store = gtk.ListStore(int, str, int)
-        self.set_model(self.__store)
-
-        if numbers:
-            col0 = gtk.TreeViewColumn('Num')
-            self.append_column(col0)
-            cell0 = gtk.CellRendererText()
-            col0.pack_start(cell0, True)
-            col0.add_attribute(cell0, 'text', 0)
-
-        col = gtk.TreeViewColumn('Name')
-        self.append_column(col)
-        cell = gtk.CellRendererText()
-        col.pack_start(cell, True)
-        col.add_attribute(cell, 'text', 1)
-
-        self.set_search_column(1 if numbers else 0)
-        col.set_sort_column_id(0)
-
-    def add_track(self, track):
-        self.__store.append([track.numalbum, track.name, track.ID])
-
-    def get_track_id(self, path):
-        treeiter = self.__store.get_iter(path)
-        _, _, _id = self.__store.get(treeiter, 0, 1, 2)
-        return _id
-
-
-class PlaylistList(_BaseList):
-    def __init__(self):
-        _BaseList.__init__(self)
-        (self.COL_ICON, self.COL_NAME, self.COL_INFO, self.COL_ID) = range(4)
-        self.__store = gtk.ListStore(gtk.gdk.Pixbuf, str, str, int)
-
-        self.set_model(self.__store)
-
-        icon = gtk.TreeViewColumn('Icon')
-        self.append_column(icon)
-        cell = gtk.CellRendererPixbuf()
-        icon.pack_start(cell, True)
-        icon.add_attribute(cell, 'pixbuf', self.COL_ICON)
-
-        col = gtk.TreeViewColumn('Name')
-        self.append_column(col)
-        cell = gtk.CellRendererText()
-        col.pack_start(cell, True)
-        col.add_attribute(cell, 'text', self.COL_NAME)
-
-        col = gtk.TreeViewColumn('Info')
-        self.append_column(col)
-        cell = gtk.CellRendererText()
-        cell.set_property('xalign', 1.0)
-        col.pack_start(cell, True)
-        col.add_attribute(cell, 'text', self.COL_INFO)
-
-        self.set_search_column(self.COL_NAME)
-        col.set_sort_column_id(self.COL_NAME)
-
-        postoffice.connect('album-cover', self, self.on_album_cover)
-
-    def on_destroy(self, wnd):
-        _BaseList.on_destroy(self, wnd)
-        postoffice.disconnect('album-cover', self)
-
-    def on_album_cover(self, albumid, size, cover):
-        if size == self.ICON_SIZE:
-            for row in self.__store:
-                if row[self.COL_ID] == albumid:
-                    row[self.COL_ICON] = self.get_pixbuf(cover)
-
-    def add_playlist(self, name, tracks):
-        def trackcount(lst):
-            ln = len(lst)
-            if ln > 1:
-                return "(%d tracks)"%(ln)
-            elif ln == 1:
-                return "(1 track)"
-            return "(empty)"
-        track = tracks[0] if len(tracks) else None
-        track_album_id = int(track['data']['album_id']) if track else 0
-        self.__store.append([self.get_default_pixbuf(), name, trackcount(tracks), track_album_id])
-        if track_album_id:
-            postoffice.notify('request-album-cover', track_album_id, self.ICON_SIZE)
-
-    def get_playlist_name(self, path):
-        return self.__store.get(self.__store.get_iter(path), self.COL_NAME)[0]
-
-class RadioList(_BaseList):
-    def __init__(self):
-        _BaseList.__init__(self)
-        (self.COL_ICON, self.COL_NAME, self.COL_ID, self.COL_IMAGE) = range(4)
-        self.__store = gtk.ListStore(gtk.gdk.Pixbuf, str, int, str)
-        self.set_model(self.__store)
-
-        icon = gtk.TreeViewColumn('Icon')
-        self.append_column(icon)
-        cell = gtk.CellRendererPixbuf()
-        icon.pack_start(cell, True)
-        icon.add_attribute(cell, 'pixbuf', self.COL_ICON)
-
-        col = gtk.TreeViewColumn('Name')
-        self.append_column(col)
-        cell = gtk.CellRendererText()
-        col.pack_start(cell, True)
-        col.add_attribute(cell, 'text', self.COL_NAME)
-        self.set_search_column(self.COL_NAME)
-        col.set_sort_column_id(self.COL_NAME)
-
-        postoffice.connect('images', self, self.on_images)
-
-    def on_destroy(self, wnd):
-        postoffice.disconnect('images', self)
-
-    def add_radios(self, radios):
-        for radio in radios:
-            self.__store.append([self.get_default_pixbuf(), self.radio_name(radio), radio.ID, radio.image])
-        postoffice.notify('request-images', [radio.image for radio in radios])
-
-
-    def get_radio_id(self, path):
-        treeiter = self.__store.get_iter(path)
-        name, _id = self.__store.get(treeiter, self.COL_NAME, self.COL_ID)
-        return name, _id
-
-    def on_images(self, images):
-        for url, image in images:
-            for row in self.__store:
-                if row[self.COL_IMAGE] == url:
-                    pb = self.get_pixbuf(image)
-                    if pb:
-                        row[self.COL_ICON] = pb
-
-    def radio_name(self, radio):
-        if radio.idstr:
-            return radio.idstr.capitalize()
-        elif radio.name:
-            return radio.name
-        else:
-            return "Radio #%s" % (radio.ID)
diff --git a/jamaui/colors.py b/jamaui/colors.py
deleted file mode 100644 (file)
index 62ab8c9..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-# hildon colors
-
-import gtk
-try:
-    import hildon
-    _using_helldon = False
-except:
-    import helldon as hildon
-    _using_helldon = True
-
-# See the Fremantle Master Layout Guide for more information:
-# http://tinyurl.com/fremantle-master-layout-guide
-
-logical_font_names = (
-    'SystemFont',
-    'EmpSystemFont',
-    'SmallSystemFont', # Used for secondary text in buttons/TreeViews
-    'EmpSmallSystemFont',
-    'LargeSystemFont', # Used for empty TreeView text
-    'X-LargeSystemFont',
-    'XX-LargeSystemFont',
-    'XXX-LargeSystemFont',
-    'HomeSystemFont',
-)
-
-logical_color_names = (
-    'ButtonTextColor',
-    'ButtonTextPressedColor',
-    'ButtonTextDisabledColor',
-    'ActiveTextColor', # Used for Button values, etc..
-    'SecondaryTextColor', # Used for additional/secondary information
-)
-
-def get_font_desc(logicalfontname):
-    settings = gtk.settings_get_default()
-    font_style = gtk.rc_get_style_by_paths(settings, logicalfontname, \
-                                               None, None)
-    if not font_style:
-        font_style = gtk.rc_get_style_by_paths(settings,
-                                               'GtkButton', 'GtkButton', gtk.Button)
-
-    font_desc = font_style.font_desc
-    return font_desc
-
-def get_color(logicalcolorname):
-    settings = gtk.settings_get_default()
-    color_style = gtk.rc_get_style_by_paths(settings, 'GtkButton', \
-                                                'osso-logical-colors', gtk.Button)
-
-    if not color_style:
-        font_style = gtk.rc_get_style_by_paths(settings,
-                                               'GtkButton', 'GtkButton', gtk.Button)
-    return color_style.lookup_color(logicalcolorname)
-
-if _using_helldon:
-    def font(name):
-        return "normal"
-else:
-    def font(name):
-        return get_font_desc(name).to_string()
-
-if _using_helldon:
-    def color(name):
-        return "#333333"
-else:
-    def color(name):
-        return get_color(name).to_string()
-
-import sys
-current_module = sys.modules[__name__]
-
-def mk_font_fun(name):
-    def inner():
-        return font(name)
-    return inner
-
-for fnt in logical_font_names:
-    setattr(current_module, fnt.replace('-', ''), mk_font_fun(fnt))
-
-for clr in logical_color_names:
-    setattr(current_module, clr, lambda: color(clr))
diff --git a/jamaui/console.py b/jamaui/console.py
deleted file mode 100644 (file)
index 56e795c..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-# console interface to jamaui/jamaendo
-
-# debugging hack - add . to path
-import os, sys
-local_module_dir = os.path.join(os.path.dirname(sys.argv[0]), '..')
-if os.path.isdir(local_module_dir):
-    sys.path.append(local_module_dir)
-
-import jamaendo
-from jamaui.player import Player, Playlist, the_player
-import time
-import gobject
-
-gobject.threads_init()
-
-import pprint
-
-pp = pprint.PrettyPrinter(indent=4)
-
-#pp.pprint(stuff)
-
-class Console(object):
-    def run(self):
-        query = sys.argv[1]
-
-        queries = ['albums_of_the_week', 'artists', 'albums']
-        if query in queries:
-            getattr(self, "query_"+query)()
-        else:
-            print "Valid queries: " + ", ".join(queries)
-
-    def query_albums_of_the_week(self):
-        result = jamaendo.albums_of_the_week()
-        pp.pprint([(a.ID, a.name) for a in result])
-        for a in result:
-            self.play_album(a)
-
-    def query_artists(self):
-        result = jamaendo.search_artists(sys.argv[2])
-        pp.pprint([(a.ID, a.name) for a in result])
-        for a in result:
-            albums = jamaendo.get_albums(a.ID)
-            for album in albums:
-                print "Playing album: %s - %s" % (a.name, album.name)
-                self.play_album(album)
-
-    def query_albums(self):
-        result = jamaendo.search_albums(sys.argv[2])
-        pp.pprint([(a.ID, a.name) for a in result])
-        for a in result:
-            self.play_album(a)
-
-    def play_tracks(self, tracks):
-        playlist = Playlist(tracks)
-        player = the_player
-        player.play(playlist)
-
-        while player.playing():
-            try:
-                time.sleep(1)
-            except KeyboardInterrupt:
-                player.next()
-
-    def play_album(self, album):
-        if not album.tracks:
-            album.load()
-        print "%s - %s" % (album.artist_name, album.name)
-        if album.tracks:
-            self.play_tracks(album.tracks)
-
-if __name__=="__main__":
-    main()
diff --git a/jamaui/draw.py b/jamaui/draw.py
deleted file mode 100644 (file)
index 400111d..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-import gtk
-import pango
-
-def text_box_centered(ctx, widget, w_width, w_height, text, font_desc=None):
-    style = widget.rc_get_style()
-    text_color = style.text[gtk.STATE_PRELIGHT]
-    red, green, blue = text_color.red, text_color.green, text_color.blue
-    text_color = [float(x)/65535. for x in (red, green, blue)]
-    text_color.append(.5)
-
-    if font_desc is None:
-        font_desc = style.font_desc
-        font_desc.set_size(14*pango.SCALE)
-
-    pango_context = widget.create_pango_context()
-    layout = pango.Layout(pango_context)
-    layout.set_font_description(font_desc)
-    layout.set_text(text)
-    width, height = layout.get_pixel_size()
-
-    ctx.move_to(w_width/2-width/2, w_height/2-height/2)
-    ctx.set_source_rgba(*text_color)
-    ctx.show_layout(layout)
diff --git a/jamaui/favorites.py b/jamaui/favorites.py
deleted file mode 100644 (file)
index ef58143..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-try:
-    import hildon
-except:
-    import helldon as hildon
-import jamaendo
-from playerwindow import open_playerwindow
-from showartist import ShowArtist
-from showalbum import ShowAlbum
-from settings import settings
-import logging
-from fetcher import Fetcher
-import itertools
-
-from albumlist import MusicList
-
-log = logging.getLogger(__name__)
-
-def _alist(l, match):
-    for key, value in l:
-        if key == match:
-            return value
-    return None
-
-class FavoritesWindow(hildon.StackableWindow):
-    def __init__(self):
-        hildon.StackableWindow.__init__(self)
-        self.set_title("Favorites")
-        self.connect('destroy', self.on_destroy)
-        self.fetcher = None
-        self.idmap = {}
-
-        self.panarea = hildon.PannableArea()
-        self.favorites = MusicList()
-        self.favorites.connect('row-activated', self.row_activated)
-        self.panarea.add(self.favorites)
-        self.add(self.panarea)
-
-        if not settings.user:
-            self.favorites.loading_message = """give your username
-to the settings dialog
-favorites appear
-"""
-        else:
-            self.favorites.loading_message = """Loading favorites"""
-
-        self.start_favorites_fetcher()
-
-    def on_destroy(self, wnd):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def start_favorites_fetcher(self):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-
-        def gen():
-            generated = []
-            for item in jamaendo.favorite_albums(settings.user):
-                generated.append(item.ID)
-                yield item
-            fav = [f[1] for f in settings.favorites \
-                       if isinstance(f, tuple) and \
-                       len(f) == 2 and \
-                       f[0] == 'album' and \
-                       f[1] not in generated]
-            for item in jamaendo.get_albums(fav):
-                yield item
-
-        self.fetcher = Fetcher(gen,
-                               self,
-                               on_item = self.on_favorites_result,
-                               on_ok = self.on_favorites_complete,
-                               on_fail = self.on_favorites_complete)
-        self.fetcher.start()
-
-    def on_favorites_result(self, wnd, item):
-        if wnd is self:
-            if item.ID not in self.idmap:
-                self.idmap[item.ID] = item
-                self.favorites.add_items([item])
-
-    def on_favorites_complete(self, wnd, error=None):
-        if wnd is self:
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def get_item_text(self, item):
-        if isinstance(item, jamaendo.Album):
-            return "%s - %s" % (item.artist_name, item.name)
-        elif isinstance(item, jamaendo.Track):
-            return "%s - %s" % (item.artist_name, item.name)
-        else:
-            return item.name
-
-    def row_activated(self, treeview, path, view_column):
-        _id = self.favorites.get_item_id(path)
-        item = self.idmap.get(_id)
-        if item:
-            self.open_item(item)
-
-    def open_item(self, item):
-        if isinstance(item, jamaendo.Album):
-            wnd = ShowAlbum(item)
-            wnd.show_all()
-        elif isinstance(item, jamaendo.Artist):
-            wnd = ShowArtist(item)
-            wnd.show_all()
-        elif isinstance(item, jamaendo.Track):
-            wnd = open_playerwindow()
-            wnd.play_tracks([item])
diff --git a/jamaui/featured.py b/jamaui/featured.py
deleted file mode 100644 (file)
index b85ec5a..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-import gtk
-try:
-    import hildon
-except:
-    import helldon as hildon
-import jamaendo
-from playerwindow import open_playerwindow
-from showartist import ShowArtist
-from showalbum import ShowAlbum
-from albumlist import MusicList
-from player import Playlist
-from fetcher import Fetcher
-import logging
-
-log = logging.getLogger(__name__)
-
-def _alist(l, match):
-    for key, value in l:
-        if key == match:
-            return value
-    return None
-
-class FeaturedWindow(hildon.StackableWindow):
-    features = (
-        ("New releases",jamaendo.new_releases),
-        ("Top albums today", lambda: jamaendo.top_albums(order='ratingday_desc')),
-        ("Top tracks today", lambda: jamaendo.top_tracks(order='ratingday_desc')),
-        ("Albums of the week",jamaendo.albums_of_the_week),
-        ("Tracks of the week",jamaendo.tracks_of_the_week),
-        ("Top 50 tags", lambda: jamaendo.top_tags(count=50)),
-        ("Top 50 artists", lambda: jamaendo.top_artists(count=50)),
-        ("Top 50 albums", lambda: jamaendo.top_albums(count=50)),
-        ("Top 50 tracks", lambda: jamaendo.top_tracks(count=50)),
-        )
-
-    def __init__(self, feature):
-        hildon.StackableWindow.__init__(self)
-        self.set_title(feature)
-
-        self.fetcher = None
-        self.connect('destroy', self.on_destroy)
-        self.featurefn = _alist(self.features, feature)
-
-        # Results list
-        self.panarea = hildon.PannableArea()
-        self.musiclist = MusicList()
-        self.musiclist.connect('row-activated', self.row_activated)
-        self.panarea.add(self.musiclist)
-
-        self.idmap = {}
-        self.items = []
-
-        self.add(self.panarea)
-
-        self.create_menu()
-
-        self.start_feature_fetcher()
-
-    def start_feature_fetcher(self):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-        self.fetcher = Fetcher(self.featurefn, self,
-                               on_item = self.on_feature_result,
-                               on_ok = self.on_feature_complete,
-                               on_fail = self.on_feature_complete)
-        self.fetcher.start()
-
-    def on_feature_result(self, wnd, item):
-        if wnd is self:
-            self.musiclist.add_items([item])
-            self.idmap[item.ID] = item
-            self.items.append(item)
-
-    def on_feature_complete(self, wnd, error=None):
-        if wnd is self:
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def create_menu(self):
-        def on_player(*args):
-            from playerwindow import open_playerwindow
-            open_playerwindow()
-        self.menu = hildon.AppMenu()
-        player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Open player")
-        player.connect("clicked", on_player)
-        self.menu.append(player)
-        self.menu.show_all()
-        self.set_app_menu(self.menu)
-
-    def on_destroy(self, wnd):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def row_activated(self, treeview, path, view_column):
-        _id = self.musiclist.get_item_id(path)
-        item = self.idmap[_id]
-        self.open_item(item)
-
-    def open_item(self, item):
-        if isinstance(item, jamaendo.Album):
-            wnd = ShowAlbum(item)
-            wnd.show_all()
-        elif isinstance(item, jamaendo.Artist):
-            wnd = ShowArtist(item)
-            wnd.show_all()
-        elif isinstance(item, jamaendo.Track):
-            playlist = Playlist(self.items)
-            playlist.jump_to(item.ID)
-            wnd = open_playerwindow()
-            wnd.play_tracks(playlist)
-        elif isinstance(item, jamaendo.Tag):
-            self.start_tag_fetcher(item.ID)
-
-    def start_tag_fetcher(self, item_id):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-        self.fetcher = Fetcher(lambda: jamaendo.get_tag_tracks(item_id),
-                               self,
-                               on_item = self.on_tag_result,
-                               on_ok = self.on_tag_complete,
-                               on_fail = self.on_tag_complete)
-        self.fetcher.taglist = []
-        self.fetcher.start()
-
-    def on_tag_result(self, wnd, item):
-        if wnd is self and hasattr(self.fetcher, 'taglist'):
-            self.fetcher.taglist.append(item)
-
-    def on_tag_complete(self, wnd, error=None):
-        if wnd is self:
-            self.fetcher.stop()
-            if error is not None and hasattr(self.fetcher, 'taglist'):
-                wnd = open_playerwindow()
-                wnd.play_tracks(self.fetcher.taglist)
-            self.fetcher = None
-
diff --git a/jamaui/fetcher.py b/jamaui/fetcher.py
deleted file mode 100644 (file)
index eb1d08d..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-# Background fetcher:
-# Takes a generator and a notification identifier as parameter,
-# starts a thread, and post a notification whenever data arrives.
-# Posts a completion notification when done.
-# Terminates if generator fails or encounters an error.
-
-import threading
-from postoffice import postoffice
-import jamaendo
-import logging
-
-import gobject
-import gtk
-import hildon
-
-log = logging.getLogger(__name__)
-
-class _Worker(threading.Thread):
-    def __init__(self, generator, owner):
-        threading.Thread.__init__(self)
-        self.setDaemon(True)
-        self.generator = generator
-        self.owner = owner
-
-    def _post(self, item):
-        def idle_fetch(owner, item):
-            postoffice.notify("fetch", owner, item)
-        gobject.idle_add(idle_fetch, self.owner, item)
-
-    def _post_ok(self):
-        def idle_fetch_ok(owner):
-            postoffice.notify("fetch-ok", owner)
-        gobject.idle_add(idle_fetch_ok, self.owner)
-
-    def _post_fail(self, e):
-        def idle_fetch_fail(owner, e):
-            postoffice.notify("fetch-fail", owner, e)
-        gobject.idle_add(idle_fetch_fail, self.owner, e)
-
-    def run(self):
-        try:
-            for item in self.generator():
-                self._post(item)
-            self._post_ok()
-        except jamaendo.JamendoAPIException, e:
-            log.exception("Failed to fetch, id %s" % (self.owner))
-            self._post_fail(e)
-
-class Fetcher(object):
-    def __init__(self, generator, owner, on_item = None, on_ok = None, on_fail = None):
-        self.generator = generator
-        self.owner = owner
-        self.worker = None
-
-        self.on_item = on_item
-        self.on_ok = on_ok
-        self.on_fail = on_fail
-
-    def _on_item_cb(self, i, x):
-        self.on_item(i, x)
-
-    def _on_ok_cb(self, i):
-        self.on_ok(i)
-        if isinstance(self.owner, gtk.Window):
-            hildon.hildon_gtk_window_set_progress_indicator(self.owner, 0)
-
-    def _on_fail_cb(self, i, e):
-        self.on_fail(i, e)
-        if isinstance(self.owner, gtk.Window):
-            hildon.hildon_gtk_window_set_progress_indicator(self.owner, 0)
-
-    def start(self):
-        postoffice.connect('fetch', self, self._on_item_cb)
-        postoffice.connect('fetch-ok', self, self._on_ok_cb)
-        postoffice.connect('fetch-fail', self, self._on_fail_cb)
-        if isinstance(self.owner, gtk.Window):
-            hildon.hildon_gtk_window_set_progress_indicator(self.owner, 1)
-        self.worker = _Worker(self.generator, self.owner)
-        self.worker.start()
-
-    def stop(self):
-        postoffice.disconnect(['fetch', 'fetch-ok', 'fetch-fail'], self)
diff --git a/jamaui/listbox.py b/jamaui/listbox.py
deleted file mode 100644 (file)
index dad31ef..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-# what the fuck, GTK
-import gtk
-try:
-    import hildon
-    _using_helldon = False
-except:
-    import helldon as hildon
-    _using_helldon = True
-
-class ListBox(gtk.TreeView):
-    def __init__(self):
-        gtk.TreeView.__init__(self)
-        self.store = gtk.ListStore(str)
-        column = gtk.TreeViewColumn("Text")
-        textRenderer = gtk.CellRendererText()
-        column.pack_start(textRenderer, True)
-        column.set_attributes(textRenderer, text = 0)
-        self.append_column(column)
-        self.set_model(self.store)
-
-    def get_text(self, path):
-        it = self.store.get_iter(path)
-        if not it:
-            return None
-        ret = self.store.get(it, 0)
-        return ret[0]
-
-    def get_selected_text(self):
-        model, it = self.get_selection().get_selected()
-        if not it:
-            return None
-        ret = self.store.get(it, 0)
-        return ret[0]
-
-    def append(self, text):
-        self.store.append([text])
-
-    def on_select(self, callback, *args):
-        def cb(lb, path, col):
-            ret = self.get_text(path)
-            if ret:
-                callback(ret, *args)
-        self.connect('row-activated', cb)
-
-class ListDialog(gtk.Dialog):
-    def __init__(self, title, parent=None):
-        gtk.Dialog.__init__(self, title, parent if not _using_helldon else None)
-        self.listbox = ListBox()
-        panarea = hildon.PannableArea()
-        panarea.add(self.listbox)
-        panarea.set_size_request_policy(hildon.SIZE_REQUEST_CHILDREN)
-        self.vbox.pack_start(panarea, True, True, 0)
-
-        self.selected = None
-
-        def selector(selected, dialog):
-            if selected:
-                self.selected = selected
-                dialog.response(gtk.RESPONSE_OK)
-        self.listbox.on_select(selector, self)
-
-class ButtonListDialog(gtk.Dialog):
-    def __init__(self, title, parent=None):
-        gtk.Dialog.__init__(self, title, parent if not _using_helldon else None)
-        self.panarea = hildon.PannableArea()
-        self.panarea.set_size_request(800, 400)
-        self.buttons = gtk.VBox(False, 0)
-        self.panarea.add_with_viewport(self.buttons)
-        self.vbox.pack_start(self.panarea, True, True, 0)
-
-    def add_button(self, label, clickcb, *args):
-        btn = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH|gtk.HILDON_SIZE_FINGER_HEIGHT,
-                            hildon.BUTTON_ARRANGEMENT_VERTICAL)
-        btn.set_label(label)
-        btn.connect('clicked', clickcb, *args)
-        self.buttons.pack_start(btn, False, False, 0)
-
diff --git a/jamaui/ossohelper.py b/jamaui/ossohelper.py
deleted file mode 100644 (file)
index c7cbde2..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-# ossohelper.py - Helper to osso functions
-#
-#  Copyright (c) 2008 INdT - Instituto Nokia de Tecnologia
-#
-#  This file is part of carman-python.
-#  Modified for inclusion in Panucci (June 2009).
-#
-#  carman-python 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 3 of the License, or
-#  (at your option) any later version.
-#
-#  carman-python 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, see <http://www.gnu.org/licenses/>.
-
-import os
-import logging
-
-__log = logging.getLogger(__name__)
-
-try:
-    import osso
-    __osso_imported__ = True
-except ImportError:
-    __log.warning('osso module not found - are you running on a desktop?')
-    __osso_imported__ = False
-
-DEVICE_STATE_NORMAL     = "normal"
-OSSO_DEVSTATE_MODE_FILE = "/tmp/.libosso_device_mode_cache"
-
-__osso_context__ = None
-__osso_application__ = None
-__osso_device_state__ = None
-
-def application_init(app_name, app_version):
-    """
-    Osso application init.
-    """
-
-    global __osso_context__, __osso_device_state__, __osso_application__
-
-    if __osso_imported__:
-        if has_osso():
-            __log.warning('osso application was already called. Ignoring...')
-            return
-        try:
-            __osso_context__ = osso.Context(app_name, app_version, False)
-        except Exception, err:
-            __log.warning('osso module found but could not be initialized: %s',
-                          err)
-            __osso_context__ = None
-            return
-
-        try:
-            __osso_application__ = osso.Application(__osso_context__)
-        except Exception, err:
-            __log.warning('error creating osso application: %s' % err)
-            __osso_application__ = None
-
-        __log.debug( 'osso application init sent - %s v%s', app_name,
-                    app_version)
-        __osso_device_state__ = osso.DeviceState(__osso_context__)
-# application_init
-
-def application_exit():
-    """
-    Osso application exit.
-    """
-    #if __osso_application__ is not None and __osso_context__ is not None:
-    #    try:
-    #        #__osso_application__.close()
-    #        __osso_context__.close()
-    #    except Exception, err:
-    #        __log.warning('application end could not be sent: %s' % err)
-    #    __log.debug('osso application end sent')
-# application_exit
-
-def application_top(app_name):
-    """
-    Osso application top.
-    """
-    if __osso_imported__ and __osso_application__:
-        try:
-            __osso_application__.application_top(app_name)
-        except Exception, err:
-            __log.warning( "Error calling application top for %s: %s",
-                           app_name, err)
-        __log.info('osso application top for %s sent', app_name)
-
-# application_top
-
-def has_osso():
-    """
-    Return if the osso module was initialized and all objects were created
-    without any problem
-    """
-    return __osso_imported__ and not None in ( __osso_context__, 
-                                               __osso_device_state__, 
-                                               __osso_application__ )
-# has_osso
-
-def display_on():
-    """
-    Turn on the display
-    """
-    if __osso_device_state__ is not None:
-        __osso_device_state__.display_state_on()
-        __osso_device_state__.display_blanking_pause()
-        __log.info('osso display on')
-# display_on
-
-def display_blanking_pause():
-    """
-    Keep the backlight on. Should be called every 45 seconds.
-    """
-    if __osso_device_state__ is not None:
-        __osso_device_state__.display_blanking_pause()
-        __log.debug('osso blanking screen')
-#display_blanking_pause
-
-def get_device_state():
-    if __osso_device_state__ is not None:
-        cache_file_name = OSSO_DEVSTATE_MODE_FILE + "-" + str(os.getuid())
-        try:
-            state = os.readlink(cache_file_name)
-        except:
-            state = None
-        if not state:
-            __log.debug( "Failure to read device state from %s",
-                         cache_file_name)
-            state = DEVICE_STATE_NORMAL
-        return state
-    else:
-        return DEVICE_STATE_NORMAL
diff --git a/jamaui/player.py b/jamaui/player.py
deleted file mode 100644 (file)
index 31211eb..0000000
+++ /dev/null
@@ -1,446 +0,0 @@
-# Implements playback controls
-# Gstreamer stuff mostly snibbed from Panucci
-#
-# This file is part of Panucci.
-# Copyright (c) 2008-2009 The Panucci Audiobook and Podcast Player Project
-#
-# Panucci 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 3 of the License, or
-# (at your option) any later version.
-#
-# Panucci 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 Panucci.  If not, see <http://www.gnu.org/licenses/>.
-#
-
-import logging
-import pygst
-pygst.require('0.10')
-import gst
-import util
-import dbus
-
-import jamaendo
-from settings import settings
-from postoffice import postoffice
-from fetcher import Fetcher
-
-log = logging.getLogger(__name__)
-
-class _Player(object):
-    """Defines the internal player interface"""
-    def __init__(self):
-        pass
-    def play_url(self, filetype, uri):
-        raise NotImplemented
-    def playing(self):
-        raise NotImplemented
-    def play_pause_toggle(self):
-        self.pause() if self.playing() else self.play()
-    def play(self):
-        raise NotImplemented
-    def pause(self):
-        raise NotImplemented
-    def stop(self):
-        raise NotImplemented
-    def set_eos_callback(self, cb):
-        raise NotImplemented
-
-class GStreamer(_Player):
-    """Wraps GStreamer"""
-    STATES = { gst.STATE_NULL    : 'stopped',
-               gst.STATE_PAUSED  : 'paused',
-               gst.STATE_PLAYING : 'playing' }
-
-    def __init__(self):
-        _Player.__init__(self)
-        self.time_format = gst.Format(gst.FORMAT_TIME)
-        self.player = None
-        self.filesrc = None
-        self.filesrc_property = None
-        self.volume_control = None
-        self.volume_multiplier = 1.
-        self.volume_property = None
-        self.eos_callback = lambda: self.stop()
-        postoffice.connect('settings-changed', self, self.on_settings_changed)
-
-    def on_settings_changed(self, key, value):
-        if key == 'volume':
-            self._set_volume_level(value)
-        #postoffice.disconnect(self)
-
-
-    def play_url(self, filetype, uri):
-        if None in (filetype, uri):
-            self.player = None
-            return False
-
-        _first = False
-        if self.player is None:
-            _first = True
-            if False:
-                self._maemo_setup_playbin2_player(uri)
-                log.debug('Using playbin2 (maemo)')
-            elif util.platform == 'maemo':
-                self._maemo_setup_playbin_player()
-                log.debug('Using playbin (maemo)')
-            else:
-                self._setup_playbin_player()
-                log.debug( 'Using playbin (non-maemo)' )
-
-            bus = self.player.get_bus()
-            bus.add_signal_watch()
-            bus.connect('message', self._on_message)
-            self._set_volume_level(settings.volume)
-
-        self._set_uri_to_be_played(uri)
-
-        self.play()
-        return True
-
-    def get_state(self):
-        if self.player:
-            state = self.player.get_state()[1]
-            return self.STATES.get(state, 'none')
-        return 'none'
-
-    def get_position_duration(self):
-        try:
-            pos_int = self.player.query_position(self.time_format, None)[0]
-            dur_int = self.player.query_duration(self.time_format, None)[0]
-        except Exception, e:
-            log.exception('Error getting position')
-            pos_int = dur_int = 0
-        return pos_int, dur_int
-
-    def playing(self):
-        return self.get_state() == 'playing'
-
-    def play(self):
-        if self.player:
-            self.player.set_state(gst.STATE_PLAYING)
-
-    def pause(self):
-        if self.player:
-            self.player.set_state(gst.STATE_PAUSED)
-
-    def stop(self, reset = True):
-        if self.player:
-            self.player.set_state(gst.STATE_NULL)
-            if reset:
-                self.player = None
-
-    def _maemo_setup_playbin2_player(self, url):
-        self.player = gst.parse_launch("playbin2 uri=%s" % (url,))
-        self.filesrc = self.player
-        self.filesrc_property = 'uri'
-        self.volume_control = self.player
-        self.volume_multiplier = 1.
-        self.volume_property = 'volume'
-
-    def _maemo_setup_playbin_player( self):
-        self.player = gst.element_factory_make('playbin2', 'player')
-        self.filesrc = self.player
-        self.filesrc_property = 'uri'
-        self.volume_control = self.player
-        self.volume_multiplier = 1.
-        self.volume_property = 'volume'
-        return True
-
-    def _setup_playbin_player( self ):
-        """ This is for situations where we have a normal (read: non-maemo)
-        version of gstreamer like on a regular linux distro. """
-        self.player = gst.element_factory_make('playbin2', 'player')
-        self.filesrc = self.player
-        self.filesrc_property = 'uri'
-        self.volume_control = self.player
-        self.volume_multiplier = 10.
-        self.volume_property = 'volume'
-
-    def _on_decoder_pad_added(self, decoder, src_pad, sink_pad):
-        # link the decoder's new "src_pad" to "sink_pad"
-        src_pad.link( sink_pad )
-
-    def _get_volume_level(self):
-        if self.volume_control is not None:
-            vol = self.volume_control.get_property( self.volume_property )
-            return  vol / float(self.volume_multiplier)
-
-    def _set_volume_level(self, value):
-        assert  0 <= value <= 1
-
-        if self.volume_control is not None:
-            vol = value * float(self.volume_multiplier)
-            log.debug("Setting volume to %s", vol)
-            self.volume_control.set_property( self.volume_property, vol )
-
-    def _set_uri_to_be_played(self, uri):
-        # Sets the right property depending on the platform of self.filesrc
-        if self.player is not None:
-            self.filesrc.set_property(self.filesrc_property, uri)
-            log.info("%s", uri)
-
-    def _on_message(self, bus, message):
-        t = message.type
-
-        if t == gst.MESSAGE_EOS:
-            log.debug("Gstreamer: End of stream")
-            self.eos_callback()
-        #elif t == gst.MESSAGE_STATE_CHANGED:
-        #    if (message.src == self.player and
-        #        message.structure['new-state'] == gst.STATE_PLAYING):
-        #        log.debug("gstreamer: state -> playing")
-        elif t == gst.MESSAGE_ERROR:
-            err, debug = message.parse_error()
-            log.critical( 'Error: %s %s', err, debug )
-            self.stop()
-
-    def set_eos_callback(self, cb):
-        self.eos_callback = cb
-
-if util.platform == 'maemo':
-    class OssoPlayer(_Player):
-        """
-        A player which uses osso-media-player for playback (Maemo-specific)
-        """
-
-        SERVICE_NAME         = "com.nokia.osso_media_server"
-        OBJECT_PATH          = "/com/nokia/osso_media_server"
-        AUDIO_INTERFACE_NAME = "com.nokia.osso_media_server.music"
-
-        def __init__(self):
-            self._on_eos = lambda: self.stop()
-            self._state = 'none'
-            self._audio = self._init_dbus()
-            self._init_signals()
-
-        def play_url(self, filetype, uri):
-            self._audio.play_media(uri)
-
-        def playing(self):
-            return self._state == 'playing'
-
-        def play_pause_toggle(self):
-            self.pause() if self.playing() else self.play()
-
-        def play(self):
-            self._audio.play()
-
-        def pause(self):
-            if self.playing():
-                self._audio.pause()
-
-        def stop(self):
-            self._audio.stop()
-
-        def set_eos_callback(self, cb):
-            self._on_eos = cb
-
-
-        def _init_dbus(self):
-            session_bus = dbus.SessionBus()
-            oms_object = session_bus.get_object(self.SERVICE_NAME,
-                                                self.OBJECT_PATH,
-                                                introspect = False,
-                                                follow_name_owner_changes = True)
-            return dbus.Interface(oms_object, self.AUDIO_INTERFACE_NAME)
-
-        def _init_signals(self):
-            error_signals = {
-                "no_media_selected":            "No media selected",
-                "file_not_found":               "File not found",
-                "type_not_found":               "Type not found",
-                "unsupported_type":             "Unsupported type",
-                "gstreamer":                    "GStreamer Error",
-                "dsp":                          "DSP Error",
-                "device_unavailable":           "Device Unavailable",
-                "corrupted_file":               "Corrupted File",
-                "out_of_memory":                "Out of Memory",
-                "audio_codec_not_supported":    "Audio codec not supported"
-            }
-
-            # Connect status signals
-            self._audio.connect_to_signal( "state_changed",
-                                                self._on_state_changed )
-            self._audio.connect_to_signal( "end_of_stream",
-                                                lambda x: self._call_eos() )
-
-            # Connect error signals
-            for error, msg in error_signals.iteritems():
-                self._audio.connect_to_signal(error, lambda *x: self._error(msg))
-
-        def _error(self, msg):
-            log.error(msg)
-
-        def _call_eos(self):
-            self._on_eos()
-
-        def _on_state_changed(self, state):
-            states = ("playing", "paused", "stopped")
-            self.__state = state if state in states else 'none'
-
-#    PlayerBackend = OssoPlayer
-#else:
-PlayerBackend = GStreamer
-
-class Playlist(object):
-    def __init__(self, items = []):
-        self.radio_mode = False
-        self.radio_id = None
-        self.radio_name = None
-        if items is None:
-            items = []
-        for item in items:
-            assert(isinstance(item, jamaendo.Track))
-        self.items = items
-        self._current = -1
-
-    def add(self, item):
-        if isinstance(item, list):
-            for i in item:
-                assert(isinstance(i, jamaendo.Track))
-            self.items.extend(item)
-        else:
-            self.items.append(item)
-
-    def next(self):
-        if self.has_next():
-            self._current = self._current + 1
-            return self.items[self._current]
-        return None
-
-    def prev(self):
-        if self.has_prev():
-            self._current = self._current - 1
-            return self.items[self._current]
-        return None
-
-    def has_next(self):
-        return self._current < (len(self.items)-1)
-
-    def has_prev(self):
-        return self._current > 0
-
-    def current(self):
-        if self._current >= 0:
-            return self.items[self._current]
-        return None
-
-    def jump_to(self, item_id):
-        for c, i in enumerate(self.items):
-            if i.ID == item_id:
-                self._current = c
-
-    def current_index(self):
-        return self._current
-
-    def size(self):
-        return len(self.items)
-
-    def __repr__(self):
-        return "Playlist(%d of %s)"%(self._current, ", ".join([str(item.ID) for item in self.items]))
-
-class Player(object):
-    def __init__(self):
-        self.backend = PlayerBackend()
-        self.backend.set_eos_callback(self._on_eos)
-        self.playlist = Playlist()
-        self.fetcher = None # for refilling the radio
-
-    def get_position_duration(self):
-        return self.backend.get_position_duration()
-
-    def _play_track(self, track, notify='play'):
-        self.backend.play_url('mp3', track.mp3_url())
-        log.debug("playing %s", track)
-        postoffice.notify(notify, track)
-
-    def _refill_radio(self):
-        log.debug("Refilling radio %s", self.playlist)
-        #self.playlist.add(jamaendo.get_radio_tracks(self.playlist.radio_id))
-        self._start_radio_fetcher()
-
-    def _start_radio_fetcher(self):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-        self.fetcher = Fetcher(lambda: jamaendo.get_radio_tracks(self.playlist.radio_id),
-                               self,
-                               on_item = self._on_radio_result,
-                               on_ok = self._on_radio_complete,
-                               on_fail = self._on_radio_complete)
-        self.fetcher.has_no_results = True
-        self.fetcher.start()
-
-    def _on_radio_result(self, wnd, item):
-        if wnd is self:
-            self.playlist.add(item)
-            if not self.playing():
-                if self.fetcher.has_no_results:
-                    self.fetcher.has_no_results = False
-                    entry = self.playlist.next()
-                    self._play_track(entry)
-
-    def _on_radio_complete(self, wnd, error=None):
-        if wnd is self:
-            if error:
-                self.stop()
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def play(self, playlist = None):
-        if playlist:
-            self.playlist = playlist
-        elif self.playlist is None:
-            self.playlist = Playlist()
-
-        if self.playlist.current():
-            entry = self.playlist.current()
-            self._play_track(entry)
-        elif self.playlist.has_next():
-            entry = self.playlist.next()
-            self._play_track(entry)
-        elif self.playlist.radio_mode:
-            self._refill_radio()
-            #self.play()
-
-    def next(self):
-        if self.playlist.has_next():
-            self.backend.stop(reset=False)
-            entry = self.playlist.next()
-            self._play_track(entry, notify='next')
-        elif self.playlist.radio_mode:
-            self._refill_radio()
-            #if self.playlist.has_next():
-            #    self.next()
-            #else:
-            #    self.stop()
-        else:
-            self.stop()
-
-    def prev(self):
-        if self.playlist.has_prev():
-            self.backend.stop(reset=False)
-            entry = self.playlist.prev()
-            self._play_track(entry, 'prev')
-
-    def pause(self):
-        self.backend.pause()
-        postoffice.notify('pause', self.playlist.current())
-
-    def stop(self):
-        self.backend.stop()
-        postoffice.notify('stop', self.playlist.current())
-
-    def playing(self):
-        return self.backend.playing()
-
-    def _on_eos(self):
-        self.next()
-
-the_player = Player() # the player instance
diff --git a/jamaui/playerwindow.py b/jamaui/playerwindow.py
deleted file mode 100644 (file)
index fcf9ff3..0000000
+++ /dev/null
@@ -1,388 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-import gtk
-import gobject
-try:
-    import hildon
-except:
-    import helldon as hildon
-import util
-import datetime
-import pango
-import jamaendo
-from settings import settings
-from postoffice import postoffice
-from player import Playlist, the_player
-import logging
-import cgi
-
-from songposition import SongPosition
-from listbox import ListDialog
-import colors
-log = logging.getLogger(__name__)
-
-class PlayerWindow(hildon.StackableWindow):
-    instance = None
-
-    def __init__(self):
-        hildon.StackableWindow.__init__(self)
-        self.set_title("Jamaendo")
-
-        PlayerWindow.instance = self
-
-        self.connect('destroy', self.on_destroy)
-
-        self.player = the_player
-        self.playlist = the_player.playlist
-
-        vbox = gtk.VBox(False, 8)
-
-        hbox = gtk.HBox(False)
-
-        self.cover = gtk.Image()
-        self.set_default_cover()
-
-        vbox2 = gtk.VBox()
-
-        self.playlist_pos = gtk.Label()
-        self.playlist_pos.set_alignment(1.0,0)
-        self.track = gtk.Label()
-        self.track.set_alignment(0,1)
-        self.track.set_ellipsize(pango.ELLIPSIZE_END)
-        self.artist = gtk.Label()
-        self.artist.set_alignment(0,0.5)
-        self.artist.set_ellipsize(pango.ELLIPSIZE_END)
-        self.album = gtk.Label()
-        self.album.set_alignment(0,0)
-        self.album.set_ellipsize(pango.ELLIPSIZE_END)
-        self.playtime = gtk.Label()
-        self.playtime.set_alignment(1,0)
-        self.playtime.set_ellipsize(pango.ELLIPSIZE_END)
-        self.progress = SongPosition()
-
-        self.set_labels('', '', '', 0, 0)
-
-        self._position_timer = None
-
-        vbox2.pack_start(self.playlist_pos, False)
-        vbox2.pack_start(self.track, True)
-        vbox2.pack_start(self.artist, True)
-        vbox2.pack_start(self.album, True)
-        vbox2.pack_start(self.playtime, False, True)
-        vbox2.pack_start(self.progress, False, True)
-
-        hbox.set_border_width(8)
-        hbox.pack_start(self.cover, False, True, 8)
-        hbox.pack_start(vbox2, True, True, 0)
-
-        vbox.pack_start(hbox, True, True, 0)
-
-        btns = gtk.HButtonBox()
-        btns.set_property('layout-style', gtk.BUTTONBOX_SPREAD)
-
-        vbox.pack_start(btns, False, True, 0)
-
-        self.add_stock_button(btns, gtk.STOCK_MEDIA_PREVIOUS, self.on_prev)
-        self.add_play_button(btns)
-        self.add_stock_button(btns, gtk.STOCK_MEDIA_STOP, self.on_stop)
-        self.add_stock_button(btns, gtk.STOCK_MEDIA_NEXT, self.on_next)
-
-        self.controls = btns
-
-        self.add(vbox)
-
-        postoffice.connect('album-cover', self, self.set_album_cover)
-        postoffice.connect(['next', 'prev', 'play', 'pause', 'stop'], self, self.on_state_changed)
-
-        self.on_state_changed()
-
-        self.create_menu()
-
-    def create_menu(self):
-        self.menu = hildon.AppMenu()
-
-        def to_artist():
-            from showartist import ShowArtist
-            try:
-                hildon.hildon_gtk_window_set_progress_indicator(self, 1)
-                track = self.playlist.current()
-                artist = jamaendo.get_artist(int(track.artist_id))
-                wnd = ShowArtist(artist)
-                wnd.show_all()
-            finally:
-                hildon.hildon_gtk_window_set_progress_indicator(self, 0)
-        def to_album():
-            from showalbum import ShowAlbum
-            try:
-                hildon.hildon_gtk_window_set_progress_indicator(self, 1)
-                track = self.playlist.current()
-                album = jamaendo.get_album(int(track.album_id))
-                wnd = ShowAlbum(album)
-                wnd.show_all()
-            finally:
-                hildon.hildon_gtk_window_set_progress_indicator(self, 0)
-
-        b = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        b.set_label("Artist")
-        b.connect("clicked", lambda w: gobject.idle_add(to_artist))
-        self.menu.append(b)
-
-        b = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        b.set_label("Album")
-        b.connect("clicked", lambda w: gobject.idle_add(to_album))
-        self.menu.append(b)
-
-        b = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        b.set_label("Add to playlist")
-        b.connect("clicked", self.on_add_to_playlist)
-        self.menu.append(b)
-
-        self.menu.show_all()
-        self.set_app_menu(self.menu)
-
-    def on_state_changed(self, *args):
-        self.update_state()
-        self.update_play_button()
-
-        if self.player.playing():
-            self.start_position_timer()
-        else:
-            self.stop_position_timer()
-
-
-    def get_album_id(self):
-        if self.player.playlist and self.player.playlist.current():
-            c = self.player.playlist.current()
-            if not c.album_id:
-                c.load()
-            if c.album_id:
-                return c.album_id
-        return None
-
-    def on_destroy(self, wnd):
-        PlayerWindow.instance = None
-        self.stop_position_timer()
-        postoffice.disconnect(['album-cover', 'next', 'prev', 'play', 'stop'], self)
-
-    def add_stock_button(self, btns, stock, cb):
-        btn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
-        btn.set_relief(gtk.RELIEF_NONE)
-        btn.set_focus_on_click(False)
-        sz = gtk.ICON_SIZE_BUTTON
-        btn.set_image(gtk.image_new_from_stock(stock, sz))
-        btn.connect('clicked', cb)
-        btns.add(btn)
-
-    def add_play_button(self, btns):
-        sz = gtk.ICON_SIZE_BUTTON
-        self.playimg = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PLAY, sz)
-        self.pauseimg = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PAUSE, sz)
-        btn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
-        btn.set_relief(gtk.RELIEF_NONE)
-        if self.player.playing():
-            btn.set_image(self.pauseimg)
-            btn.set_data('state', 'pause')
-        else:
-            btn.set_image(self.playimg)
-            btn.set_data('state', 'play')
-        btn.connect('clicked', self.on_play)
-        btns.add(btn)
-        self.playbtn = btn
-
-    def update_play_button(self):
-        if self.player.playing():
-            self.playbtn.set_image(self.pauseimg)
-            self.playbtn.set_data('state', 'pause')
-        else:
-            self.playbtn.set_image(self.playimg)
-            self.playbtn.set_data('state', 'play')
-
-    def set_labels(self, track, artist, album, playlist_pos, playlist_size):
-
-        if self.playlist.radio_mode:
-            ppstr = 'Radio: %s'%(cgi.escape(self.playlist.radio_name))
-        elif playlist_size > 0:
-            ppstr = 'Track %s of %s'%(int(playlist_pos)+1, playlist_size)
-        else:
-            ppstr = "No playlist"
-        ppstr = '<span font_desc="%s" foreground="%s">%s</span>'%(colors.SmallSystemFont(), colors.SecondaryTextColor(), ppstr)
-        self.playlist_pos.set_markup(ppstr)
-        self.track.set_markup('<span font_desc="%s">%s</span>'%(colors.LargeSystemFont(), cgi.escape(track)))
-        self.artist.set_markup('%s'%(cgi.escape(artist)))
-        self.album.set_markup('<span foreground="%s">%s</span>'%(colors.SecondaryTextColor(), cgi.escape(album)))
-
-        if not track:
-            txt = '<span font_desc="%s" foreground="%s">%s</span>' % \
-                (colors.XXLargeSystemFont(),
-                 colors.SecondaryTextColor(),
-                 '00:00'
-                 )
-            self.playtime.set_markup(txt)
-
-    def show_banner(self, message, timeout = 2000):
-        banner = hildon.hildon_banner_show_information(self, '', message)
-        banner.set_timeout(2000)
-
-    def on_add_to_playlist(self, button, user_data=None):
-        track = self.player.playlist.current()
-        from playlists import add_to_playlist
-        add_to_playlist(self, track)
-
-    def volume_changed_hildon(self, widget):
-        settings.volume = widget.get_level()/100.0
-
-    def mute_toggled(self, widget):
-        if widget.get_mute():
-            settings.volume = 0
-        else:
-            settings.volume = widget.get_level()/100.0
-
-    def on_position_timeout(self):
-        if the_player.playing():
-            self.set_song_position(*the_player.get_position_duration())
-        else:
-            log.debug("position timeout, but not playing")
-        return True
-
-    def start_position_timer(self):
-        if self._position_timer is not None:
-            self.stop_position_timer()
-        self._position_timer = gobject.timeout_add(1000, self.on_position_timeout)
-
-    def stop_position_timer(self):
-        if self._position_timer is not None:
-            gobject.source_remove(self._position_timer)
-            self._position_timer = None
-
-    def clear_position(self):
-        self.progress.set_position(0)
-
-    def nanosecs_to_str(self, ns):
-        time_secs = int(float(ns)/1000000000.0)
-        if time_secs <= 59:
-            return "00:%02d"%(time_secs)
-        time_mins = int(time_secs/60.0)
-        time_secs -= time_mins*60
-        if time_mins <= 59:
-            return "%02d:%02d"%(time_mins, time_secs)
-
-        time_hrs = int(time_mins/60.0)
-        time_mins -= time_hrs*60
-        return "%d:%02d:%02d"%(time_hrs, time_secs, time_mins)
-
-    def set_song_position(self, time_elapsed, total_time):
-        value = (float(time_elapsed) / float(total_time)) if total_time else 0
-        self.progress.set_position(value)
-
-
-        txt = '<span font_desc="%s" foreground="%s">%s</span>' % \
-            (colors.XXLargeSystemFont(),
-             colors.SecondaryTextColor(),
-             self.nanosecs_to_str(time_elapsed)
-             )
-        self.playtime.set_markup(txt)
-
-    def set_default_cover(self):
-        tmp = util.find_resource('album.png')
-        if tmp:
-            log.debug("Setting cover to %s", tmp)
-            self.cover.set_from_file(tmp)
-
-    def update_state(self):
-        item = self.playlist.current()
-        if item:
-            hildon.hildon_gtk_window_set_progress_indicator(self, 0)
-            if not item.name:
-                item.load()
-            self.set_labels(item.name, item.artist_name, item.album_name,
-                            self.playlist.current_index(), self.playlist.size())
-            self.set_default_cover()
-            postoffice.notify('request-album-cover', int(item.album_id), 300)
-        else:
-            self.set_labels('', '', '', 0, 0)
-            self.set_default_cover()
-        state = bool(item)
-        for btn in self.controls:
-            if isinstance(btn, hildon.GtkButton):
-                btn.set_sensitive(state)
-
-    def set_album_cover(self, albumid, size, cover):
-        if size == 300:
-            playing = self.get_album_id()
-            if playing and albumid and (int(playing) == int(albumid)):
-                log.debug("Setting cover to %s", cover)
-                self.cover.set_from_file(cover)
-
-    def play_radio(self, radio_name, radio_id):
-        playlist = Playlist([])
-        playlist.radio_mode = True
-        playlist.radio_name = radio_name
-        playlist.radio_id = radio_id
-        hildon.hildon_gtk_window_set_progress_indicator(self, 1)
-        self.__play_tracks(playlist)
-        #log.debug("Playlist current: %s, playing? %s",
-        #          playlist.current_index(),
-        #          self.player.playing())
-
-    def play_tracks(self, tracks):
-        self.__play_tracks(tracks)
-
-    def __play_tracks(self, tracks):
-        self.clear_position()
-        if isinstance(tracks, Playlist):
-            self.playlist = tracks
-        else:
-            self.playlist = Playlist(tracks)
-        log.debug("Playing: %s", self.playlist)
-        self.player.stop()
-        self.player.play(self.playlist)
-
-    def on_play(self, button):
-        if not self.player.playing():
-            self.player.play(self.playlist)
-        else:
-            self.player.pause()
-    def on_prev(self, button):
-        self.player.prev()
-
-    def on_next(self, button):
-        self.player.next()
-
-    def on_stop(self, button):
-        self.clear_position()
-        self.player.stop()
-
-def open_playerwindow():
-    if PlayerWindow.instance:
-        player = PlayerWindow.instance
-        stack = player.get_stack()
-        sz = stack.size()
-        windows = stack.pop(sz)
-        windows.remove(player)
-        windows.append(player)
-        stack.push_list(windows)
-    else:
-        player = PlayerWindow()
-        player.show_all()
-    return player
diff --git a/jamaui/playlists.py b/jamaui/playlists.py
deleted file mode 100644 (file)
index 6384b1e..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-import gtk
-try:
-    import hildon
-except:
-    import helldon as hildon
-import jamaendo
-from settings import settings
-import logging
-from albumlist import PlaylistList
-from postoffice import postoffice
-
-log = logging.getLogger(__name__)
-
-def _alist(l, match):
-    for key, value in l:
-        if key == match:
-            return value
-    return None
-
-def _show_banner(parent, message, timeout = 2000):
-    banner = hildon.hildon_banner_show_information(parent, '', message)
-    banner.set_timeout(2000)
-
-show_banner = _show_banner
-
-from listbox import ListDialog
-
-def add_to_playlist(wnd, track):
-    if not track:
-        _show_banner(wnd, "Nothing to add")
-        return
-
-    dialog = ListDialog('Add to playlist', wnd)
-    for name,_ in settings.playlists.iteritems():
-        dialog.listbox.append(name)
-    dialog.listbox.append("New...")
-    try:
-        dialog.show_all()
-        if dialog.run() == gtk.RESPONSE_OK:
-            selected_playlist = dialog.selected
-            if selected_playlist == "New...":
-                dialog.hide()
-                selected_playlist = create_new_playlist(wnd)
-            if track and selected_playlist:
-                if isinstance(track, (list, tuple)):
-                    for t in track:
-                        settings.add_to_playlist(selected_playlist, {'id':t.ID, 'data':t.get_data()})
-                else:
-                    settings.add_to_playlist(selected_playlist, {'id':track.ID, 'data':track.get_data()})
-                settings.save()
-                _show_banner(wnd, "Added to playlist '%s'" % (selected_playlist))
-    finally:
-        dialog.destroy()
-
-def create_new_playlist(wnd):
-    dia_name = gtk.Dialog()
-    dia_name.set_title("New playlist")
-    dia_name.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK )
-    entry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
-    entry.set_placeholder("Enter name")
-    entry.set_max_length(32)
-    entry.connect('activate', lambda entry, dialog: dialog.response(gtk.RESPONSE_OK), dia_name)
-    dia_name.vbox.pack_start(entry, True, True, 0)
-    dia_name.show_all()
-    if dia_name.run() != gtk.RESPONSE_OK:
-        return False
-    selected_playlist = entry.get_text()
-    dia_name.destroy()
-    if selected_playlist == '' or selected_playlist == 'New...':
-        return False
-    elif settings.get_playlist(selected_playlist):
-        _show_banner(wnd, "Playlist '%s' already exists!" % (selected_playlist))
-        return False
-    return selected_playlist
-
-
-class PlaylistsWindow(hildon.StackableWindow):
-    def __init__(self):
-        hildon.StackableWindow.__init__(self)
-        self.set_title("Playlists")
-        self.connect('destroy', self.on_destroy)
-
-        #self.deltoolbar = hildon.EditToolbar("Choose playlists to delete", "Delete")
-        #self.set_edit_toolbar(self.deltoolbar)
-        #self.deltoolbar.connect("button-clicked", self.on_delete_button)
-        #self.deltoolbar.connect_swapped("arrow-clicked", gtk.widget_destroy, window)
-
-        self.panarea = hildon.PannableArea()
-        self.playlistlist = PlaylistList()
-        self.playlistlist.loading_message = "Loading playlists"
-        self.playlistlist.empty_message = "No playlists found"
-        self.playlistlist.connect('row-activated', self.row_activated)
-        self.panarea.add(self.playlistlist)
-        self.add(self.panarea)
-
-        self.populate()
-
-        self.create_menu()
-
-        postoffice.connect('settings-changed', self, self.settings_changed)
-
-    def on_destroy(self, wnd):
-        postoffice.disconnect('settings-changed', self)
-
-    def create_menu(self):
-        def on_player(*args):
-            from playerwindow import open_playerwindow
-            open_playerwindow()
-        self.menu = hildon.AppMenu()
-        player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Open player")
-        player.connect("clicked", on_player)
-        self.menu.append(player)
-
-        #player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        #player.set_label("Delete playlists")
-        #player.connect("clicked", self.on_delete_playlists)
-        #self.menu.append(player)
-
-        # bah, I haven't decided how I want to do this yet
-        # lets just hide it for now
-        #player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        #player.set_label("Manage playlists")
-        #player.connect("clicked", self.on_manage_playlists)
-        #self.menu.append(player)
-        #player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        #player.set_label("Export...")
-        #player.connect("clicked", self.on_export_playlists)
-        #self.menu.append(player)
-        self.menu.show_all()
-        self.set_app_menu(self.menu)
-
-    def populate(self):
-        for key, lst in sorted(list(settings.playlists.iteritems())):
-            self.playlistlist.add_playlist(key, lst)
-        self.playlistlist.set_loading(False)
-
-    def settings_changed(self, setting, *args):
-        if setting == 'playlists':
-            self.playlistlist.set_loading(True)
-            self.playlistlist.get_model().clear()
-            self.populate()
-
-    #def on_delete_playlists(self, *args):
-    #    self.deltoolbar.show()
-    #    self.playlistlist.set_hildon_ui_mode(hildon.UI_MODE_EDIT)
-
-    #def on_delete_button(self, btn):
-    #    pass
-
-    #def on_export_playlists(self, *args):
-    #    _show_banner(self, "TODOO")
-
-
-    def row_activated(self, treeview, path, view_column):
-        sel = self.playlistlist.get_playlist_name(path)
-        pl = settings.get_playlist(sel)
-        if pl:
-            from showplaylist import ShowPlaylist
-            wnd = ShowPlaylist(sel, pl)
-            wnd.show_all()
-            #from playerwindow import open_playerwindow
-            #wnd = open_playerwindow()
-            #wnd.play_tracks(pl)
diff --git a/jamaui/postoffice.py b/jamaui/postoffice.py
deleted file mode 100644 (file)
index e47cccf..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-# message central
-
-from __future__ import with_statement
-import logging
-import threading
-
-log = logging.getLogger(__name__)
-
-class PostOffice(object):
-
-    def __init__(self):
-        self.lock = threading.RLock()
-        self.tags = {} # tag -> [callback]
-
-    def notify(self, tag, *data):
-        with self.lock:
-            #log.info("(%s %s)", tag, " ".join(str(x) for x in data))
-            clients = self.tags.get(tag)
-            if clients:
-                for ref, client in clients:
-                    client(*data)
-
-    def connect(self, tag, ref, callback):
-        with self.lock:
-            if not isinstance(tag, list):
-                tag = [tag]
-            for t in tag:
-                if t not in self.tags:
-                    self.tags[t] = []
-                clients = self.tags[t]
-                if callback not in clients:
-                    clients.append((ref, callback))
-
-    def disconnect(self, tag, ref):
-        with self.lock:
-            if not isinstance(tag, list):
-                tag = [tag]
-            for t in tag:
-                if t not in self.tags:
-                    self.tags[t] = []
-                self.tags[t] = [(_ref, cb) for _ref, cb in self.tags[t] if _ref != ref]
-
-postoffice = PostOffice()
-
-
diff --git a/jamaui/radios.py b/jamaui/radios.py
deleted file mode 100644 (file)
index ac3e2ed..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-import gtk
-try:
-    import hildon
-except:
-    import helldon as hildon
-import jamaendo
-from playerwindow import open_playerwindow
-from albumlist import RadioList
-from fetcher import Fetcher
-
-class RadiosWindow(hildon.StackableWindow):
-    def __init__(self):
-        hildon.StackableWindow.__init__(self)
-        self.fetcher = None
-        self.radios = {}
-
-        self.set_title("Radios")
-        self.connect('destroy', self.on_destroy)
-
-        # Results list
-        self.panarea = hildon.PannableArea()
-        self.radiolist = RadioList()
-        self.radiolist.connect('row-activated', self.row_activated)
-        self.panarea.add(self.radiolist)
-        self.add(self.panarea)
-
-        self.start_radio_fetcher()
-
-    def on_destroy(self, wnd):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def row_activated(self, treeview, path, view_column):
-        name, _id = self.radiolist.get_radio_id(path)
-        wnd = open_playerwindow()
-        wnd.play_radio(name, _id)
-
-    def start_radio_fetcher(self):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-        self.fetcher = Fetcher(jamaendo.starred_radios, self,
-                               on_item = self.on_radio_result,
-                               on_ok = self.on_radio_complete,
-                               on_fail = self.on_radio_complete)
-        self.fetcher.start()
-
-    def on_radio_result(self, wnd, item):
-        if wnd is self:
-            self.radios[item.ID] = item
-            self.radiolist.add_radios([item])
-
-    def on_radio_complete(self, wnd, error=None):
-        if wnd is self:
-            self.fetcher.stop()
-            self.fetcher = None
diff --git a/jamaui/refresh.py b/jamaui/refresh.py
deleted file mode 100644 (file)
index ed5fd37..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-
-class RefreshDialog(object):
-    def __init__(self):
-        self.notebook = gtk.Notebook()
-        info = gtk.VBox()
-        info.pack_start(gtk.Label("Downloading complete DB from jamendo.com."), True, False)
-        info.pack_start(gtk.Label("This will download approximately 8 MB."), True, False)
-        self.force = hildon.GtkToggleButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
-        self.force.set_label("Force refresh")
-
-        info.pack_start(self.force, True, False)
-        self.notebook.append_page(info)
-
-        pcont = gtk.VBox()
-        self.progress = gtk.ProgressBar()
-        pcont.pack_start(self.progress, True, False)
-        self.notebook.append_page(pcont,
-                                  gtk.Label("Updating Database"))
-        self.progress.set_fraction(0)
-        self.progress.set_orientation(gtk.PROGRESS_LEFT_TO_RIGHT)
-        self.progress.set_text("Downloading...")
-
-        self.notebook.append_page(gtk.Label("Database refreshed."))
-
-        self.dialog = hildon.WizardDialog(None, "Refresh", self.notebook)
-        self.notebook.connect("switch-page", self.on_switch)
-        self.dialog.set_forward_page_func(self.forward_func)
-
-        self.refresher = None
-
-    def on_complete(self, status):
-        hildon.hildon_gtk_window_set_progress_indicator(self.dialog, 0)
-        if status:
-            self.progress.set_fraction(1)
-            self.progress.set_text("DB up to date.")
-        else:
-            self.progress.set_fraction(0)
-            self.progress.set_text("Download failed.")
-
-    def on_progress(self, percent):
-        if percent < 100:
-            self.progress.set_text("Downloading...")
-        self.progress.set_fraction(percent/100.0)
-
-    def on_switch(self, notebook, page, num):
-        if num == 1:
-            hildon.hildon_gtk_window_set_progress_indicator(self.dialog, 1)
-            refresh_dump(self.on_complete, self.on_progress, force=self.force.get_active())
-        elif self.refresher:
-            # cancel download
-            pass
-        return True
-
-    def forward_func(self, notebook, current, userdata):
-        #page = notebook.get_nth_page(current)
-        if current == 0:
-            return True
-        else:
-            return False
-
-    def show_all(self):
-        self.dialog.show_all()
-
-    def run(self):
-        self.dialog.run()
-
-    def hide(self):
-        self.dialog.hide()
diff --git a/jamaui/search.py b/jamaui/search.py
deleted file mode 100644 (file)
index c05794c..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-import gtk
-try:
-    import hildon
-except:
-    import helldon as hildon
-import jamaendo
-from playerwindow import open_playerwindow
-from showartist import ShowArtist
-from showalbum import ShowAlbum
-from albumlist import MusicList
-from fetcher import Fetcher
-
-class SearchWindow(hildon.StackableWindow):
-    def __init__(self):
-        hildon.StackableWindow.__init__(self)
-        self.set_title("Search")
-        self.idmap = {}
-
-        vbox = gtk.VBox(False, 0)
-
-        self.fetcher = None
-        self.connect('destroy', self.on_destroy)
-
-        # Results list
-        self.panarea = hildon.PannableArea()
-        self.musiclist = MusicList()
-        self.musiclist.loading_message = "Nothing found yet"
-        self.musiclist.empty_message = "No matching results"
-        self.musiclist.connect('row-activated', self.row_activated)
-        self.panarea.add(self.musiclist)
-        vbox.pack_start(self.panarea, True, True, 0)
-
-
-        # Create selector for search mode
-        self.mode_selector = hildon.TouchSelector(text=True)
-
-        self.mode_selector.append_text("Artists")
-        self.mode_selector.append_text("Albums")
-        self.mode_selector.append_text("Tracks")
-        self.mode = hildon.PickerButton(gtk.HILDON_SIZE_FINGER_HEIGHT,
-                                        hildon.BUTTON_ARRANGEMENT_VERTICAL)
-        self.mode.set_title("Search for")
-        self.mode.set_selector(self.mode_selector)
-        self.mode_selector.connect("changed", self.mode_changed)
-        #vbox.pack_start(self.mode, False)
-        self.mode.set_active(1)
-
-
-        # Search box
-        hbox = gtk.HBox(False, 0)
-        self.entry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
-        self.entry.set_placeholder("Search")
-        self.entry.connect('activate', self.on_search)
-        btn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
-        btn.set_label(">>")
-        btn.connect('clicked', self.on_search)
-        hbox.pack_start(self.mode, False)
-        hbox.pack_start(self.entry, True, True, 0)
-        hbox.pack_start(btn, False)
-        vbox.pack_start(hbox, False)
-        self.add(vbox)
-
-        self.create_menu()
-
-    def create_menu(self):
-        def on_player(*args):
-            from playerwindow import open_playerwindow
-            open_playerwindow()
-        self.menu = hildon.AppMenu()
-        player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Open player")
-        player.connect("clicked", on_player)
-        self.menu.append(player)
-        self.menu.show_all()
-        self.set_app_menu(self.menu)
-
-    def on_destroy(self, wnd):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def mode_changed(self, selector, user_data):
-        pass
-        #current_selection = selector.get_current_text()
-
-    def on_search(self, w):
-        mode = self.mode.get_active()
-        txt = self.entry.get_text()
-        self.musiclist.set_loading(False)
-        self.musiclist.empty_message = "Searching..."
-        self.musiclist.get_model().clear()
-
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-
-        itemgen = None
-        if mode == 0:
-            itemgen = lambda: jamaendo.search_artists(query=txt)
-        elif mode == 1:
-            itemgen = lambda: jamaendo.search_albums(query=txt)
-        elif mode == 2:
-            itemgen = lambda: jamaendo.search_tracks(query=txt)
-        else:
-            return
-
-        self.fetcher = Fetcher(itemgen, self,
-                               on_item = self.on_add_result,
-                               on_ok = self.on_add_complete,
-                               on_fail = self.on_add_complete)
-        self.fetcher.start()
-        '''
-        try:
-            if mode == 0:
-                items = jamaendo.search_artists(query=txt)
-            elif mode == 1:
-                items = jamaendo.search_albums(query=txt)
-            elif mode == 2:
-                items = jamaendo.search_tracks(query=txt)
-
-            for item in items:
-                self.idmap[item.ID] = item
-
-            self.musiclist.add_items(items)
-        except jamaendo.JamaendoAPIException:
-            # nothing found, force redraw
-            self.musiclist.queue_draw()
-        '''
-
-    def on_add_result(self, wnd, item):
-        if wnd is self:
-            self.musiclist.add_items([item])
-            self.idmap[item.ID] = item
-
-    def on_add_complete(self, wnd, error=None):
-        if wnd is self:
-            self.musiclist.empty_message = "No matching results"
-            self.musiclist.queue_draw()
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def row_activated(self, treeview, path, view_column):
-        _id = self.musiclist.get_item_id(path)
-        item = self.idmap[_id]
-        self.open_item(item)
-
-    def open_item(self, item):
-        if isinstance(item, jamaendo.Album):
-            wnd = ShowAlbum(item)
-            wnd.show_all()
-        elif isinstance(item, jamaendo.Artist):
-            wnd = ShowArtist(item)
-            wnd.show_all()
-        elif isinstance(item, jamaendo.Track):
-            wnd = open_playerwindow()
-            wnd.play_tracks([item])
diff --git a/jamaui/settings.py b/jamaui/settings.py
deleted file mode 100644 (file)
index db1a6aa..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-import cPickle, os
-import logging
-import jamaendo
-import datetime
-
-from postoffice import postoffice
-
-VERSION = 1
-log = logging.getLogger(__name__)
-
-class Settings(object):
-    defaults = {
-        'volume':0.1,
-        'user':None,
-        'favorites':set([]), # local favorites - until we can sync back
-        'playlists':{},
-        }
-
-    def __init__(self):
-        self.__savename = "/tmp/jamaendo_uisettings"
-        for k,v in self.defaults.iteritems():
-            setattr(self, k, v)
-
-    def __setattr__(self, key, value):
-        object.__setattr__(self, key, value)
-        if key in self.defaults.keys():
-            postoffice.notify('settings-changed', key, value)
-
-    def set_filename(self, savename):
-        self.__savename = savename
-
-    def favorite(self, album):
-        self.favorites.add(('album', album.ID))
-        self.save()
-        postoffice.notify('settings-changed', 'favorites', self.favorites)
-
-    def get_playlist(self, playlist, get_track_objects=True):
-        entry = self.playlists.get(playlist)
-        if entry:
-            if get_track_objects:
-                return [jamaendo.Track(item['id'], item['data']) for item in entry]
-            return entry
-        return None
-
-    def add_to_playlist(self, playlist, track):
-        if isinstance(track, jamaendo.Track):
-            track = {'id':track.ID, 'data':track.get_data()}
-        assert(isinstance(track, dict))
-        lst = self.playlists.get(playlist)
-        if not lst:
-            lst = []
-            self.playlists[playlist] = lst
-        lst.append(track)
-        postoffice.notify('settings-changed', 'playlists', self.playlists)
-        log.debug("playlists is now %s", self.playlists)
-
-    def delete_playlist(self, name):
-        if name in self.playlists:
-            del self.playlists[name]
-            postoffice.notify('settings-changed', 'playlists', self.playlists)
-
-    def load(self):
-        if not os.path.isfile(self.__savename):
-            return
-        try:
-            f = open(self.__savename)
-            settings = cPickle.load(f)
-            f.close()
-
-            if settings['version'] > VERSION:
-                log.warning("Settings version %s higher than current version (%s)",
-                            settings['version'], VERSION)
-
-            for k in self.defaults.keys():
-                if k in settings:
-                    val = settings[k]
-                    if k == 'playlists' and not isinstance(val, dict):
-                        continue
-                    setattr(self, k, val)
-            log.debug("Loaded settings: %s", settings)
-        except Exception, e:
-            log.exception('failed to load settings')
-
-    def save(self):
-        try:
-            settings = {
-                'version':VERSION,
-                }
-            for k in self.defaults.keys():
-                settings[k] = getattr(self, k)
-            f = open(self.__savename, 'w')
-            cPickle.dump(settings, f)
-            f.close()
-            log.debug("Saved settings: %s", settings)
-        except Exception, e:
-            log.exception('failed to save settings')
-
-settings = Settings()
diff --git a/jamaui/showalbum.py b/jamaui/showalbum.py
deleted file mode 100644 (file)
index 7d0eed3..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-import gtk
-import cgi
-try:
-    import hildon
-except:
-    import helldon as hildon
-import jamaendo
-from player import Playlist
-from playerwindow import open_playerwindow
-from settings import settings
-from postoffice import postoffice
-import util
-import logging
-import thread
-import gobject
-from albumlist import TrackList
-from playlists import add_to_playlist
-from fetcher import Fetcher
-
-import webbrowser
-
-log = logging.getLogger(__name__)
-
-class ShowAlbum(hildon.StackableWindow):
-    def __init__(self, album):
-        hildon.StackableWindow.__init__(self)
-        self.set_title(album.artist_name)
-        self.album = album
-        self.fetcher = None
-
-        self.connect('destroy', self.on_destroy)
-
-        top_hbox = gtk.HBox()
-        vbox1 = gtk.VBox()
-        self.cover = gtk.Image()
-        tmp = util.find_resource('album.png')
-        if tmp:
-            self.cover.set_from_file(tmp)
-        self.bbox = gtk.HButtonBox()
-        self.bbox.set_property('layout-style', gtk.BUTTONBOX_SPREAD)
-        self.goto_artist = self.make_imagebutton('artist', self.on_goto_artist)
-        self.download = self.make_imagebutton('download', self.on_download)
-        self.favorite = self.make_imagebutton('favorite', self.on_favorite)
-        self.license = self.make_imagebutton('license', self.on_license)
-
-        vbox2 = gtk.VBox()
-        self.albumname = gtk.Label()
-        self.albumname.set_markup('<big>%s</big>'%(cgi.escape(album.name)))
-        self.trackarea = hildon.PannableArea()
-
-        self.tracks = TrackList(numbers=True)
-        self.tracks.connect('row-activated', self.row_activated)
-
-        self.tracklist = []
-        #self.tracklist = jamaendo.get_tracks(album.ID)
-        #for track in self.tracklist:
-        # self.tracks.add_track(track)
-
-        top_hbox.pack_start(vbox1, False)
-        top_hbox.pack_start(vbox2, True)
-        vbox1.pack_start(self.cover, True)
-        vbox1.pack_start(self.bbox, False)
-        self.bbox.add(self.goto_artist)
-        self.bbox.add(self.download)
-        self.bbox.add(self.favorite)
-        self.bbox.add(self.license)
-        vbox2.pack_start(self.albumname, False)
-        vbox2.pack_start(self.trackarea, True)
-        self.trackarea.add(self.tracks)
-
-        self.add(top_hbox)
-
-        postoffice.connect('album-cover', self, self.on_album_cover)
-        postoffice.notify('request-album-cover', self.album.ID, 300)
-
-        self.create_menu()
-        self.start_track_fetcher()
-
-    def create_menu(self):
-        def on_player(*args):
-            from playerwindow import open_playerwindow
-            open_playerwindow()
-        self.menu = hildon.AppMenu()
-        player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Open player")
-        player.connect("clicked", on_player)
-        self.menu.append(player)
-        player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Add to playlist")
-        player.connect("clicked", self.on_add_to_playlist)
-        self.menu.append(player)
-        self.menu.show_all()
-        self.set_app_menu(self.menu)
-
-    def on_destroy(self, wnd):
-        postoffice.disconnect('album-cover', self)
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def start_track_fetcher(self):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-        self.fetcher = Fetcher(lambda: jamaendo.get_tracks(self.album.ID), self,
-                               on_item = self.on_track_result,
-                               on_ok = self.on_track_complete,
-                               on_fail = self.on_track_complete)
-        self.fetcher.start()
-
-    def on_track_result(self, wnd, item):
-        if wnd is self:
-            self.tracklist.append(item)
-            self.tracks.add_track(item)
-
-    def on_track_complete(self, wnd, error=None):
-        if wnd is self:
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def on_album_cover(self, albumid, size, cover):
-        if albumid == self.album.ID and size == 300:
-            self.cover.set_from_file(cover)
-
-    def on_add_to_playlist(self, button, user_data=None):
-        add_to_playlist(self, self.tracklist)
-
-    def make_imagebutton(self, name, cb):
-        btn = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        btn.set_relief(gtk.RELIEF_NONE)
-        img = util.find_resource('icon_%s.png'%(name))
-        if img:
-            btn.set_image(gtk.image_new_from_file(img))
-        else:
-            btn.set_image(gtk.image_new_from_stock(gtk.STOCK_MEDIA_STOP, gtk.ICON_SIZE_SMALL_TOOLBAR))
-        btn.set_border_width(0)
-        btn.connect('clicked', cb)
-        return btn
-
-    def on_goto_artist(self, btn):
-        def threadfun(wnd, artist_id):
-            try:
-                artist = jamaendo.get_artist(artist_id)
-                def oncomplete(wnd, artist):
-                    wnd.open_item(artist)
-                    hildon.hildon_gtk_window_set_progress_indicator(wnd, 0)
-                gobject.idle_add(oncomplete, wnd, artist)
-            except:
-                def onfail(wnd):
-                    hildon.hildon_gtk_window_set_progress_indicator(wnd, 0)
-                gobject.idle_add(onfail, wnd)
-        hildon.hildon_gtk_window_set_progress_indicator(self, 1)
-        thread.start_new_thread(threadfun, (self, int(self.album.artist_id)))
-
-    def on_download(self, btn):
-        banner = hildon.hildon_banner_show_information(self, '', "Opening in web browser")
-        banner.set_timeout(2000)
-        url = self.album.torrent_url()
-        webbrowser.open_new(url)
-
-    def on_favorite(self, btn):
-        settings.favorite(self.album)
-        banner = hildon.hildon_banner_show_information(self, '', "Favorite added")
-        banner.set_timeout(2000)
-
-
-    def on_license(self, btn):
-        banner = hildon.hildon_banner_show_information(self, '', "Opening in web browser")
-        banner.set_timeout(2000)
-        url = self.album.license_url
-        webbrowser.open_new(url)
-
-    def on_play(self, btn):
-        self.open_item(self.album)
-
-    def row_activated(self, treeview, path, view_column):
-        # TODO: wait for all tracks to load
-        _id = self.tracks.get_track_id(path)
-        playlist = Playlist(self.tracklist)
-        playlist.jump_to(_id)
-        wnd = open_playerwindow()
-        wnd.play_tracks(playlist)
-
-    def open_item(self, item):
-        if isinstance(item, jamaendo.Album):
-            tracks = jamaendo.get_tracks(item.ID)
-            if tracks:
-                wnd = open_playerwindow()
-                wnd.play_tracks(tracks)
-        elif isinstance(item, jamaendo.Artist):
-            from showartist import ShowArtist
-            wnd = ShowArtist(item)
-            wnd.show_all()
-        elif isinstance(item, jamaendo.Track):
-            wnd = open_playerwindow()
-            wnd.play_tracks([item])
diff --git a/jamaui/showartist.py b/jamaui/showartist.py
deleted file mode 100644 (file)
index 9b49b4e..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-import os
-import gtk
-try:
-    import hildon
-except:
-    import helldon as hildon
-import jamaendo
-from playerwindow import open_playerwindow
-from albumlist import AlbumList
-from postoffice import postoffice
-import util
-import gobject
-from playlists import add_to_playlist, show_banner
-from fetcher import Fetcher
-
-import logging
-
-log = logging.getLogger(__name__)
-
-class ShowArtist(hildon.StackableWindow):
-    ICON_SIZE = 200
-
-    def __init__(self, artist):
-        hildon.StackableWindow.__init__(self)
-        self.connect('destroy', self.on_destroy)
-        self.set_title(artist.name)
-        self.artist = artist
-
-        self.connect('destroy', self.on_destroy)
-        self.fetcher = None
-
-        top_hbox = gtk.HBox()
-        self.image = gtk.Image()
-        self.default_pixbuf = util.find_resource('album.png')
-        self.image.set_from_pixbuf(self.get_default_pixbuf())
-
-        self.panarea = hildon.PannableArea()
-        vbox = gtk.VBox(False, 0)
-
-        self.albums = AlbumList()
-        self.albums.loading_message = "No albums"
-        self.albums.empty_message = "No albums"
-        self.albums.show_artist(False)
-        self.albums.connect('row-activated', self.row_activated)
-
-        self.panarea.add(self.albums)
-        vbox.pack_start(self.panarea, True, True, 0)
-        #self.add(vbox)
-
-        #imgalign = gtk.Alignment(xalign=0.2, yalign=0.4, xscale=1.0)
-        #alignment.add(bbox)
-
-        self.image.set_alignment(0.5, 0.0)
-
-        top_hbox.pack_start(self.image, False)
-        top_hbox.pack_start(vbox)
-
-        self.add(top_hbox)
-
-        self.albumlist = []
-
-        postoffice.connect('images', self, self.on_images)
-
-        if self.artist.image:
-            postoffice.notify('request-images', [self.artist.image])
-
-        self.create_menu()
-        self.start_album_fetcher()
-
-    def on_destroy(self, wnd):
-        postoffice.disconnect('images', self)
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def start_album_fetcher(self):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-        self.fetcher = Fetcher(lambda: jamaendo.get_albums(self.artist.ID), self,
-                               on_item = self.on_album_result,
-                               on_ok = self.on_album_complete,
-                               on_fail = self.on_album_complete)
-        self.fetcher.start()
-
-    def on_album_result(self, wnd, item):
-        if wnd is self:
-            self.albums.add_album(item)
-            self.albumlist.append(item)
-
-    def on_album_complete(self, wnd, error=None):
-        if wnd is self:
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def create_menu(self):
-        def on_player(*args):
-            from playerwindow import open_playerwindow
-            open_playerwindow()
-        self.menu = hildon.AppMenu()
-        player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Open player")
-        player.connect("clicked", on_player)
-        self.menu.append(player)
-        player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Add to playlist")
-        player.connect("clicked", self.on_add_to_playlist)
-        self.menu.append(player)
-        self.menu.show_all()
-        self.set_app_menu(self.menu)
-
-    def start_addpl_fetcher(self):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-        def fetchgen():
-            for album in self.albumlist:
-                yield jamaendo.get_tracks(album.ID)
-            raise StopIteration
-        self.fetcher = Fetcher(fetchgen, self,
-                               on_item = self.on_addpl_result,
-                               on_ok = self.on_addpl_complete,
-                               on_fail = self.on_addpl_complete)
-        self.fetcher.tracklist = []
-        self.fetcher.pdlg = gtk.Dialog("Fetching album tracks", self)
-        self.fetcher.pbar = gtk.ProgressBar()
-        self.fetcher.pbar.set_fraction(0)
-        self.fetcher.pdlg.vbox.add(self.fetcher.pbar)
-        self.fetcher.pdlg.show_all()
-        self.fetcher.ppos = 1
-        self.fetcher.start()
-
-    def on_addpl_result(self, wnd, items):
-        if wnd is self:
-            self.fetcher.tracklist.extend(items)
-            self.fetcher.ppos += 1
-            self.fetcher.pbar.set_fraction(float(self.fetcher.ppos)/float(len(self.albumlist)+1))
-
-    def on_addpl_complete(self, wnd, error=None):
-        if wnd is self:
-            self.fetcher.stop()
-            self.fetcher.pdlg.destroy()
-            if self.fetcher.tracklist:
-                add_to_playlist(self, self.fetcher.tracklist)
-            self.fetcher = None
-
-
-    def on_add_to_playlist(self, button, user_data=None):
-        if self.albumlist:
-            self.start_addpl_fetcher()
-        else:
-            show_banner(self, "Error when opening track list")
-
-    def get_pixbuf(self, img):
-        try:
-            return gtk.gdk.pixbuf_new_from_file_at_size(img,
-                                                        self.ICON_SIZE,
-                                                        self.ICON_SIZE)
-        except gobject.GError:
-            log.error("Broken image in cache: %s", img)
-            try:
-                os.unlink(img)
-            except OSError, e:
-                log.warning("Failed to unlink broken image.")
-            if img != self.default_pixbuf:
-                return self.get_default_pixbuf()
-            else:
-                return None
-
-    def get_default_pixbuf(self):
-        if self.default_pixbuf:
-            return self.get_pixbuf(self.default_pixbuf)
-
-    def on_images(self, images):
-        for url, image in images:
-            if url == self.artist.image:
-                pb = self.get_pixbuf(image)
-                if pb:
-                    self.image.set_from_pixbuf(pb)
-
-    def start_albumplay_fetcher(self, ID):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-        def albumgen():
-            yield jamaendo.get_album(ID)
-            raise StopIteration
-        self.fetcher = Fetcher(albumgen, self,
-                               on_item = self.on_albumplay_result,
-                               on_ok = self.on_albumplay_complete,
-                               on_fail = self.on_albumplay_complete)
-        self.fetcher.album = None
-        self.fetcher.start()
-
-    def on_albumplay_result(self, wnd, item):
-        if wnd is self:
-            if isinstance(item, list):
-                self.fetcher.album = item[0]
-            else:
-                self.fetcher.album = item
-
-    def on_albumplay_complete(self, wnd, error=None):
-        if wnd is self:
-            self.fetcher.stop()
-            if self.fetcher.album:
-                self.open_item(self.fetcher.album)
-            self.fetcher = None
-
-    def row_activated(self, treeview, path, view_column):
-        _id = self.albums.get_album_id(path)
-        self.start_albumplay_fetcher(_id)
-
-    def open_item(self, item):
-        if isinstance(item, jamaendo.Album):
-            from showalbum import ShowAlbum
-            wnd = ShowAlbum(item)
-            wnd.show_all()
-        elif isinstance(item, jamaendo.Artist):
-            wnd = ShowArtist(item)
-            wnd.show_all()
-        elif isinstance(item, jamaendo.Track):
-            wnd = open_playerwindow()
-            wnd.play_tracks([item])
diff --git a/jamaui/showplaylist.py b/jamaui/showplaylist.py
deleted file mode 100644 (file)
index ed43c5a..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-import gtk
-import cgi
-try:
-    import hildon
-except:
-    import helldon as hildon
-import jamaendo
-from player import Playlist
-from playerwindow import open_playerwindow
-from settings import settings
-from postoffice import postoffice
-import util
-import logging
-import thread
-import gobject
-from albumlist import TrackList
-from fetcher import Fetcher
-
-import webbrowser
-
-log = logging.getLogger(__name__)
-
-class ShowPlaylist(hildon.StackableWindow):
-    def __init__(self, plname, playlist):
-        hildon.StackableWindow.__init__(self)
-        self.set_title(plname)
-        self.playlist_name = plname
-        self.playlist = playlist
-        self.fetcher = None
-
-        self.connect('destroy', self.on_destroy)
-
-        top_hbox = gtk.HBox()
-        vbox1 = gtk.VBox()
-        self.cover = gtk.Image()
-        tmp = util.find_resource('album.png')
-        if tmp:
-            self.cover.set_from_file(tmp)
-
-        vbox2 = gtk.VBox()
-        self.trackarea = hildon.PannableArea()
-        self.tracks = TrackList(numbers=True)
-        self.tracks.connect('row-activated', self.row_activated)
-        self.tracklist = []
-
-        top_hbox.pack_start(vbox1, False)
-        top_hbox.pack_start(vbox2, True)
-        vbox1.pack_start(self.cover, True)
-        vbox2.pack_start(self.trackarea, True)
-        self.trackarea.add(self.tracks)
-
-        self.add(top_hbox)
-
-        postoffice.connect('album-cover', self, self.on_album_cover)
-        if len(self.playlist) > 0:
-            postoffice.notify('request-album-cover', int(self.playlist[0].album_id), 300)
-
-        self.create_menu()
-        self.start_track_fetcher()
-
-    def create_menu(self):
-        def on_player(*args):
-            from playerwindow import open_playerwindow
-            open_playerwindow()
-        self.menu = hildon.AppMenu()
-        player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Open player")
-        player.connect("clicked", on_player)
-        self.menu.append(player)
-        player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Delete playlist")
-        player.connect("clicked", self.on_delete_pl)
-        self.menu.append(player)
-        self.menu.show_all()
-        self.set_app_menu(self.menu)
-
-    def on_destroy(self, wnd):
-        postoffice.disconnect('album-cover', self)
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def start_track_fetcher(self):
-        if self.fetcher:
-            self.fetcher.stop()
-            self.fetcher = None
-        self.fetcher = Fetcher(lambda: self.playlist, self,
-                               on_item = self.on_track_result,
-                               on_ok = self.on_track_complete,
-                               on_fail = self.on_track_complete)
-        self.fetcher.start()
-
-    def on_track_result(self, wnd, item):
-        if wnd is self:
-            self.tracklist.append(item)
-            self.tracks.add_track(item)
-
-    def on_track_complete(self, wnd, error=None):
-        if wnd is self:
-            self.fetcher.stop()
-            self.fetcher = None
-
-    def on_delete_pl(self, btn):
-        note = hildon.hildon_note_new_confirmation(self, "Do you want to delete '%s' ?" % (self.playlist_name))
-        response = note.run()
-        note.destroy()
-        print response
-        if response == gtk.RESPONSE_OK:
-            settings.delete_playlist(self.playlist_name)
-            settings.save()
-            self.destroy()
-
-    def on_album_cover(self, albumid, size, cover):
-        if size == 300:
-            self.cover.set_from_file(cover)
-
-    def on_play(self, btn):
-        self.open_item(self.album)
-
-    def row_activated(self, treeview, path, view_column):
-        # TODO: wait for all tracks to load
-        _id = self.tracks.get_track_id(path)
-        pl = Playlist(self.playlist)
-        pl.jump_to(_id)
-        wnd = open_playerwindow()
-        wnd.show_all()
-        wnd.play_tracks(pl)
-
-    def open_item(self, item):
-        if isinstance(item, jamaendo.Album):
-            tracks = jamaendo.get_tracks(item.ID)
-            if tracks:
-                wnd = open_playerwindow()
-                wnd.play_tracks(tracks)
-        elif isinstance(item, jamaendo.Artist):
-            from showartist import ShowArtist
-            wnd = ShowArtist(item)
-            wnd.show_all()
-        elif isinstance(item, jamaendo.Track):
-            wnd = open_playerwindow()
-            wnd.play_tracks([item])
diff --git a/jamaui/songposition.py b/jamaui/songposition.py
deleted file mode 100644 (file)
index 67e7a12..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-import logging
-import gtk
-import cairo
-
-log = logging.getLogger(__name__)
-
-# shows the current song position (looking a bit nicer than a default widget, hopefully)
-class SongPosition(gtk.DrawingArea):
-    WIDTH = 32.0
-    HEIGHT = 16.0
-
-    def __init__(self):
-        gtk.DrawingArea.__init__(self)
-        self.connect('expose-event', self.on_expose)
-        self.set_size_request(int(self.WIDTH), int(self.HEIGHT))
-        self.pos = 0.0
-
-        orange0 = self.hex_to_flt(0xec, 0xac, 0x1f)
-        orange1 = self.hex_to_flt(0xea, 0x86, 0x1d)
-        orange2 = self.hex_to_flt(0xda, 0x76, 0x0d)
-        orange3 = self.hex_to_flt(0xd0, 0x70, 0x00)
-        purple0 = self.hex_to_flt(0x81, 0x3e, 0x82)
-        purple1 = self.hex_to_flt(0x56, 0x2d, 0x5a)
-
-        lightclr = cairo.LinearGradient(0.0, 0.0, 0.0, self.HEIGHT)
-        lightclr.add_color_stop_rgb(0.0, 1.0, 1.0, 1.0)
-        lightclr.add_color_stop_rgb(0.1, *orange0)
-        lightclr.add_color_stop_rgb(0.5, *orange1)
-        lightclr.add_color_stop_rgb(0.5, *orange2)
-        lightclr.add_color_stop_rgb(1.0, *orange3)
-
-        darkclr = cairo.LinearGradient(0.0, 0.0, 0.0, self.HEIGHT)
-        darkclr.add_color_stop_rgb(0.0, 0.5, 0.5, 0.5)
-        darkclr.add_color_stop_rgb(0.5, 0.0, 0.0, 0.0)
-        darkclr.add_color_stop_rgb(1.0, 0.25, 0.25, 0.25)
-
-        markerclr = cairo.LinearGradient(0.0, 0.0, 0.0, self.HEIGHT)
-        markerclr.add_color_stop_rgb(0.0, 1.0, 1.0, 1.0)
-        markerclr.add_color_stop_rgb(0.5, 1.0, 1.0, 1.0)
-        markerclr.add_color_stop_rgb(1.0, 1.0, 1.0, 1.0)
-
-        self.lightclr = lightclr
-        self.darkclr = darkclr
-        self.markerclr = markerclr
-
-    def on_expose(self, widget, event):
-        context = self.window.cairo_create()
-        context.rectangle(event.area.x, event.area.y,
-            event.area.width, event.area.height)
-        context.clip()
-        self.draw(context)
-        return True
-
-    #ecac1f - light orange
-    #ea861d - dark orange
-
-    #813e82 - light purple
-    #562d5a - dark purple
-
-    def hex_to_flt(self, r, g, b):
-        return float(r)/255.0, float(g)/255.0, float(b)/255.0
-
-    def draw(self, context):
-        rect = self.get_allocation()
-
-
-        #context.set_source_rgb(1.0, 0.5, 0.0)
-        lowx = rect.width*self.pos
-        hix = rect.width*self.pos
-
-        if lowx < 0.0:
-            lowx = 0.0
-            hix = self.WIDTH
-        elif hix > rect.width:
-            lowx = rect.width - self.WIDTH
-            hix = rect.width
-
-        context.rectangle(0, 0, rect.width, rect.height)
-        context.set_source(self.darkclr)
-        context.fill()
-
-        if lowx > 0.01:
-            context.rectangle(0, 0, lowx, rect.height)
-            context.set_source(self.lightclr)
-            context.fill()
-
-        context.rectangle(0, 0, rect.width, rect.height)
-        context.set_source_rgb(0.3, 0.3, 0.3)
-        context.stroke()
-
-        #context.rectangle(lowx, 0, self.WIDTH, rect.height)
-        #context.set_source(self.markerclr)
-        #context.fill()
-
-    def set_position(self, pos):
-        if pos < 0.0:
-            pos = 0.0
-        elif pos > 1.0:
-            pos = 1.0
-        self.pos = pos
-        self.invalidate()
-
-    def invalidate(self):
-        self.queue_draw()
diff --git a/jamaui/ui.py b/jamaui/ui.py
deleted file mode 100644 (file)
index ac1c6a8..0000000
+++ /dev/null
@@ -1,297 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-# Jamaendo jamendo.com API wrapper licensed under the New BSD License;
-# see module for details.
-#
-
-import os, sys
-import gtk
-import gobject
-import util
-import logging
-from settings import settings
-
-import ossohelper
-
-gobject.threads_init()
-
-log = logging.getLogger(__name__)
-
-VERSION = '0.2'
-
-try:
-    import hildon
-except:
-    if util.platform == 'maemo':
-        log.critical('Using GTK widgets, install "python2.5-hildon" '
-            'for this to work properly.')
-    else:
-        log.critical('This ui (probably) only works in maemo')
-        import helldon as hildon
-
-from dbus.mainloop.glib import DBusGMainLoop
-
-DBusGMainLoop(set_as_default=True)
-
-import jamaendo
-
-from postoffice import postoffice
-from playerwindow import open_playerwindow
-from search import SearchWindow
-from featured import FeaturedWindow
-from radios import RadiosWindow
-from favorites import FavoritesWindow
-from playlists import PlaylistsWindow
-from listbox import ButtonListDialog
-
-class Jamaui(object):
-    def __init__(self):
-        self.app = None
-        self.menu = None
-        self.window = None
-
-    def create_window(self):
-        log.debug("Creating main window...")
-        self.app = hildon.Program()
-        self.window = hildon.StackableWindow()
-        self.app.add_window(self.window)
-
-        self.window.set_title("jamaendo")
-
-        self.window.connect("destroy", self.destroy)
-
-        self.CONFDIR = os.path.expanduser('~/MyDocs/.jamaendo')
-        jamaendo.set_cache_dir(self.CONFDIR)
-        settings.set_filename(os.path.join(self.CONFDIR, 'ui_settings'))
-        settings.load()
-
-        postoffice.connect('request-album-cover', self, self.on_request_cover)
-        postoffice.connect('request-images', self, self.on_request_images)
-        log.debug("Created main window.")
-
-    def create_menu(self):
-        self.menu = hildon.AppMenu()
-
-        player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Open player")
-        player.connect("clicked", self.on_player)
-        self.menu.append(player)
-
-        player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Favorites")
-        player.connect("clicked", self.on_favorites)
-        self.menu.append(player)
-
-        player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Playlists")
-        player.connect("clicked", self.on_playlists)
-        self.menu.append(player)
-
-        player = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        player.set_label("Settings")
-        player.connect("clicked", self.on_settings)
-        self.menu.append(player)
-
-        menu_about = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
-        menu_about.set_label("About")
-        menu_about.connect("clicked", self.show_about, self.window)
-        self.menu.append(menu_about)
-        gtk.about_dialog_set_url_hook(self.open_link, None)
-
-        self.menu.show_all()
-        self.window.set_app_menu(self.menu)
-
-
-    def setup_widgets(self):
-        bgimg = util.find_resource('bg.png')
-        if bgimg:
-            background, mask = gtk.gdk.pixbuf_new_from_file(bgimg).render_pixmap_and_mask()
-            self.window.realize()
-            self.window.window.set_back_pixmap(background, False)
-
-        bbox = gtk.HButtonBox()
-        alignment = gtk.Alignment(xalign=0.2, yalign=0.4, xscale=1.0)
-        alignment.add(bbox)
-        bbox.set_property('layout-style', gtk.BUTTONBOX_SPREAD)
-        self.bbox = bbox
-        self.window.add(alignment)
-
-        self.add_mainscreen_button("Featured", "Most listened to", self.on_featured)
-        self.add_mainscreen_button("Radios", "The best in free music", self.on_radios)
-        self.add_mainscreen_button("Search", "Search for artists/albums", self.on_search)
-
-        self.window.show_all()
-
-    def add_mainscreen_button(self, title, subtitle, callback):
-        btn = hildon.Button(gtk.HILDON_SIZE_THUMB_HEIGHT,
-                            hildon.BUTTON_ARRANGEMENT_VERTICAL)
-        btn.set_text(title, subtitle)
-        btn.set_property('width-request', 225)
-        btn.connect('clicked', callback)
-        self.bbox.add(btn)
-
-    def on_request_cover(self, albumid, size):
-        jamaendo.get_album_cover_async(self.got_album_cover, int(albumid), size)
-
-    def on_request_images(self, urls):
-        jamaendo.get_images_async(self.got_images, urls)
-
-    def got_album_cover(self, albumid, size, cover):
-        gtk.gdk.threads_enter()
-        postoffice.notify('album-cover', albumid, size, cover)
-        gtk.gdk.threads_leave()
-
-    def got_images(self, images):
-        gtk.gdk.threads_enter()
-        postoffice.notify('images', images)
-        gtk.gdk.threads_leave()
-
-    def destroy(self, widget):
-        postoffice.disconnect(['request-album-cover', 'request-images'], self)
-        settings.save()
-        from player import the_player
-        if the_player:
-            the_player.stop()
-        gtk.main_quit()
-
-    def show_about(self, w, win):
-        dialog = gtk.AboutDialog()
-        dialog.set_program_name("jamaendo")
-        dialog.set_website("http://jamaendo.garage.maemo.org/")
-        dialog.set_website_label("http://jamaendo.garage.maemo.org/")
-        dialog.set_version(VERSION)
-        dialog.set_license("""Copyright (c) 2010, Kristoffer Gronlund
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-     * Redistributions of source code must retain the above copyright
-       notice, this list of conditions and the following disclaimer.
-     * Redistributions in binary form must reproduce the above copyright
-       notice, this list of conditions and the following disclaimer in the
-       documentation and/or other materials provided with the distribution.
-     * Neither the name of Jamaendo nor the
-       names of its contributors may be used to endorse or promote products
-       derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-""")
-        dialog.set_authors(("Kristoffer Gronlund <kristoffer.gronlund@purplescout.se>",
-                            "Based on Panucci, written by Thomas Perl <thpinfo.com>",
-                            "Icons by Joseph Wain <http://glyphish.com/>"))
-        dialog.set_comments("""Jamaendo plays music from the music catalog of JAMENDO.
-
-JAMENDO is an online platform that distributes musical works under Creative Commons licenses.""")
-        gtk.about_dialog_set_email_hook(self.open_link, dialog)
-        gtk.about_dialog_set_url_hook(self.open_link, dialog)
-        dialog.connect( 'response', lambda dlg, response: dlg.destroy())
-        for parent in dialog.vbox.get_children():
-            for child in parent.get_children():
-                if isinstance(child, gtk.Label):
-                    child.set_selectable(False)
-                    child.set_alignment(0.0, 0.5)
-        dialog.run()
-        dialog.destroy()
-
-    def open_link(self, d, url, data):
-        import webbrowser
-        webbrowser.open_new(url)
-
-    def on_featured(self, button):
-        dialog = ButtonListDialog('Featured', self.window)
-        def fn(btn, feature):
-            self.featuredwnd = FeaturedWindow(feature)
-            self.featuredwnd.show_all()
-            dialog.response(gtk.RESPONSE_OK)
-        for feature, _ in FeaturedWindow.features:
-            dialog.add_button(feature, fn, feature)
-        dialog.show_all()
-        dialog.run()
-        dialog.destroy()
-
-    def on_radios(self, button):
-        self.radioswnd = RadiosWindow()
-        self.radioswnd.show_all()
-
-    def on_search(self, button):
-        self.searchwnd = SearchWindow()
-        self.searchwnd.show_all()
-
-    def on_playlists(self, button):
-        self.playlistswnd = PlaylistsWindow()
-        self.playlistswnd.show_all()
-
-    def on_settings(self, button):
-        dialog = gtk.Dialog()
-        dialog.set_title("Settings")
-        dialog.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK )
-        vbox = dialog.vbox
-        hboxinner = gtk.HBox()
-        hboxinner.pack_start(gtk.Label("Username:"), False, False, 0)
-        entry = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
-        entry.set_placeholder("jamendo.com username")
-        if settings.user:
-            entry.set_text(settings.user)
-        hboxinner.pack_start(entry, True, True, 0)
-        vbox.pack_start(hboxinner, True, True, 0)
-        dialog.show_all()
-        result = dialog.run()
-        val = entry.get_text()
-        dialog.destroy()
-        if val and result == gtk.RESPONSE_OK:
-            settings.user = val
-            settings.save()
-
-
-    def on_favorites(self, button):
-        self.favoriteswnd = FavoritesWindow()
-        self.favoriteswnd.show_all()
-
-    def on_player(self, button):
-        open_playerwindow()
-
-    def run(self):
-        ossohelper.application_init('org.jamaendo', '0.1')
-        self.create_window()
-        self.create_menu()
-        self.setup_widgets()
-        self.window.show_all()
-        gtk.gdk.threads_enter()
-        gtk.main()
-        gtk.gdk.threads_leave()
-        ossohelper.application_exit()
-
-if __name__=="__main__":
-    ui = Jamaui()
-    ui.run()
-
diff --git a/jamaui/util.py b/jamaui/util.py
deleted file mode 100644 (file)
index 3c82f17..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env python
-#
-# This file is part of Jamaendo.
-# Copyright (c) 2010 Kristoffer Gronlund
-#
-# Jamaendo 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 3 of the License, or
-# (at your option) any later version.
-#
-# Jamaendo 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 Jamaendo.  If not, see <http://www.gnu.org/licenses/>.
-#
-# Player code heavily based on http://thpinfo.com/2008/panucci/:
-#  A resuming media player for Podcasts and Audiobooks
-#  Copyright (c) 2008-05-26 Thomas Perl <thpinfo.com>
-#  (based on http://pygstdocs.berlios.de/pygst-tutorial/seeking.html)
-#
-import os
-import simplejson
-
-def string_in_file( filepath, string ):
-    try:
-        f = open( filepath, 'r' )
-        found = f.read().find( string ) != -1
-        f.close()
-    except:
-        found = False
-
-    return found
-
-def get_platform():
-    if ( os.path.exists('/etc/osso_software_version') or
-         os.path.exists('/proc/component_version') or
-         string_in_file('/etc/issue', 'maemo') ):
-        return 'maemo'
-    else:
-        return 'linux'
-
-platform = get_platform()
-
-#def jsonprint(x):
-#    print simplejson.dumps(x, sort_keys=True, indent=4)
-
-def find_resource(name):
-    if os.path.isfile(os.path.join('data', name)):
-        return os.path.join('data', name)
-    elif os.path.isfile(os.path.join('/opt/jamaendo', name)):
-        return os.path.join('/opt/jamaendo', name)
-    elif os.path.isfile(os.path.join('/usr/share/jamaendo', name)):
-        return os.path.join('/usr/share/jamaendo', name)
-    else:
-        return None
diff --git a/scripts/jamaendo b/scripts/jamaendo
deleted file mode 100755 (executable)
index debdf19..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env python2.5
-
-# Dependencies:
-
-# python-simplejson
-# python-lxml
-# python-gst0.10
-
-
-# debugging hack - add . to path
-import os, sys
-local_module_dir = os.path.join(os.path.dirname(sys.argv[0]), '..')
-if os.path.isdir(local_module_dir):
-    sys.path = [local_module_dir] + sys.path
-
-def main():
-    from jamaui.ui import Jamaui
-    player = Jamaui()
-
-    player.run()
-
-if __name__=="__main__":
-    main()
diff --git a/scripts/player b/scripts/player
deleted file mode 100755 (executable)
index 802bd4d..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env python
-
-# debugging hack - add . to path
-import os, sys
-local_module_dir = os.path.join(os.path.dirname(sys.argv[0]), '..')
-if os.path.isdir(local_module_dir):
-    sys.path.append(local_module_dir)
-
-def main():
-    #from jamaui.ui import JamaUI
-    #player = JamaUI()
-    from jamaui.console import Console
-    player = Console()
-
-    player.run()
-
-if __name__=="__main__":
-    main()
diff --git a/setup.py b/setup.py
deleted file mode 100644 (file)
index 0782d26..0000000
--- a/setup.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env python\r
-#\r
-# This file is part of Jamaendo.\r
-# Copyright (c) 2010, Kristoffer Gronlund\r
-# All rights reserved.\r
-#\r
-# Redistribution and use in source and binary forms, with or without\r
-# modification, are permitted provided that the following conditions are met:\r
-#     * Redistributions of source code must retain the above copyright\r
-#       notice, this list of conditions and the following disclaimer.\r
-#     * Redistributions in binary form must reproduce the above copyright\r
-#       notice, this list of conditions and the following disclaimer in the\r
-#       documentation and/or other materials provided with the distribution.\r
-#     * Neither the name of Jamaendo nor the\r
-#       names of its contributors may be used to endorse or promote products\r
-#       derived from this software without specific prior written permission.\r
-#\r
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND\r
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
-# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY\r
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\r
-# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
-\r
-from distutils.core import setup\r
-from glob import glob\r
-\r
-data_files = [\r
-    ('/opt/jamaendo', glob('data/icon_*.png') + ['data/bg.png', 'data/album.png']),\r
-    ('share/applications/hildon', ['data/jamaendo.desktop']),\r
-    ('share/icons/hicolor/26x26/apps', ['data/26x26/jamaendo.png']),\r
-    ('share/icons/hicolor/40x40/apps', ['data/40x40/jamaendo.png']),\r
-    ('share/icons/hicolor/scalable/apps', ['data/64x64/jamaendo.png']),\r
-]\r
-\r
-# search for translations and repare to install\r
-#translation_files = []\r
-#for mofile in glob('data/locale/*/LC_MESSAGES/jamaendo.mo'):\r
-#    modir = os.path.dirname(mofile).replace('data', 'share')\r
-#    translation_files.append((modir, [mofile]))\r
-\r
-setup(\r
-    name = "jamaendo",\r
-    version = '0.2',\r
-    author = "Kristoffer Gronlund",\r
-    author_email = "kristoffer.gronlund@purplescout.se",\r
-    url = "http://jamaendo.garage.maemo.org/",\r
-    packages = ['jamaendo', 'jamaui'],\r
-    scripts = ['scripts/jamaendo'],\r
-    data_files = data_files# + translation_files,\r
-    )\r
-\r
diff --git a/tests/__init__.py b/tests/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/testicle b/tests/testicle
deleted file mode 100755 (executable)
index 2a62fa5..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/bin/env python
-# runs tons of test queries against the API,
-# sleeping between each and printing the result
-
-# debugging hack - add . to path
-import os, sys
-local_module_dir = os.path.join(os.path.dirname(sys.argv[0]), '..')
-if os.path.isdir(local_module_dir):
-    sys.path.append(os.path.abspath(local_module_dir))
-
-import time
-
-import jamaendo as api2
-
-class Tests(object):
-    def XXXtestSearchArtists(self):
-        result = api2.search_artists('porn')
-        print "Result:", result
-        print "Cache:", api2._artists
-
-    def XXXtestSearchAlbums(self):
-        result = api2.search_albums('porn')
-        print "Result:", result
-        print "Cache:", api2._albums
-
-    def XXXtestSearchTracks(self):
-        result = api2.search_tracks('porn')
-        print "Result:", result
-        print "Cache:", api2._tracks
-
-    def XXXtestAlbumsOfTheWeek(self):
-        result = api2.albums_of_the_week()
-        print "Result:", result
-        print "Cache:", api2._albums
-
-    def XXXtestNewReleases(self):
-        result = api2.new_releases()
-        print "Result:", result
-        print "Cache:", api2._tracks
-
-    def XXXtestTracksOfTheWeek(self):
-        result = api2.tracks_of_the_week()
-        print "Result:", result
-        print "Cache:", api2._tracks
-
-    def XXXtestStarredRadios(self):
-        result = api2.starred_radios()
-        print "Result:", result
-
-    def XXXtestGetRadio283(self):
-        result = api2.get_radio(283)
-        print "Result:", result
-
-    def testGetRadioTracks283(self):
-        result = api2.get_radio_tracks(283)
-        print "Result:", result
-
-    def XXXtestGetArtist91(self):
-        result = api2.get_artist(91)
-        print "Result:", result
-
-    def XXXtestGetAlbum27865(self):
-        result = api2.get_album(27865)
-        print "Result:", result
-
-    def XXXtestGetTrack353341(self):
-        result = api2.get_track(353341)
-        print "Result:", result
-
-    def XXXtestGetTracks27865(self):
-        result = api2.get_tracks(27865)
-        print "Result:", result
-
-    def XXXtestGetAlbums91(self):
-        result = api2.get_albums(91)
-        print "Result:", result
-
-    def XXXtestFavoriteAlbumsKegie(self):
-        result = api2.favorite_albums('kegie')
-        print "Result:", result
-
-    def XXXtestGetAlbumCover27865(self):
-        result = api2.get_album_cover(27865)
-        print "Result:", result
-
-    def XXXtestGetAlbumCoverAsync27865(self):
-        self.got_cover = False
-        def gotit(cover):
-            print "Got:", cover
-            self.got_cover = True
-        api2.get_album_cover_async(gotit, 27865)
-        while not self.got_cover:
-            print "Waiting for cover..."
-            time.sleep(4)
-
-import traceback
-
-def main():
-    for name in Tests.__dict__.keys():
-        if name.startswith('test'):
-            print "Running %s" % (name)
-            try:
-                t = Tests()
-                getattr(t, name)()
-            except Exception, e:
-                traceback.print_exc()
-            print "Waiting..."
-            time.sleep(10)
-
-if __name__=="__main__":
-    main()
diff --git a/welcome b/welcome
deleted file mode 100644 (file)
index b6cbc2d..0000000
--- a/welcome
+++ /dev/null
@@ -1 +0,0 @@
-welcome
\ No newline at end of file