pax_global_header00006660000000000000000000000064122051514750014514gustar00rootroot0000000000000052 comment=2fba6182e3cabe5fc4002473b16ee47e62e95683 emms-3.0+20130821+1.git2fba618.orig/000077500000000000000000000000001220515147500160625ustar00rootroot00000000000000emms-3.0+20130821+1.git2fba618.orig/AUTHORS000066400000000000000000000017311220515147500171340ustar00rootroot00000000000000This file lists all people who contributed more than a few lines to emms. This is necessary to keep track of people who have copyright claims on sources, so please don't be too humble and add yourself. Damien Elmes Daniel Brockman Jean-Philippe Theberge Jorgen Schaefer Lawrence Mitchell Lucas Bonnet Mario Domgörgen Mario Lang Martin Schoenmakers Matthew Kennedy Michael Olson Tassilo Horn Trent Buck Ulrik Jensen William Xu Ye Wenbin Yoni Rabkin Katzenell Jesse Weinstein David Engster Bram van der Kroef ;; Local variables: ;; coding: utf-8 ;; End: emms-3.0+20130821+1.git2fba618.orig/COPYING000066400000000000000000001045131220515147500171210ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 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 . 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: Copyright (C) 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 . 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 . emms-3.0+20130821+1.git2fba618.orig/FAQ000066400000000000000000000016061220515147500164170ustar00rootroot00000000000000Frequently Asked Questions about emms. Please read this before submitting a bug report. Q: I seem unable to play files with accents in them. Why? A: Emacs doesn't know the coding system of your files, and it apparently decodes them the wrong way. Set `default-file-name-coding-system' to the correct encoding of your file names. It might even work to set it to 'undecided and let Emacs guess. Q: Emms skips some songs in the playlist for no apparent reason. When I select them manually, everything works. Why? A: Increase `emms-player-delay' until it works. The problem is that emms is told by Emacs that a player finished, so it starts a new one. But in reality, the player has not yet freed the audio device, so the next player gets an error when trying to play. The best way to fix this is by using ALSA or other sound systems which allow concurrent access. emms-3.0+20130821+1.git2fba618.orig/Makefile000066400000000000000000000043561220515147500175320ustar00rootroot00000000000000GZIP=gzip MAN1PAGES=emms-print-metadata.1 DOCDIR=doc/ LISPDIR=lisp SRCDIR=src ALLSOURCE=$(wildcard $(LISPDIR)/*.el) ALLCOMPILED=$(wildcard $(LISPDIR)/*.elc) DESTDIR= PREFIX=$(DESTDIR)/usr/local INFODIR=$(PREFIX)/info MAN1DIR=$(PREFIX)/share/man/man1 SITELISP=$(PREFIX)/share/emacs/site-lisp/emms INSTALLINFO = /usr/sbin/install-info --info-dir=$(INFODIR) CHANGELOG_CMD = git log --pretty=medium --no-merges # The currently released version of EMMS VERSION=3.0 .PHONY: all install lisp docs deb-install clean .PRECIOUS: %.elc all: lisp docs autoloads: $(MAKE) -C $(LISPDIR) emms-auto.el lisp: $(MAKE) -C $(LISPDIR) docs: $(MAKE) -C $(DOCDIR) emms-print-metadata: $(SRCDIR)/emms-print-metadata.c $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $(SRCDIR)/$@ $< `taglib-config --cflags --libs` -ltag_c install: test -d $(SITELISP) || mkdir -p $(SITELISP) test -d $(INFODIR) || install -d $(INFODIR) install -m 644 $(ALLSOURCE) $(SITELISP) install -m 644 $(ALLCOMPILED) $(SITELISP) install -m 0644 $(DOCDIR)emms.info $(INFODIR)/emms for p in $(MAN1PAGES) ; do $(GZIP) -9c $$p > $(MAN1DIR)/$$p.gz ; done $(INSTALLINFO) emms.info remove-info: $(INSTALLINFO) --remove emms.info ChangeLog: $(CHANGELOG_CMD) > $@ clean: -rm -f *~ $(DOCDIR)emms.info $(DOCDIR)emms.html $(SRCDIR)/emms-print-metadata $(MAKE) -C $(LISPDIR) clean dist: clean autoloads git archive --format=tar --prefix=emms-$(VERSION)/ HEAD | \ (cd .. && tar xf -) rm -f ../emms-$(VERSION)/.gitignore cp lisp/emms-auto.el ../emms-$(VERSION)/lisp $(CHANGELOG_CMD) > ../emms-$(VERSION)/ChangeLog release: dist (cd .. && tar -czf emms-$(VERSION).tar.gz \ emms-$(VERSION) ; \ zip -r emms-$(VERSION).zip emms-$(VERSION) && \ gpg --detach emms-$(VERSION).tar.gz && \ gpg --detach emms-$(VERSION).zip) upload: (cd .. && echo "Directory: emms" | gpg --clearsign > \ emms-$(VERSION).tar.gz.directive.asc && \ cp emms-$(VERSION).tar.gz.directive.asc \ emms-$(VERSION).zip.directive.asc && \ echo open ftp://ftp-upload.gnu.org > upload.lftp ; \ echo cd /incoming/ftp >> upload.lftp ; \ echo mput emms-$(VERSION).zip* >> upload.lftp ; \ echo mput emms-$(VERSION).tar.gz* >> upload.lftp ; \ echo close >> upload.lftp ; \ lftp -f upload.lftp ; \ rm -f upload.lftp) emms-3.0+20130821+1.git2fba618.orig/NEWS000066400000000000000000000061541220515147500165670ustar00rootroot00000000000000News since version 3.0: - Drop support for emms-player-gstreamer. - emms-player-mplayer: Remove code for loading movie subtitles automatically by emms, since mplayer has an option (`sub-fuzziness') controlling that. - Support displaying lyrics in a dedicated buffer, to highlight playing lyric. News since version 2.1: - Saving bookmarks in individual tracks is now supported. - Support deleting files from the cache. - Add mms:// URL support for emms-player-mplayer. - Implement pause, resume, and seek support for emms-player-alsaplayer. - A new player called emms-player-timidity has been implemented which is capable of playing midi files. - A new player called emms-player-fluidsynth is also capable of playing midi files. - A new player called emms-player-xine (in emms-player-xine.el) is capable of using Xine to play tracks. - Hitting "<" or ">" in emms-playlist-mode causes seeking backward and forward, respectively. - New file jack.el allows jackd to be started from within emacs, and permits clients to be connected or disconnected. - New file emms-tag-editor.el is capable of editing track information in songs. - New file emms-mark.el allows tracks in a playlist to be marked and operated on, much like dired mode. - New file emms-lastfm.el implements submitting track information to Last.fm and playing Last.fm radio streams. Support for playing these streams has been added to emms-streams.el. - New file emms-i18n.el adds support for handling coding systems properly in process input/output. This is currently only used by emms-info-mp3info.el. - New file emms-history.el causes playlists to be saved automatically when exiting Emacs. - emms-player-mpd: - Using the emms-browser to insert and play tracks now works properly with emms-player-mpd. - Display the correct error message if an error occurs while trying to play a stream. - Handle any encoding errors that occur during playback. - Integrate with emms-lastfm.el in `emms-player-mpd-show', if emms-lastfm is loaded and active. - emms-playing-time supports different display styles now. - emms-lyrics: Now support lyrics auto-scrolling. - emms-player-mplayer: Capable of loading movie subtitles automatically now. News since version 2.0: - A new player that uses mpg321's remote mode is now available (emms-player-mpg321-remote)--this allows seeking and copes with errors in files. - A metadata browser has been added in emms-browser.el. - Recording of the time a track was last played is now supported. - emms-play-* and emms-add-* functions now toggle their play/add behavior when a prefix argument is supplied. - EMMS now caches tracks, which speeds up loading of large collections with info tags considerably. - EMMS now knows how to change the volume (emms-volume.el). - When playing a playlist from emms-playlist-mode, EMMS now optionally opens a new buffer for this playlist. User-visible bug fixes: - The emms-playlist-mode is now much faster. - MP3s with errors are now playable when using the remote player. emms-3.0+20130821+1.git2fba618.orig/README000066400000000000000000000156761220515147500167610ustar00rootroot00000000000000EMMS --- The Emacs Multi-Media System -*-outline-*- ===================================== * Introduction, Overview ======================== EMMS is the Emacs Multi-Media System. It tries to be a clean and small application to play multimedia files from Emacs using external players. Many of its ideas are derived from MpthreePlayer (http://www.nongnu.org/mp3player), but it tries to be more general and more clean. The basic functionality of Emms consists of three parts: The core, the sources, and the players. The core resides in `emms.el', and provides a simple playlist and the basic functionality to use all the other features of Emms. It provides the common user commands and interfaces for other parts. It thinks in tracks, where a track is the combination of a type and a name--e.g., the track type 'file has a name that is the file name. Other track types are possible. To get to tracks, the core needs sources. The file `emms-source-file.el' provides simple sources to interact with the file system. When Emms finally has the sources in the playlist, it needs a player to play them. `emms-player-simple.el' defines a few useful players, and allows you to define your own in a very simple way. The way Emms works is easy to customize with your own code or by using `M-x customize RET'. * Installation ============== You need to put all the .el files of EMMS in a directory in your load-path. For example, if you put all those files into ~/elisp/emms/, then in your ~/.emacs you should do: (add-to-list 'load-path "~/elisp/emms/") For information about compiling Emms into byte-code see the ``Compiling Emms'' section in the Emms manual. ** Setup -------- After adding the location of the Emms code to the load-path variable, we invoke the following using the `emms-setup' feature which allows for quick and simple Emms setup. (require 'emms-setup) (emms-standard) (emms-default-players) After which Emms is set up and ready to go! For more information about different setup levels and features see the ``Simple Setup'' section of the Emms manual. ** Usage -------- The basic functionality of EMMS is just to play music without being noticed. It provides a few commands to skip the current track and such, but otherwise, it doesn't show up. EMMS provides the following basic user commands (that you might want to bind to keys): emms-start ...... Start playing the current playlist emms-stop ....... Stop playing emms-next ....... Go to the next track in the playlist emms-previous ... Go to the previous track in the playlist emms-shuffle .... Shuffle the playlist emms-show ....... What is playing? But before you can use these, you need a playlist to start with. The following commands allow you to create a playlist from different sources: emms-play-file ............. Play a single file emms-play-directory ........ Play a whole directory emms-play-directory-tree ... Play a directory tree * The Interactive Playlist buffer ================================= Emms provides a visual, interactive playlist mode as well as the ability to use playlists without ever looking at them. This visual, interactive mode is called the `emms-playlist-mode' and is defined in `emms-playlist-mode.el'. To use the interactive playlist invoke: `M-x emms-playlist-mode-go RET' When in the interactive playlist mode we can perform different actions on the current playlist. Here are some basic commands: `n'....Start playing the next track in the playlist. `p'....Start playing the previous track in the playlist. `s'....Stop playing. `f'....Describe the currently playing track in the minibuffer. `c'....Display the current track in the center of the screen. `RET'..Start playing the track under point. Note that this is also available with `Mouse-2'. `q'....Put the interactive playlist buffer at the end of the list of all buffers (i.e., bury it). As always, for more commands see the mode documentation and the ``Interactive Playlists'' section of the Emms manual. * Bare Bones Setup ================== The following code fragment provides a minimal EMMS setup without using the layer of `emms-default'. It can maybe be used to better understand the internals of EMMS. You can see how EMMS needs to know about players (these are defined in `emms-player-simple') and about sources for tracks (trivial file system based sources, such as this `emms-directory-tree', are defined in `emms-source-file'). (require 'emms-player-simple) (require 'emms-source-file) (require 'emms-source-playlist) (setq emms-player-list '(emms-player-mpg321 emms-player-ogg123 emms-player-mplayer)) * Advanced configuration ======================== ** Seeking ---------- In most multimedia players, you can seek forward or backward in a track. EMMS supports this too. If you're using mplayer, check that `emms-player-mplayer-parameters' contains ``slave''. If you're using mpg321, there is a module called emms-player-mpg321-remote.el. To use it, simply add the following lines to your configuration: (require 'emms-player-mpg321-remote) (push 'emms-player-mpg321-remote emms-player-list) Finally, if you are using mpd, no special config is needed. Seeking works through the following functions: `emms-seek' which takes a negative or positive amount of seconds. `emms-seek-forward' which seeks ten seconds forward. `emms-seek-backward' which seeks ten seconds backward. * Using libtag for reading tags =============================== There is a way to read tags using Libtag as your backend for emms-info. However, since it requires a binary file (source code provided with EMMS), it isn't enabled by default. To compile it, type ``make emms-print-metadata''. To install it, either put emms-print-metadata in your $PATH, or add EMMS' directory to Emacs' exec-path. Since libtag overwrites most of the usual methods for providing info, it's best to keep it as the only function in `emms-info-functions'. Here's a way to do so: (require 'emms-info-libtag) (setq emms-info-functions '(emms-info-libtag)) * EMMS, Emms, emms, or what? ============================ In various contexts, this program is called EMMS, Emms or emms. Those are all correct, and which one you use is a matter of personal preference. EMMS highlights the acronym character of the name. Emms is akin to Emacs and Gnus, ignoring that Emms is pronounced ee-em-em-es, and not a single name. emms is highlighting that emms is a case-sensitive file name and Emacs Lisp command. * Getting help ============== Emms has a mailing list at emms-help@gnu.org. To subscribe to it, visit http://lists.gnu.org/mailman/listinfo/emms-help. If you are familiar with the Gmane service, there is a Gmane newsgroup which mirrors this mailing address at gmane.emacs.emms.user. Emms also has a website at . * License ========= EMMS is available under the terms of the GNU General Public License. Please see the file COPYING for details. emms-3.0+20130821+1.git2fba618.orig/RELEASE000066400000000000000000000003131220515147500170620ustar00rootroot00000000000000This file tries to list the things people have to do before they do a release. 1) Increase the version number in emms.el 2) Update NEWS 3) Run make release 4) Run make upload 5) Send announcement email emms-3.0+20130821+1.git2fba618.orig/doc/000077500000000000000000000000001220515147500166275ustar00rootroot00000000000000emms-3.0+20130821+1.git2fba618.orig/doc/Makefile000066400000000000000000000002541220515147500202700ustar00rootroot00000000000000# Don't delete if make is interrupted .PRECIOUS: %.info %.html all: emms.info %.info: %.texinfo makeinfo --no-split $< %.html: %.texinfo makeinfo --html --no-split $< emms-3.0+20130821+1.git2fba618.orig/doc/emms.texinfo000066400000000000000000002260531220515147500211760ustar00rootroot00000000000000\input texinfo @c -*-texinfo-*- @c %**start of header @setfilename emms.info @settitle The Emms Manual @c %**end of header @c History: The Emms manual was almost entirely rewritten for the @c release of Emms version 2. @c As a rule, modules which are stable enough to be included into the @c `emms-all' setup level should be documented. That is, any feature @c which is considered stable should be included. @dircategory Emacs @direntry * Emms: (emms). The Emacs Multimedia System @end direntry @copying @copyright{} 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. @quotation Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License". @end quotation @end copying @c For printed material @titlepage @title The Emms Manual @page @vskip 0pt plus 1filll @insertcopying @end titlepage @contents @c END For printed material @ifnottex @node Top, Introduction, (dir), (dir) @top Emms Manual This is the Manual for the Emacs Multimedia System. @insertcopying @menu Starting out * Introduction:: Introduction to Emms. * Installation:: How to install Emms on your System. * Simple Setup:: Quick, basic default Emms setup. * Configuration:: More detailed setup and configuration. * Quickstart Guide:: First steps with EMMS for new users. * Getting Help:: Where to get help with Emms and make suggestions. * Formats and Freedom:: File formats without restrictions. Emms basics * Basic Commands:: How to control Emms with ease. * The Core File:: The inner core of Emms. * Sources:: Sources for playlists-creation. * Simple Players:: Some simple players. * Playlists:: How Emms organizes media. Advanced Features * Track Information:: More narrative track descriptions. * Interactive Playlists:: Interactive Playlists. * Markable Playlists:: Allow tracks to be marked. Modules and Extensions * The Browser:: Advanced metadata browsing. * Sorting Playlists:: Sorting the order of the tracks. * Persistent Playlists:: Restoring playlists on emacs startup. * Editing Tracks:: Editing track information from within Emms. * Emms Mode Line:: Emms information on the mode line. * Music Player Daemon:: Interface to Music Player Daemon. * Streaming Audio:: Interface to streaming audio. * Lyrics:: Displaying lyrics synchronously. * Volume:: Changing the volume. * Last.fm:: Interact with the Last.fm service. * APE / FLAC Commands:: How to play next or previous track in these files. * Bookmarks:: Saving a place in a media file. * Extending Emms:: How to define new players and modules. Copying and license * Copying:: The GNU General Public License gives you permission to redistribute Emms on certain terms; it also explains that there is no warranty. * The GNU FDL:: The license for this documentation. Indices * Concept Index:: * Function Index:: * Variable Index:: * Keybinding Index:: @detailmenu --- The Detailed Node Listing --- Here are some other nodes which are really inferiors of the ones already listed, mentioned here so you can get to them in one step: Installation * Compiling Emms:: Compiling Emms into Byte-Code. The Core File * User Variables:: Variables for the user to tweak. * Hooks:: Hooks for important Emms functions. * Core Functions:: Providing the basic functionality of Emms. Track Information * Defining Info Methods:: Defining new info methods. Last.fm * Last.fm Setup:: Configuring Emms to use Last.fm. * Last.fm Radio:: Listening to music through Last.fm. * Last.fm Audioscrobbler:: Submitting music to Last.fm Extending Emms * New Player:: How to define a new player. * Simple Player for `play':: Example player using @command{play}. * More Complex Player:: Example of a complex player using @command{mpg321}. @end detailmenu @end menu @end ifnottex @node Introduction @chapter Introduction @cindex introduction Emms is the Emacs Multi-Media System. It tries to be a clean and small application to play multimedia files from Emacs using external players. Many of its ideas are derived from @uref{http://www.nongnu.org/mp3player, MpthreePlayer}, but it tries to be more general and more clean. This manual tries to be the definitive source of information about Emms, an online version of the manual is available at: @uref{http://www.gnu.org/software/emms/manual/}. The basic functionality of Emms consists of three parts: The core, the sources, and the players. The core resides in @file{emms.el}, and provides a simple playlist and the basic functionality to use all the other features of Emms. It provides the common user commands and interfaces for other parts. It thinks in tracks, where a track is the combination of a type and a name - e.g. the track type 'file has a name that is the file name. Other track types are possible. To get to tracks, the core needs sources. The file @file{emms-source-file.el} provides simple sources to interact with the file system. When Emms finally has the sources in the playlist, it needs a player to play them. @file{emms-player-simple.el} defines a few useful players, and allows you to define your own in a very simple way. The way Emms works is easy to customize with your own code or by using `M-x customize'. @node Installation @chapter Installation @cindex installation You need to put all the .el files of emms in a directory in your load-path. For example, if you put all those files into ~/elisp/emms/, then in your ~/.emacs, you should do: @lisp (add-to-list 'load-path "~/elisp/emms/lisp/") @end lisp @noindent @menu * Compiling Emms:: Compiling Emms into Byte-Code. @end menu @node Compiling Emms @section Compiling Emms @cindex compiling If you are using XEmacs, you will need to edit @file{Makefile} as follows before continuing. @example EMACS=xemacs SITEFLAG=-no-site-file @end example You can byte-compile Emms by first entering the directory containing the Emms source code, followed by invoking: @command{make} Which will byte compile Emms. You can then invoke: @command{make install} Which will install Emms into your Emacs directories (provided you have the appropriate permissions to do so on your system). Note that Emms is a light-weight and agile program, you can therefore run Emms just fine without byte compiling it. @node Simple Setup @chapter Simple Setup @cindex simple setup @cindex setting up Emms @cindex quick setup After adding the location of the Emms code to the @var{load-path} variable, see @xref{Installation}. We invoke the following using the `emms-setup' feature which allows for quick and simple Emms setup. @lisp (require 'emms-setup) (emms-standard) (emms-default-players) @end lisp After which Emms is set-up and ready to go! The above will setup Emms with standard features (interactive playlists, audio track tag reading etc.) and a default list of players (ogg, mp3, mplayer etc.). The `emms-setup' feature is provided by the file @file{emms-setup.el}. It is essentially a collection of shortcuts for setting up Emms quickly and simply. Everything you can do with `emms-setup' can also be done manually. We use `emms-setup' by calling one of the setup functions. Each of the functions sets up Emms to include a number of features. @defun emms-minimalistic An Emms setup script. Invisible playlists and all the basics for playing media. @end defun @defun emms-standard An Emms setup script. Everything included in the @code{emms-minimalistic} setup, the Emms interactive playlist mode and reading information from tagged audio files. @end defun @defun emms-all An Emms setup script. Everything included in the @code{emms-standard} setup and adds all the stable features which come with the Emms distribution. @end defun @defun emms-devel An Emms setup script. Everything included in the @code{emms-all} setup and adds all of the features which come with the Emms distribution regardless of if they are considered stable or not. Use this if you like living on the edge. @end defun `emms-setup' also comes with a convenience function to set a default list of media players. @defun emms-default-players Set @var{emms-player-list} to @var{emms-setup-default-player-list}. @end defun It is also worth noting that you can write your own Emms setup functions like the above by looking at the existing function definitions in @file{emms-setup.el}. @node Configuration @chapter Configuration @cindex Configuration This chapter discusses the configuration of Emms in more detail. The following code fragment provides a minimal Emms setup without using the layer of `emms-setup'. It can maybe be used to better understand the internals of Emms. You can see how Emms needs to know about players (these are defined in `emms-player-simple') and about sources for tracks (trivial file system based sources, such as this `emms-directory-tree', are defined in `emms-source-file'). @lisp (require 'emms-player-simple) (require 'emms-source-file) (require 'emms-source-playlist) (setq emms-player-list '(emms-player-mpg321 emms-player-ogg123 emms-player-mplayer)) @end lisp For a discussion on how to define additional players, see @xref{Simple Players}. Much of the behaviour of Emms can be changed by setting variables. For example: @lisp (setq emms-info-asynchronously nil) (setq emms-playlist-buffer-name "*Music*") @end lisp The first @code{setq} turns off the asynchronous updating of info tags. The second sets the default name of the Emms playlist buffer. Another way to change Emms variables is to use the M-x @command{customize} mechanism provided by Emacs. @menu * Finding files and speed:: Finding files quickly or portably. @end menu @node Finding files and speed @section Finding files and speed @cindex files @cindex speed Emms needs to traverse directories in order to find playable media. The default method Emms uses to achive this is @code{emms-source-file-directory-tree-internal} as defined in @file{emms-source-file.el}. The above method is written portably and will always work, but might be too slow if we want to load several hundred tracks (or more). @file{emms-source-file.el} defines another method for finding files, @code{emms-source-file-directory-tree-find} which uses GNU/find. @code{emms-source-file-directory-tree-find} is usually an order of magnitude faster, but of course will not work if you do not have GNU/find installed. The method Emms will use is defined in the customisable variable @var{emms-source-file-directory-tree-function}. @node Quickstart Guide @chapter Quickstart Guide This chapter demonstrates how to setup EMMS so that you can start listening to your music without having to read all of the documentation first. The first thing you have to do is telling Emacs where the sources of EMMS are located. Let's say you have them in @file{~/elisp/emms/}. So add this line to your @file{.emacs}. @lisp (add-to-list 'load-path "~/elisp/emms/lisp/") @end lisp Further informations about installing EMMS can be found in the installation chapter, @xref{Installation}. Let's say you want to enable all features which are considered stable by the EMMS developers. To achieve this you invoke the @code{emms-all} setup function by adding the following three lines to your @file{.emacs}. @lisp (require 'emms-setup) (emms-all) (emms-default-players) @end lisp The function @code{emms-default-players} in the last line sets up the list of default players. The list contains lightweight specialized players like ogg123 or mpg321 and we-play-everything-players like mplayer or xine. To be sure that emms can play all your music you should check that the needed players are installed. Further informations about the several setup scripts can be found in the simple setup chapter, @xref{Simple Setup}. Of course EMMS tries to display the tags of the music files you listen to. For this to work you have to make sure that the appropriate programs are installed. For mp3 files you need `mp3info', and for ogg files you need `ogginfo'. The last thing to do is to tell EMMS the root directory of our music collection. Let's say all your music is in @file{~/Music} or in subdirectories thereof. @lisp (setq emms-source-file-default-directory "~/Music/") @end lisp OK, now we've set up EMMS. Reload your @file{.emacs} or restart Emacs to let the changes have an effect. Now we will add all our music to a playlist by invoking @kbd{M-x emms-add-directory-tree RET ~/Music/ RET}. We do this because then EMMS will read the tags of all your music files and cache them. This is required for the browser, @xref{The Browser}. To switch to the playlist buffer, invoke @kbd{M-x emms-playlist-mode-go} or simply @kbd{M-x emms}. You can see that most tracks are displayed with their file name, but track by track the filename gets replaced with the artist and track name of the file's tag. Hit @kbd{RET} on a track to start playback. Now you can start exploring EMMS. It's probably best to begin with the basic commands (@pxref{Basic Commands}), the interactive playlists (@pxref{Interactive Playlists}), and the browser (@pxref{The Browser}). @node Getting Help @chapter Getting Help @cindex mailing list @cindex website If you have a bug to report, need help, or wish to suggest a feature, please feel free to use the Emms mailing list. The address of the list is emms-help@@gnu.org. To subscribe to it, visit @url{http://lists.gnu.org/mailman/listinfo/emms-help}. If you are familiar with the Gmane service, there is a Gmane newsgroup which mirrors this mailing address at gmane.emacs.emms.user. Emms also has a website at @url{http://www.gnu.org/software/emms/}. @node Formats and Freedom @chapter Formats and Freedom @cindex freedom @cindex format Emms is free software, but some of the file formats it can play carry restrictions, they are proprietary file formats. Proprietary software companies are pushing out audio and video formats which restrict when, where and how you can play them, and restrict developers from writing free software which interacts with them. Restrictive file formats put the corporate bottom-line before the public interest. Fortunately there are alternatives like Ogg. Ogg is a professional grade multimedia format. Ogg Vorbis is the compressed audio format (like MP3), and Ogg Theora is the video format. For more information, go to @url{http://www.xiph.org/}. If you want to transcode audio into a lossless format, you can try FLAC (Free Lossless Audio Codec). FLAC stands out as the fastest and most widely supported lossless audio codec, and the only one that at once is non-proprietary, is unencumbered by patents and has the source code for a reference implementation freely available. For more information about FLAC, go to @url{http://flac.sourceforge.net/}. @node Basic Commands @chapter Basic Commands @cindex basic commands @cindex commands, basic Before you can use the interface commands, you need a playlist to start with. The following commands allow you to add to the current playlist from different sources: Note that the commands with the ``emms-add-'' prefix add the source to the playlist but do not start playing it immediately. Conversely, the commands with the ``emms-play-'' prefix begin playing the track immediately. @defun emms-play-file file A source for a single file - either @var{file}, or queried from the user. If called with a prefix the file will be added like @command{emms-add-file}. @end defun @defun emms-add-file file A source for a single file - either @var{file}, or queried from the user. If called with a prefix the file will be played like @command{emms-play-file}. @end defun @defun emms-play-directory dir A source for a whole directory tree - either @var{dir}, or queried from the user. @end defun @defun emms-add-directory dir A source for a whole directory tree - either @var{dir}, or queried from the user. @end defun @defun emms-play-directory-tree dir A source for multiple directory trees - either @var{dir}, or the value of @var{emms-source-file-default-directory}. @end defun @defun emms-add-directory-tree dir A source for multiple directory trees - either @var{dir}, or the value of @var{emms-source-file-default-directory}. @end defun @defun emms-play-url url A source for an @var{url} - for example, for streaming. @end defun @defun emms-add-url url A source for an @var{url} - for example, for streaming. @end defun @defun emms-play-playlist playlist A source for the M3u or PLS playlist format from the file @var{playlist}. @end defun @defun emms-add-playlist playlist A source for the M3u or PLS playlist format from the file @var{playlist}. @end defun @defun emms-play-find dir regexp A source that will find files in @var{dir} or @var{emms-source-file-default-directory} which match @var{regexp}. @end defun @defun emms-add-find dir regexp A source that will find files in @var{dir} or @var{emms-source-file-default-directory} which match @var{regexp}. @end defun The basic functionality of Emms is just to play music without being noticed. It provides a few commands to skip the current track and such, but other than that it doesn't show up. Emms provides the following basic user commands (which you might want to bind to keystrokes): @defun emms-start Start playing the current playlist @end defun @defun emms-stop Stop playing @end defun @defun emms-next Start playing the next track in the playlist @end defun @defun emms-previous Start playing previous track in the playlist @end defun @defun emms-shuffle Shuffle the current playlist. This uses @var{emms-playlist-shuffle-function}. @end defun @defun emms-sort Sort the current playlist. This uses @var{emms-playlist-sort-function}. @end defun @defun emms-show &optional insertp Describe the current Emms track in the minibuffer. If @var{insertp} is non-nil, insert the description into the current buffer instead. This function uses @var{emms-show-format} to format the current track. @end defun @node The Core File @chapter The Core File @cindex core file @cindex heart of Emms @cindex primitive functions The core file @file{emms.el} provides the all basic functions for playing music, generating playlists and defining players. @menu * User Variables:: Variables for the user to tweak. * Hooks:: Hooks for important Emms functions. * Core Functions:: Providing the basic functionality of Emms. @end menu @node User Variables @section User Variables @cindex user variables @cindex options The core file defines a number of user variables. @defopt emms-player-list A list of players Emms can use. You need to set this in order to use Emms to play media. @end defopt @defopt emms-show-format The format to use for @command{emms-show}. Any "%s" is replaced by what @var{emms-track-description-function} returns for the currently playing track. @end defopt @defopt emms-repeat-playlist Non-nil if the Emms playlist should automatically repeat the playlist. If nil, playback will stop when the last track finishes playing. @end defopt @defopt emms-track-description-function Function for describing an Emms track in a user-friendly way. @end defopt @defopt emms-sort-lessp-function A function that compares two tracks, and returns non-nil if the first track should be sorted before the second (see also @code{sort}). @end defopt @node Hooks @section Hooks @cindex hooks @cindex adding functionality The core file provides hook variables for the basic functionality of Emms. @defopt emms-player-started-hook A hook run when an Emms player started playing. @end defopt @defopt emms-player-stopped-hook A hook run when an Emms player stopped playing. See also @var{emms-player-finished-hook}. @end defopt @defopt emms-playlist-source-inserted-hook Hook run when a source got inserted into the playlist. The buffer is narrowed to the new tracks. @end defopt @defopt emms-playlist-selection-changed-hook Hook run after another track is selected in the Emms playlist. @end defopt @defopt emms-playlist-cleared-hook Hook run after the current Emms playlist is cleared. This happens both when the playlist is cleared and when a new buffer is created for it. @end defopt @defopt emms-player-finished-hook Hook run when an Emms player finishes playing a track. Please pay attention to the differences between @var{emms-player-finished-hook} and @var{emms-player-stopped-hook}. The former is called only when the player is stopped interactively; the latter, only when the player actually finishes playing a track. @end defopt @defopt emms-player-paused-hook Hook run when a player is paused or resumed. Use @var{emms-player-paused-p} to find the current state. @end defopt @node Core Functions @section Core Functions @cindex core functions @cindex basic functions The core file also defines all the functions important to the basic use of Emms. There are functions which deal with movement in the playlist. @defun emms-next-noerror Start playing the next track in the Emms playlist. Unlike @code{emms-next}, this function doesn't signal an error when called at the end of the playlist. This function should only be called when no player is playing. This is a good function to put in @code{emms-player-finished-hook}. @end defun @defun emms-playlist-next Move to the previous track in the current buffer. @end defun @defun emms-playlist-previous Move to the previous track in the current buffer. @end defun @defun emms-random Jump to a random track. @end defun @defun emms-toggle-repeat-playlist Toggle whether emms repeats the playlist after it is done. See @var{emms-repeat-playlist}. @end defun @defun emms-toggle-repeat-track Toggle whether emms repeats the current track. See @var{emms-repeat-track}. @end defun @defun emms-toggle-random-playlist Toggle whether emms plays the tracks randomly or sequentially. See @end defun @var{emms-random-playlist}. Some functions deal with the getting and setting track information. @defun emms-track type name Create a track with type @var{type} and name @var{name}. @end defun @defun emms-track-type track Return the type of @var{track}. @end defun @defun emms-track-name track Return the name of @var{track}. @end defun @defun emms-track-get name track &optional inexistent Return the value of @var{name} for @var{track}. If there is no value, return @var{default} (or nil, if not given). @end defun @defun emms-track-set track name value Set the value of @var{name} for @var{track} to @var{value}. @end defun @defun emms-track-description track Return a description of @var{track}. This function uses @var{emms-track-description-function}. @end defun @defun emms-player-for track Return an Emms player capable of playing @var{track}. This will be the first player whose PLAYABLEP function returns non-nil, or nil if no such player exists. @end defun @defun emms-playlist-current-selected-track Return the currently selected track in the current playlist. @end defun There are also functions which deal with the playing itself. @defun emms-player-start track Start playing @var{track}. @end defun @defun emms-player-stop Stop the currently playing player. @end defun @defun emms-player-stopped Declare that the current Emms player is finished. This should only be done by the current player itself. @end defun @defun emms-seek seconds Seek the current player @var{seconds} seconds. This can be a floating point number for sub-second fractions. It can also be negative to seek backwards. @end defun @defun emms-seek-forward Seek ten seconds forward. @end defun @defun emms-seek-backward Seek ten seconds backward. @end defun For more basic commands defined in the core file see @xref{Basic Commands}. @node Sources @chapter Sources @cindex Sources Sources allow Emms to add and play tracks. Emms comes with a number of sources of its own. Sources are designed so that creating new ones will be easy. For examples of Emms sources for files and directories see @file{emms-source-file.el}. @defopt emms-source-file-default-directory The default directory to look for media files. @end defopt @defun emms-play-find Play all files in @var{emms-source-file-default-directory} that match a specific regular expression. @end defun @defun emms-source-file &optional file An Emms source for a single file - either @var{file}, or queried from the user. @end defun @defun emms-source-files files An Emms source for a list of @var{files}. @end defun @defun emms-source-directory &optional dir An Emms source for a whole directory tree - either @var{dir}, or queried from the user @end defun @defun emms-source-directory-tree & optional dir An Emms source for multiple directory trees - either @var{dir}, or the value of @var{emms-source-file-default-directory}. @end defun @defun emms-source-playlist file An EMMS source for playlists. See @var{emms-source-playlist-formats} for a list of supported formats. @end defun @defun emms-source-playlist-native file An EMMS source for a native EMMS playlist file. @end defun @defun emms-source-playlist-m3u file An EMMS source for an m3u playlist file. @end defun @defun emms-source-playlist-pls file An EMMS source for a pls playlist file. @end defun @defun emms-source-find &optional dir regex An Emms source that will find files in @var{dir} or @var{emms-source-file-default-directory} that match @var{regexp}. @end defun @defun emms-source-file-directory-tree &optional dir Return a list of all files under @var{dir} which match @var{regex}. @end defun @defun emms-source-dired Play all marked files of a dired buffer @end defun @defun emms-source-file-regex Return a regexp that matches everything any player (that supports files) can play. @end defun @defun emms-locate regexp Search for @var{regexp} and display the results in a locate buffer @end defun @node Simple Players @chapter Simple Players @cindex players, simple @defmac define-emms-simple-player name types regex command &rest args Define a simple player with the use of `emms-define-player'. @var{name} is used to construct the name of the function like emms-player-@var{name}. @var{types} is a list of track types understood by this player. @var{regex} must be a regexp that matches the filenames the player can play. @var{command} specifies the command line argument to call the player and @var{args} are the command line arguments. @end defmac For a discussion on how to define new players see @xref{New Player}. @defun emms-player-simple-stop Stop the currently playing process, if indeed there is one. @end defun @defun emms-player-simple-start filename cmdname params Starts a process playing @var{filename} using the specified @var{cmdname} with the specified @var{params}. @end defun @defun emms-player-simple-sentinel proc str Sentinel for determining the end of process for the process @var{proc} and the sentinel string @var{str}. @end defun @node Playlists @chapter Playlists @cindex organizing tracks and media Emms uses Emacs buffers to store the media tracks for playing. We call one such buffer a ``playlist buffer'' or an ``Emms playlist buffer''. Emms then proceeds to play the media tracks in the buffer from top to bottom until the end of the playlist. The name of the playlist buffer is defined in the variable @var{emms-playlist-buffer-name} and is set to be an invisible Emacs buffer by default. You can change to any name you want. For an example configuration see @xref{Configuration}. You can create any number of playlist buffers you wish. At any time Emms has a single ``current'' buffer through which it proceeds track by track. @defun emms-playlist-new &optional name Create a new playlist buffer. The buffer is named @var{name}, but made unique. @var{name} defaults to `emms-playlist-buffer-name'. If called interactively, the new buffer is also selected. @end defun @defun emms-playlist-save &optional format file Store the current playlist to FILE as the type FORMAT. The default format is specified by @var{emms-source-playlist-default-format}. @end defun The current Emms playlist buffer is stored in the variable @var{emms-playlist-buffer}. @node Track Information @chapter Track Information @cindex track information @cindex info tags Emms is distributed with three predefined methods for retrieving info, provided by @file{emms-info-mp3info.el}, @file{emms-info-ogginfo.el} and @file{emms-cue.el}. The first two packages are front-ends for command-line tools. Ogg track information is retrieved using the @uref{http://directory.fsf.org/audio/ogg/vorbistools.html, ogginfo} software. Likewise, mp3 track information is available using @uref{http://www.ibiblio.org/mp3info/, mp3info}. While, @file{emms-cue.el} retrieves tracks information for ape/flac files by parsing a cue sheet file, which is plain text. Automatic track information retrieval is enabled by default in the `emms-standard', `emms-all' and `emms-devel' setup levels provided by @file{emms-setup.el}. For more information about @file{emms-setup.el} see @xref{Simple Setup}. If you would like to know how Emms track retreival works and how we can define new methods for track retrieval see @xref{Defining Info Methods}. There are a number of user variables which control the behaviour of `emms-info'. @defopt emms-info-auto-update Non-nil when Emms should update track information if the file changes. This will cause hard drive activity on track loading. If this is too annoying for you, set this variable to nil. @end defopt @defopt emms-info-asynchronously Non-nil when track information should be loaded asynchronously. This requires the feature `later-do' which is provided by the file @file{later-do.el}, which should come with Emms. @end defopt @defopt emms-info-functions Functions which add information to tracks. Each is called with a track as argument. @end defopt @menu * Defining Info Methods:: Defining new info methods. @end menu @node Defining Info Methods @section Defining Info Methods @cindex defining info methods An info method essentially consists of a function which given an Emms track returns the appropriate info for that track. We can for example look at the predefined method for retrieving information about audio tracks in the Ogg format. The function @command{emms-info-ogginfo} provided by @file{emms-info-ogginfo.el} accepts an Emms track as a single argument and returns the appropriate information string. We then register our info function with Emms by adding it to the @var{emms-info-functions} list. The function will then be called at the right time to provide track info. @lisp (add-to-list 'emms-info-functions 'emms-info-ogginfo) @end lisp @node Interactive Playlists @chapter Interactive Playlists @cindex Interactive Playlists Emms provides a visual, interactive playlist mode as well as the ability to use playlists without ever looking at then. This visual, interactive mode is called the `emms-playlist-mode' and is defined in @file{emms-playlist-mode.el}. The interactive playlist mode is enabled by default in the `emms-standard', `emms-all' and `emms-devel' setup levels. For more information about Emms setup levels see @xref{Simple Setup}. @defun emms-playlist-mode-go Switch to the current emms-playlist buffer and use emms-playlist-mode. @end defun If you wish to make this the default EMMS playlist mode, add the following to your @file{.emacs}. @lisp (setq emms-playlist-default-major-mode 'emms-playlist-mode) @end lisp The interactive playlist buffer shows the tracks in the current Emms playlist in the order in which they will be played. The current track will be highlighted. When in the interactive playlist mode we can perform different actions on the current playlist. @table @kbd @item a @findex emms-playlist-mode-add-contents Add files in the playlist at point to the current playlist buffer. If we are in the current playlist, make a new playlist buffer and set it as current. @item b @findex emms-playlist-set-playlist-buffer Set the current playlist buffer. @item n @findex emms-next Start playing the next track in the playlist. @item p @findex emms-next Start playing the previous track in the playlist. @item s @findex emms-stop Stop playing. @item P @findex emms-pause Pause. @item > @findex emms-seek-forward Seek ten seconds forward. @item < @findex emms-seek-backward Seek ten seconds backward. @item f @findex emms-show Describe the currently playing track in the minibuffer. @item c @findex emms-playlist-mode-center-current Display the current track in the center of the screen. @item RET @findex emms-playlist-mode-play-current-track Start playing the track under point. Note that this is also available with @kbd{}. @item SPC @findex scroll-up Scroll up a near full page. @item M-< @findex emms-playlist-mode-first Go to the first track in the playlist. @item M-> @findex emms-playlist-mode-last Go to the last track in the playlist. @item r @findex emms-random Go to a randomly selected track in the playlist. @item q @findex bury-buffer Put the interactive playlist buffer at the end of the list of all buffers. @item C-x C-s @findex emms-playlist-save Save the current playlist buffer to a file. By default, Emms will ask you for confirmation before overwriting an existing playlist. You can silently overwrite existing playlist by setting @var{emms-source-playlist-ask-before-overwrite} to nil. @item ? @findex describe-mode Describe the mode. @end table We can also edit the playlist using familiar GNU/Emacs commands: @table @kbd @item C-k @findex emms-playlist-mode-kill-track Remove the track under point from the playlist buffer. Also available using the @kbd{d} key. @item C-y @findex emms-playlist-mode-yank See the command @command{yank} @item C-w @findex emms-playlist-mode-kill See the command @command{kill-region} @item M-y @findex emms-playlist-mode-yank-pop See the command @command{yank-pop}. @item C-j @findex emms-playlist-mode-insert-newline Insert a newline at point. @end table We can use the regular GNU/Emacs killing and yanking commands to move and copy tracks in between playlist buffers. We can use the same commands to insert arbitrary text into the playlist buffers together with the playlist tracks. Text which is not a track is ignored by the program and can therefore be used to include titles and annotations within the playlist. @node Markable Playlists @chapter Markable Playlists @cindex Markable Playlists The Markable Playlists provided by the file @file{emms-mark.el} are an alternative to the default interactive playlists, @xref{Interactive Playlists}. They allow marking tracks with keybindings familiar to users of dired. To enable the Markable Playlists you have to add @lisp (require 'emms-mark) @end lisp to your @file{.emacs}. Then you can activate @command{emms-mark-mode} by executing @command{M-x emms-mark-mode} in a playlist buffer. You can return to the default interactive playlist mode with @command{M-x emms-mark-mode-disable}. If you wish to make this the default EMMS playlist mode, add the following to your @file{.emacs}. @lisp (setq emms-playlist-default-major-mode 'emms-mark-mode) @end lisp @table @kbd @item m @findex emms-mark-forward Marks the current track and sets point one line forward. If a prefix argument ARG is given, it will mark the next ARG tracks and set point accordingly. A negative argument marks backward. @item U @findex emms-mark-unmark-all Unmarks all tracks in the playlist. @item t @findex emms-mark-toggle Toggles mark on the current track. @item u @findex emms-mark-unmark-forward Unmarks same way as @command{emms-mark-forward} marks. @item % m @findex emms-mark-regexp Marks all tracks in the playlist matching the given regular expression. A prefix argument means to unmark them instead. @end table When tracks are marked you can operate on them: @table @kbd @item D @findex emms-mark-delete-marked-tracks Deletes the marked tracks from the playlist. @item K @findex emms-mark-kill-marked-tracks Deletes the marked tracks from the playlist and places them in the kill-ring, so that you can @command{yank} in into another playlist. @item W @findex emms-mark-copy-marked-tracks Adds the marked tracks to the kill-ring, so that you can @command{yank} them into another playlist. @end table emms-mark is also intent to provide a way for user to select tracks for other command to operate on them. Currently, @file{emms-tag-editor.el} used the emms-mark to edit tags of selected tracks. Two function is useful for elisp programer to handle marked tracks. @defun emms-mark-do-with-marked-track This function take a function to perform on all marked tracks. A optional argument `move-flag' to tell the function to move forward line after calling given function. If the given function didn't change position, the second argument should set to non-nil. @end defun @defun emms-mark-mapcar-marked-track This function is very similar to `emms-mark-do-with-marked-track' except it collects result of given function (that's why named with `mapcar'). @end defun @node APE / FLAC Commands @chapter APE / FLAC Commands Often, a single APE or FLAC file contains a complete ablum. We can still play next or previous track in the ablum with the help of @file{emms-cue.el} package, provided there is a corresponding cue sheet file. This package also defines @code{emms-info-cueinfo} for retreiving the track information for APE / FLAC itself. To load @file{emms-cue.el}: @lisp (require 'emms-cue) (add-to-list 'emms-info-functions 'emms-info-cueinfo) @end lisp @defun emms-cue-next Play next track from .cue file @end defun @defun emms-cue-previous Play previous track from .cue file @end defun @node Bookmarks @chapter Bookmarks Emms can save a ``temporal bookmark'' in a media file via emms-bookmarks. The file @file{emms-bookmarks.el} provides the package emms-bookmarks. While some media is playing, invoking @kbd{M-x emms-bookmarks-add} will first pause the playback and then prompt for a name describing the bookmark. Tracks can have multiple bookmarks associated with them. To jump to the next and previous bookmarks in the current track invoke @kbd{M-x emms-bookmarks-next} and @kbd{M-x emms-bookmarks-prev} respectively. To clear all of the bookmarks for the current track invoke @kbd{M-x emms-bookmarks-clear}. @node Extending Emms @chapter Extending Emms @cindex new players @cindex defining players @cindex new players, defining Emms introduces a high abstraction layer for playing music so you can customise it to your needs. @menu * New Player:: How to define a new player. * Simple Player for `play':: An example player using @command{play}. * More Complex Player:: Example of a complex player using @command{mpg321}. @end menu @node New Player @section New Player @cindex new player @cindex defining new players The file @file{emms-player-simple.el} defines some easy players to start with, but it shouldn't be hard to provide a function for your favourite player. We will start with an easy example that shows how we can use the @command{play} command under Unix to play our WAV files. @node Simple Player for `play' @section Simple Player for `play' @cindex simple player @cindex primitive player @cindex basic player Play is a very easy command line player for various format. If you want your emms to play WAV files just put the following lines in you @file{.emacs}: @lisp (require 'emms-player-simple) (define-emms-simple-player play '(file) "\\.wav$" "play") @end lisp @noindent Huh! Wasn't that easy? The macro function @command{define-emms-simple-player} takes a minimum of three arguments. The first argument (@emph{play} in our example) defines the name of the player. It's used to name the player functions. The second is a regexp, that defines which files to play with our player. @emph{\\.wav$} matches any filename ending with a dot and the string wav. The last argument is the actual command line command we use to play our files. You can also add the path but we just assume that the command is in your path. All arguments you add to these three are optional. They define the command line arguments you want to add to your argument. If you want to hear the wav file of your favourite artist in the most possible volume use the following line: @lisp (require 'emms-player-simple) (define-emms-simple-player play '(file) "\\artist-*.wav$" "play" "--volume=100") @end lisp @noindent Please notice that you have to add the arguments as strings! The command line tool you use for @command{define-emms-simple-player} has to take one song as argument and stop after playing that particular song. For any other concept you will need to customise emms a bit more... @node More Complex Player @section More Complex Player @cindex complex player @cindex advanced player The most players you use will be simple players so you don't need to read this chapter. But if you are curious how you can use (almost) every player in emms read further... In this chapter we will use mpg321 to construct a player that actually can pause a track, restart it and show rest time. We won't implement all of that, but after that chapter you will know how to define it. The command @command{define-emms-simple-player} is just a abstraction layer for @command{define-emms-player}, which is a little bit more complicated but much more powerful! @lisp (define-emms-player "emms-mpg321-remote" :start 'emms-mpg321-remote-start :stop 'emms-mpg321-remote-stop :playablep 'emms-mpg321-remote-playable-p) @end lisp @noindent So, that is almost all! @command{define-emms-player} takes a minimum of three arguments. The first is the name of the player. The rest are methods with functions to call. Three methods are required: start, stop and playable. Start says Emms how to start a track (sic!), stop how to stop a player and playablep should return non-nil if the player can play the track. So we just need these three functions to get our mpg321-remote: First we code the start function. We will check if there's a open process and start one otherwise. Then we send a string to the process with the filename and set a filter. @lisp (defun emms-mpg321-remote-start () (unless (get-process ``mpg321-remote'') (setq emms-mpg321-remote-process (start-process "mpg321-remote-process" "*mpg321*" "mpg321" "-R" "abc")) (process-send-string "mpg321-remote-process" (concat "l " (emms-track-name track))) (set-process-filter emms-mpg321-remote-process 'emms-mpg321-remote-filter))) @end lisp @noindent We need the filter, as mpg321-remote won't quit after playing the track as the simple player do. We wait until the process sends the output ``(at-sign)P 0'' (the signal of mpg321 that the song ended) to the filter and call emms-mpg321-remote-stop. @lisp (defun emms-mpg321-remote-filter (process output) (when (string-match "(at-sign)P 0" output) (emms-mpg321-remote-stop))) @end lisp @noindent @command{emms-mpg321-remote-stop} won't do anything interesting. It just test if there are other files to play and close the process otherwise. @lisp (defun emms-mpg321-remote-stop () (unless emms-playlist (process-send-string "mpg321-remote-process" "Q\n")) @end lisp @noindent And to make that a playable example I also added @command{emms-mpg321-remote-playablep}, which I really just steal from @file{emms-player-simple.el} @lisp (defun emms-mpg321-remote-playablep (track) "Return non-nil when we can play this track." (and (eq 'file (emms-track-type track)) @end lisp @noindent Now we have a ready player and we could add commands like @command{emms-mpg321-remote-pause} for example. @node The Browser @chapter The Browser The Browser allows you to browse the metadata cache and add tracks to your playlist. It includes a powerful interactive mode. The Browser is defined in @file{emms-browser.el} and is included in the @command{emms-all} setup level. For more information about Emms setup levels see @xref{Simple Setup}. You can also manually add the Browser to your Emms setup by loading it explicitly with: @lisp (require 'emms-browser) @end lisp To be properly useful, you should do M-x @command{emms-add-directory-tree} to all the files you own at least once so that the cache is fully populated. @menu * Browser Interface:: The interactive browser interface. * Filtering Tracks:: Displaying a subset of the tracks. * Displaying Covers:: Displaying album covers in the browser interface. * Changing Looks:: Changing the tree structure, display format and faces. @end menu @node Browser Interface @section Browser Interface The browser interface allows you to display and interact with your tracks in many different ways. There are a number of ways to start the browser. @defun emms-smart-browse Display browser and playlist. Toggle between selecting browser, playlist or hiding both. Tries to behave sanely if the user has manually changed the window configuration. @end defun @defun emms-browse-by-artist Display the browser and order the tracks by artist. @end defun @defun emms-browse-by-album Display the browser and order the tracks by album. @end defun @defun emms-browse-by-genre Display the browser and order the tracks by genre. @end defun @defun emms-browse-by-year Display the browser and order the tracks by year. @end defun Once the Browser is displayed you can use it to managed your track collection and playlists. The Browser is interactive and has its own keybindings. @table @kbd @item C-j @kindex C-j (emms-browser) @findex emms-browser-add-tracks-and-play Add all tracks at point, and play the first added track. @item RET @kindex RET (emms-browser) @findex emms-browser-add-tracks Add all tracks at point. @item SPC @kindex SPC (emms-browser) @findex emms-browser-toggle-subitems Show or hide (kill) subitems under the current line. @item 1 @kindex 1 (emms-browser) @findex emms-browser-collapse-all Collapse everything. @item 2 @kindex 2 (emms-browser) @findex emms-browser-expand-to-level-2 Expand all top level items one level. @item 3 @kindex 3 (emms-browser) @findex emms-browser-expand-to-level-3 Expand all top level items two levels. @item 4 @kindex 4 (emms-browser) @findex emms-browser-expand-to-level-4 Expand all top level items three levels. @item C @kindex C (emms-browser) @findex emms-browser-clear-playlist Clear the playlist. @item E @kindex E (emms-browser) @findex emms-browser-expand-all Expand everything. @item d @kindex d (emms-browser) @findex emms-browser-view-in-dired View the current directory in dired. @item q @kindex q (emms-browser) @findex emms-browser-bury-buffer Bury the browser buffer. @item r @kindex r (emms-browser) @findex emms-browser-goto-random Jump to a random track. @item / @kindex / (emms-browser) @findex emms-isearch-buffer Isearch through the buffer. @item < @kindex < (emms-browser) @findex emms-browser-previous-filter Redisplay with the previous filter. @item > @kindex > (emms-browser) @findex emms-browser-next-filter Redisplay with the next filter. @item ? @kindex ? (emms-browser) @findex describe-mode See the Emacs documentation for the function. @item C-/ @kindex C-/ (emms-browser) @findex emms-playlist-mode-undo Undo the previous playlist action. @item @kindex (emms-browser) @findex emms-browser-add-tracks-and-play Add all tracks at point, and play the first added track. @item @kindex (emms-browser) @findex emms-browser-prev-non-track Jump to the previous non-track element. @item @kindex (emms-browser) @findex emms-browser-next-non-track Jump to the next non-track element. @item s A @kindex s A (emms-browser) @findex emms-browser-search-by-album Search the collection by album. @item s a @kindex s a (emms-browser) @findex emms-browser-search-by-artist Search the collection by artist. @item s s @kindex s s (emms-browser) @findex emms-browser-search-by-names Search the collection by names. @item s t @kindex s t (emms-browser) @findex emms-browser-search-by-title Search the collection by title. @item b 1 @kindex b 1 (emms-browser) @findex emms-browse-by-artist Browse the collection by artist. @item b 2 @kindex b 2 (emms-browser) @findex emms-browse-by-album Browse the collection by album. @item b 3 @kindex b 3 (emms-browser) @findex emms-browse-by-genre Browse the collection by genre. @item b 4 @kindex b 4 (emms-browser) @findex emms-browse-by-year Browse the collection by year. @item W a p @kindex W a p (emms-browser) @findex emms-browser-lookup-album-on-pitchfork Lookup the album using Pitchfork. @item W a w @kindex W a w (emms-browser) @findex emms-browser-lookup-album-on-wikipedia Lookup the album using Wikipedia. @end table @node Filtering Tracks @section Filtering Tracks If you want to display a subset of your collection (such as a directory of 80s music, only avi files, etc.) then you can extend the Browser by defining ``filters''. Show everything: @lisp (emms-browser-make-filter "all" 'ignore) @end lisp Set "all" as the default filter: @lisp (emms-browser-set-filter (assoc "all" emms-browser-filters)) @end lisp Show all files (no streamlists, etc): @lisp (emms-browser-make-filter "all-files" (emms-browser-filter-only-type 'file)) @end lisp Show only tracks in one folder: @lisp (emms-browser-make-filter "80s" (emms-browser-filter-only-dir "~/Mp3s/80s")) @end lisp Show all tracks played in the last month: @lisp (emms-browser-make-filter "last-month" (emms-browser-filter-only-recent 30)) @end lisp After executing the above commands, you can use M-x emms-browser-show-all, emms-browser-show-80s, etc to toggle between different collections. Alternatively you can use '<' and '>' to cycle through the available filters. The second argument to make-filter is a function which returns t if a single track should be filtered. You can write your own filter functions to check the type of a file, etc. Show only tracks not played in the last year: @lisp (emms-browser-make-filter "not-played" (lambda (track) (not (funcall (emms-browser-filter-only-recent 365) track)))) @end lisp Show all files that are not in the pending directory: @lisp (emms-browser-make-filter "all" (lambda (track) (or (funcall (emms-browser-filter-only-type 'file) track) (not (funcall (emms-browser-filter-only-dir "~/Media/pending") track))))) @end lisp @node Displaying Covers @section Displaying Covers The browser will attempt to display cover images if they're available. By default it looks for images cover_small.jpg, cover_med.jpg, etc. Customize @var{emms-browser-covers} to use your own covers. Note that you'll probably want to resize your existing covers to particular sizes. Suggested sizes are 100x100 for small, and 200x200 for medium. Also, Emacs by default will jump around a lot when scrolling a buffer with images. In order to prevent that, you can set @var{scroll-up-aggressively} and @var{scroll-down-aggressively} to the number ``0.0''. To show a 'no cover' image for albums which don't have a cover, add the following code to your .emacs: @lisp (setq emms-browser-default-covers (list "/path/to/cover_small.jpg" nil nil) @end lisp The medium and large images can be set as well. You can download an example @uref{http://repose.cx/cover_small.jpg, `no cover' image}. @node Changing Looks @section Changing Looks The Browser's look can be customised. You can change the way the tree structure looks, the display format and display faces. @subheading Changing Tree Structure You can change the way the tree is displayed by modifying the function @command{emms-browser-next-mapping-type}. The following code displays artist->track instead of artist->album->track when you switch to the 'singles' filter: @lisp (defadvice emms-browser-next-mapping-type (after no-album (current-mapping)) (when (eq ad-return-value 'info-album) (setq ad-return-value 'info-title))) @end lisp @lisp (defun toggle-album-display () (if (string= emms-browser-current-filter-name "singles") (ad-activate 'emms-browser-next-mapping-type) (ad-deactivate 'emms-browser-next-mapping-type))) (add-hook 'emms-browser-filter-changed-hook 'toggle-album-display) @end lisp @subheading Changing Display Format Format strings govern the way items are displayed in the browser and playlist. You can customize these if you wish. @var{emms-browser-default-format} controls the format to use when no other format has been explicitly defined. By default, only track and albums deviate from the default. To customise the format of a particular type, find the name of the field you want to use (eg `info-artist', `info-title', etc), and insert that into emms-browser--format or emms-browser-playlist--format. For example, if you wanted to remove track numbers from tracks in both the browser and playlist, you could do: @lisp (defvar emms-browser-info-title-format "%i%n") (defvar emms-browser-playlist-info-title-format emms-browser-info-title-format) @end lisp The format specifiers available include: @itemize @w{} @item %i indent relative to the current level @item %n the value of the item - eg -info-artist might be ``pink floyd'' @item %y the album year @item %A the album name @item %a the artist name of the track @item %t the title of the track @item %T the track number @item %cS a small album cover @item %cM a medium album cover @item %cL a big album cover @end itemize Note that if you use track-related items like %t, it will take the data from the first track. @subheading Changing Display Faces The faces used to display the various fields are also customizable. They are in the format emms-browser--face, where type is one of "year/genre", "artist", "album" or "track". Note that faces lack the initial "info-" part. For example, to change the artist face, type M-x @command{customize-face} @command{emms-browser-artist-face}. @node Sorting Playlists @chapter Sorting Playlists @cindex sort @cindex track order The `emms-playlist-sort' module, defined in the @file{emms-playlist-sort.el} package provides functions for sorting Emms playlists. `emms-playlist-sort' can be loaded by invoking: @lisp (require 'emms-playlist-sort) @end lisp @defun emms-playlist-sort-by-name Sort playlist by name in ascending order. @end defun @defun emms-playlist-sort-by-info-artist Sort playlist by artist in ascending order. @end defun @defun emms-playlist-sort-by-info-title Sort playlist by title in ascending order. @end defun @defun emms-playlist-sort-by-info-album Sort playlist by album in ascending order. @end defun @defun emms-playlist-sort-by-info-year Sort playlist by year in ascending order. @end defun @defun emms-playlist-sort-by-info-note Sort playlist by notes in ascending order. @end defun @node Persistent Playlists @chapter Persistent Playlists The Emms module @file{emms-history.el} makes playlists persistent over emacs sessions. To make use of this feature put this into your ~/.emacs. @lisp (require 'emms-history) @end lisp When you kill emacs all playlists will be saved in the file given by the variable: @defopt emms-history-file The file to save playlists in. It defaults to "~/.emacs.d/emms-history". @end defopt After you started up emacs again, you can restore all saved playlists with this function. @defun emms-history-load Restore all playlists in `emms-history-file'. @end defun If that should be done automatically on each startup, put these lines into your ~/.emacs. @lisp (require 'emms-history) (emms-history-load) @end lisp Normally @code{emms-history} only restores playlists. If you want it to start playback afterwards, you can tweak this variable. @defopt emms-history-start-playing If non-nil emms starts playing the current track after `emms-history-load' was invoked. The default value is nil. @end defopt @node Editing Tracks @chapter Editing Tracks @cindex track editor Using @file{emms-tag-editor.el}, emms can set tag informations of tracks and write them back to the file with the help of external programs, such as `mp3info', `vorbiscomment'. Use the keybinding @kbd{E} to edit the tags of track under point in the playlist or all marked tracks (@pxref{Markable Playlists} for how to mark tracks). The track's tag informations are listed in a special buffer `*EMMS-TAGS*' in text format. Field names are marked in bold face and are not editable. Any tag information is placed behind an equal sign and is changable. A special field `name' is the track's file name. If any change is made in this field, the track's file will be renamed to the new name. When you finished editing the tag infos use @kbd{C-c C-c} (which calls @code{emms-tag-editor-submit-and-exit}) to submit the changes and close the `*EMMS-TAGS*' buffer. There are a few commands to perform changes on all tracks. @defun emms-tag-editor-set-all tag value Set TAG to VALUE in all tracks. If transient-mark-mode is turned on, you can apply the command to a selected region. If `transient-mark-mode' is on and the mark is active, the changes will only take effect on the tracks in the region. @end defun @defun emms-tag-editor-replace-in-tag tag from to Query and replace text in selected TAG. For example, if the info-title tag is selected, then only perform replacement in title tags. If `transient-mark-mode' is on and the mark is active, the changes will only take effect on the tracks in the region. @end defun @defun emms-tag-editor-transpose-tag tag1 tag2 Transpose value of TAG1 and TAG2. If `transient-mark-mode' is on and the mark is active, the changes will only take effect on the tracks in the region. @end defun @defun emms-tag-editor-submit arg Make modified tags take affect. With prefix argument, bury the tag edit buffer. @end defun If you want to extend the tag editor to work with file formats other than `mp3' and `ogg', have a look at these variables. @defvr {Variable} emms-tag-editor-formats This variable determine how to insert track fields to `emms-tag-editor-edit-buffer'. Emms tag info editable fields is usually determined by the extension of track name. The variable `emms-tag-editor-tags' contains all tags that emms track may have. A single charactar is assigned to the tag to make the `emms-tag-editor-formats' easier to generate. @end defvr @defvr {Variable} emms-tag-editor-tagfile-functions To write tags to track file, an extern program should specified in this variable. If the external program has an interface like `mp3info', you don't have to write a function. Take `mp3' and `ogg' as example. @end defvr @heading Renaming Files The tag editor is also capable to rename the file of the track at point or all files of the marked tracks according to the value this variable. @defopt emms-tag-editor-rename-format When `emms-tag-editor-rename' is invoked the track's file will be renamed according this format specification. The file extension will be added automatically. It uses the format specs defined in @code{emms-tag-editor-tags}. The default value is "%a - %l - %n - %t", so that files are named - - - .<extension> after renaming. @end defopt To perform the renaming put point on the track you want to rename or mark some tracks. Then hit @kbd{R} which calls this function: @defun emms-tag-editor-rename Rename the file corresponding to track at point or all marked tracks according to the value of @code{emms-tag-editor-rename-format}. @end defun @node Emms Mode Line @chapter Emms Mode Line @cindex mode line @cindex display emms information We can display information about the currenty playing track on the Emacs mode line using the package `emms-mode-line' which is provided by the file @file{emms-mode-line.el}. To activate this feature invoke: @lisp (require 'emms-mode-line) (emms-mode-line 1) @end lisp It is also possible to display the amount of time a track has been playing. This feature is defined in the `emms-playing-time' package which is provided by the file @file{emms-playing-time.el}. To use this feature invoke: @lisp (require 'emms-playing-time) (emms-playing-time 1) @end lisp Note: `(emms-playing-time -1)' will disable emms-playing-time module completely, and is not recommended. (since some other emms modules may rely on it, such as `emms-lastfm.el') Instead, to toggle displaying playing time on mode line, one could call `emms-playing-time-enable-display' and `emms-playing-time-disable-display'." @defun emms-playing-time-enable-display Display playing time on mode line. @end defun @defun emms-playing-time-disable-display Remove playing time from mode line. @end defun @node Music Player Daemon @chapter Music Player Daemon @cindex music player daemon @cindex remote interface @cindex mpd Emms provides an interface to the @uref{http://www.musicpd.org/, Music Player Daemon}(MusicPD) software. The package is called `emms-player-mpd' and is provided by the file @file{emms-player-mpd.el}. The advantages of using MusicPD as an EMMS backend include the following. @itemize @bullet @item minimal CPU usage @item fast access of track information @item optional crossfade @end itemize @subheading Setup To load `emms-player-mpd' invoke: @lisp (require 'emms-player-mpd) @end lisp Set the variables @var{emms-player-mpd-server-name} and @var{emms-player-mpd-server-port} to the location and port (respectively) of your MusicPD server. For example: @lisp (setq emms-player-mpd-server-name "localhost") (setq emms-player-mpd-server-port "6600") @end lisp If your MusicPD setup requires a password, you will to set @var{emms-player-mpd-server-password} as follows. @lisp (setq emms-player-mpd-server-password "mypassword") @end lisp To get track information from MusicPD, invoke the following: @lisp (add-to-list 'emms-info-functions 'emms-info-mpd) @end lisp Adding `emms-player-mpd' to your Emms player list is accomplished by invoking: @lisp (add-to-list 'emms-player-list 'emms-player-mpd) @end lisp If you use absolute file names in your m3u playlists (which is most likely), make sure you set @var{emms-player-mpd-music-directory} to the value of "music_directory" from your MusicPD config. There are additional options available as well, but the defaults should be sufficient for most uses. You can set @var{emms-player-mpd-sync-playlist} to nil if your master EMMS playlist contains only stored playlists. @subheading Commands provided @defun emms-player-mpd-connect Connect to MusicPD and retrieve its current playlist. Afterward, the status of MusicPD will be tracked. @end defun @defun emms-player-mpd-disconnect Terminate the MusicPD client process and disconnect from MusicPD. @end defun @defun emms-player-mpd-show &optional insertp Describe the current EMMS track in the minibuffer. If INSERTP is non-nil, insert the description into the current buffer instead. This function uses @var{emms-show-format} to format the current track. It differs from @command{emms-show} in that it asks MusicPD for the current track, rather than Emms. @end defun @subsubheading Updating the MusicPD database @defun emms-player-mpd-update-directory dir Cause the tracks in DIR to be updated in the MusicPD database. @end defun @defun emms-player-mpd-update-all Cause all tracks in the MusicPD music directory to be updated in the MusicPD database. @end defun @subsubheading emms-cache.el integration @defun emms-cache-set-from-mpd-directory dir Dump all MusicPD data from DIR into the EMMS cache. This is useful to do when you have recently acquired new music. @end defun @defun emms-cache-set-from-mpd-all Dump all MusicPD data into the EMMS cache. This is useful to do once, just before using emms-browser.el, in order to prime the cache. @end defun @subsubheading emms-volume.el integration To activate this, add the following to your .emacs. @lisp (require 'emms-volume) (setq emms-volume-change-function 'emms-volume-mpd-change) @end lisp @node Lyrics @chapter Lyrics @cindex lyrics We can display the lyrics of a song in time with the music using the `emms-lyrics' package provided by the file @file{emms-lyrics.el}. The lyrics files should have the extention ``.lrc'', and can be placed under either the same directory as the music files or @var{emms-lyrics-dir}. To add this feature we invoke: @lisp (require 'emms-lyrics) (emms-lyrics 1) @end lisp There are a number of variables we can set to define the way that `emms-lyrics' behaves, we can set these directly or by using the Customize feature in Emacs. @defvr {User Option} emms-lyrics-display-on-minibuffer If non-nil, display lyrics on minibuffer. @end defvr @defvr {User Option} emms-lyrics-display-on-modeline If non-nil, display lyrics on modeline. @end defvr @defvr {User Option} emms-lyrics-dir Local lyrics repository. @command{emms-lyrics-find-lyric} will look for lyrics in current directory(i.e., same as the music file) and this directory. @end defvr @defvr {User Option} emms-lyrics-display-format Format for displaying lyrics. "%s" will be replaced by the lyrics string. @end defvr @defvr {User Option} emms-lyrics-coding-system Coding system used in the output of lyrics. @end defvr @defvr {User Option} emms-lyrics-scroll-p Non-nil value will enable lyrics scrolling. @end defvr @defvr {User Option} emms-lyrics-scroll-timer-interval Interval between scroller timers. The shorter, the faster. @end defvr We can control `emms-lyrics' with the help of the following functions: @defun emms-lyrics-start Start displaying lyrics. @end defun @defun emms-lyrics-stop Stop displaying lyrics. @end defun @defun emms-lyrics-toggle-display-on-minibuffer Toggle display lyrics on minibufer. @end defun @defun emms-lyrics-toggle-display-on-modeline Toggle display lyrics on mode line. @end defun @defun emms-lyrics-enable Enable displaying Emms lyrics. @end defun @defun emms-lyrics-disable Disable displaying Emms lyrics. @end defun @defun emms-lyrics-toggle Toggle displaying Emms lyrics. @end defun @node Volume @chapter Volume @cindex volume We can use the `emms-volume' package, as provided by the @file{emms-volume.el} file, to manipulate the volume. @defopt emms-volume-change-amount The amount to use when raising or lowering the volume using the emms-volume interface. This should be a positive integer. @end defopt @defun emms-volume-raise Increase the volume. @end defun @defun emms-volume-lower Decrease the volume. @end defun If you feel like binding those two functions to global keys --- don't do it or you'll miss the convenience of `emms-volume-minor-mode'. Instead, bind the following two commands to some keys that you like. @defun emms-volume-mode-plus Raise volume and enable or extend the `emms-volume-minor-mode' timeout. @end defun @defun emms-volume-mode-minus Lower volume and enable or extend the `emms-volume-minor-mode' timeout. @end defun Example: @lisp (global-set-key (kbd "C-c +") 'emms-volume-mode-plus) (global-set-key (kbd "C-c -") 'emms-volume-mode-minus) @end lisp Whenever you use one of these keys or call these functions with @kbd{M-x}, Emms will be put into `emms-volume-minor-mode' for a short period defined by `emms-volume-mode-timeout'. @defopt emms-volume-mode-timeout The timeout in amount of seconds used by `emms-volume-minor-mode'. @end defopt In this interval you can raise/lower the volume simply by pressing @kbd{+} or @kbd{-}, which will also reset the timer to its initial value. So instead of pressing @kbd{C-c +} six times to increase volume by six steps of @code{emms-volume-change-amount}, you would simply type @kbd{C-c + + + + + +}. @node Last.fm @chapter Last.fm @cindex last.fm Last.fm is a popular commercial music streaming service. Last.fm allows a subscriber to listen to streaming music. Last.fm can also accept data as to what music is played locally. This information can be used by the Last.fm service. As usual the responsibility is on the user to decide how much information to share with third-parties. The emms-lastfm-client package, provided the file @file{emms-lastfm-client.el} provides native Last.fm support from within Emms. Emms does not provide a subscription to the Last.fm service, nor is it affiliated with the service in any way. There are restrictions on the use of this service. Quoting from @uref{http://www.last.fm/api/radio}: ``Who can I stream radio to? Any API account can only stream radio to Last.fm's paid subscribers''. @menu * Last.fm Setup:: Configuring Emms to use Last.fm. * Last.fm Radio:: Listening to music through Last.fm * Last.fm Audioscrobbler:: Submitting music to Last.fm @end menu @node Last.fm Setup @section Last.fm Setup We've spoken to representatives from Last.fm and arrived at the following agreement: In order to be able to use the service while preserving the essential freedoms of the GPL each client must apply for their own API key from Last.fm. Here are the steps for getting authorization from Last.fm to stream music. Thankfully this only needs to be done once: @enumerate @item Complete steps 1 and 2 from @uref{http://www.last.fm/api/authentication} to get an API key and a secret key. Set the variables @var{emms-lastfm-client-api-key} and @var{emms-lastfm-client-api-secret-key} respectively. @item Invoke @kbd{M-x emms-lastfm-client-user-authorization}. On successful completion a browser window will open asking for confirmation to allow this application access to your Last.fm account. Confirm and close the browser. @item Invoke @kbd{M-x emms-lastfm-client-get-session}. On successful completion your permanent session key will be stored in @var{emms-lastfm-client-session-key-file}. As long as this value is accessible the authentication process need not be repeated. @end enumerate After successfully completing the above Emms should be authorized to access your Last.fm account. @node Last.fm Radio @section Last.fm Radio To show the currently streaming track invoke: @kbd{M-x emms-lastfm-client-show}. To display information and a photo of currently streaming artist: @kbd{M-x emms-lastfm-client-info}. There are three ratings you can submit while streaming audio from Last.fm: ``love''-ing a track, skipping to the next song (simply skipping also counts as a form of ``scrobbing'') and ``ban''-ing (which also skips). @kbd{M-x emms-lastfm-client-love-track}: ``love'' the currently streaming track. @kbd{M-x emms-lastfm-client-track-advance}: Skip to the next streaming track. @kbd{M-x emms-lastfm-client-ban-track}: ``ban'' the currently streaming track. Note that Last.fm streams cannot be paused or replayed. Doing so may cause Last.fm to suspend your account. There are a number of stations you can tune into: @kbd{M-x emms-lastfm-client-play-similar-artists}: Play Similar-Artists stream. You will be prompted to enter the name of the artist. The input will auto-complete from the Emms cache. If an artist is not in the Emms cache and has a name with spaces, use @kbd{C-q Space} to enter literal spaces. There are personal streams you can tune into: (at the time of writing these are in a state of flux as Last.fm depreciate old stations and bring in new ones; If a station which used to work doesn't anymore that is probably the reason. As Last.fm add and remove stations emms-lastfm-client will be updated accordingly) @kbd{M-x emms-lastfm-client-play-library}: Your Last.fm Library. @kbd{M-x emms-lastfm-client-play-loved}: Your ``loved'' tracks. @kbd{M-x emms-lastfm-client-play-recommended}: Your ``recommended'' tracks. @kbd{M-x emms-lastfm-client-play-mix}: Your ``mix'' radio. @kbd{M-x emms-lastfm-client-play-neighborhood}: Your ``neighborhood''. You can use similar commands to tune into other people's streams. For each of these commands you will be prompted for the Last.fm username of the person whose radio you wish to hear. @kbd{M-x emms-lastfm-client-play-user-library}: A Last.fm user's Library. @kbd{M-x emms-lastfm-client-play-user-loved}: A Last.fm user's ``loved'' tracks. @kbd{M-x emms-lastfm-client-play-user-neighborhood}: A Last.fm user's ``neighborhood''. @node Last.fm Audioscrobbler @section Last.fm Audioscrobbler Emms can submit the tracks you play to your Last.fm profile. Assuming you have obtained a Last.fm api key, as explained in the chapter @xref{Last.fm Setup}, all the audioscrobbler needs is your username in @var{emms-lastfm-client-username}. You can enter it with @kbd{M-x customize-group RET emms-lastfm}. @kbd{M-x emms-lastfm-scrobbler-enable} turns on audioscrobbling. To turn it off use @kbd{M-x emms-lastfm-scrobbler-disable}. To turn on Emms' audioscrobber in your .emacs file add: @lisp (require 'emms-lastfm-client) (setq emms-lastfm-client-username "your-lastfm-username") (setq emms-lastfm-client-api-key "your-lastfm-api-key") (setq emms-lastfm-client-api-secret-key "your-lastfm-api-secret-key") (emms-lastfm-scrobbler-enable) @end lisp @node Streaming Audio @chapter Streaming Audio @cindex streaming audio @cindex internet radio Emms provides a friendly interface for managing and playing streaming audio in addition to the Emms playlist interface. The interface is defined in the @file{emms-streams.el} package and can be loaded by invoking: @lisp (require 'emms-streams) @end lisp The Emms interface for streaming audio is enabled by default in the `emms-all' and `emms-devel' setup levels. For more information about Emms setup levels see @xref{Simple Setup}. Enter the emms-streams interface by invoking @kbd{M-x} @command{emms-streams}. The emms-streams interface comes with a built-in, eclectic list of streaming audio channels from throughout the Web. Emms can of-course play other streams than the ones listed by default, you are free to remove any or all of them and add your own.@footnote{If you enjoy a particular streaming audio station on the Web and think that it belongs in the default list, please send us a link and we will gladly add it!} If you want to play Last.fm streams, invoke the following and use the ``lastfm'' type when adding a bookmark to a Last.fm stream. @lisp (require 'emms-lastfm) @end lisp The following is a list of the key-bindings for the emms-streams interface: @table @kbd @item RET @kindex RET (emms-streams) @vindex emms-stream-default-action Perform the default action when you press RET in the Emms Stream interface. Can be either ``add'' or ``play''. The default is ``add'', which adds the station under point to the Emms playlist. When @var{emms-stream-default-action} is ``play'' then Emms will play the streaming audio channel under point. @item q @kindex q (emms-streams) @findex emms-stream-quit Quit the emms-streams interface. @item a @kindex a (emms-streams) @findex emms-stream-add-bookmark Add a bookmark to a streaming audio URL to the list. @item d @kindex d (emms-streams) @findex emms-stream-delete-bookmark Remove a bookmark to a streaming audio URL from the list. @item e @kindex e (emms-streams) @findex emms-stream-edit-bookmark Edit the details of the bookmark under point. @item h @kindex h (emms-streams) @findex describe-mode Describe the emms-streams mode. @item n @kindex n (emms-streams) @findex emms-stream-next-line Move to the next line in the emms-streams buffer (same as C-n). @item p @kindex p (emms-streams) @findex emms-stream-previous-line Move to the previous line in the emms-streams buffer (same as C-p). @item s @kindex s (emms-streams) @findex emms-stream-save-bookmarks-file Save the bookmarks in the emms-streams interface to disk. The bookmarks will be to the location designated in the variable @var{emms-stream-bookmarks-file}. @item i @kindex i (emms-streams) @findex emms-stream-info-bookmark Return information about the streaming audio at the URL of the bookmark under point. Note that this will only work if the `emms-stream-info' has already been loaded. @end table @c including the relevant licenses @include gpl.texi @include fdl.texi @node Concept Index @unnumbered Concept Index @printindex cp @node Function Index @unnumbered Function Index @printindex fn @node Variable Index @unnumbered Variable Index @printindex vr @node Keybinding Index @unnumbered Keybinding Index @printindex ky @bye �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/doc/fdl.texi����������������������������������������������������0000664�0000000�0000000�00000051006�12205151475�0020271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@node The GNU FDL, Concept Index, Copying, Top @chapter GNU Free Documentation License @cindex FDL, GNU Free Documentation License @center Version 1.2, November 2002 @display Copyright @copyright{} 2000,2001,2002 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @end display @enumerate 0 @item PREAMBLE The purpose of this License is to make a manual, textbook, or other functional and useful document @dfn{free} in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of ``copyleft'', which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference. @item APPLICABILITY AND DEFINITIONS This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The ``Document'', below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as ``you''. You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law. A ``Modified Version'' of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language. A ``Secondary Section'' is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them. The ``Invariant Sections'' are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none. The ``Cover Texts'' are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words. A ``Transparent'' copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not ``Transparent'' is called ``Opaque''. Examples of suitable formats for Transparent copies include plain @sc{ascii} without markup, Texinfo input format, La@TeX{} input format, @acronym{SGML} or @acronym{XML} using a publicly available @acronym{DTD}, and standard-conforming simple @acronym{HTML}, PostScript or @acronym{PDF} designed for human modification. Examples of transparent image formats include @acronym{PNG}, @acronym{XCF} and @acronym{JPG}. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, @acronym{SGML} or @acronym{XML} for which the @acronym{DTD} and/or processing tools are not generally available, and the machine-generated @acronym{HTML}, PostScript or @acronym{PDF} produced by some word processors for output purposes only. The ``Title Page'' means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, ``Title Page'' means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text. A section ``Entitled XYZ'' means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as ``Acknowledgements'', ``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' of such a section when you modify the Document means that it remains a section ``Entitled XYZ'' according to this definition. The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License. @item VERBATIM COPYING You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies. @item COPYING IN QUANTITY If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document. @item MODIFICATIONS You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version: @enumerate A @item Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. @item List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement. @item State on the Title page the name of the publisher of the Modified Version, as the publisher. @item Preserve all the copyright notices of the Document. @item Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. @item Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. @item Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. @item Include an unaltered copy of this License. @item Preserve the section Entitled ``History'', Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled ``History'' in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. @item Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the ``History'' section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. @item For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. @item Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. @item Delete any section Entitled ``Endorsements''. Such a section may not be included in the Modified Version. @item Do not retitle any existing section to be Entitled ``Endorsements'' or to conflict in title with any Invariant Section. @item Preserve any Warranty Disclaimers. @end enumerate If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles. You may add a section Entitled ``Endorsements'', provided it contains nothing but endorsements of your Modified Version by various parties---for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version. @item COMBINING DOCUMENTS You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections Entitled ``History'' in the various original documents, forming one section Entitled ``History''; likewise combine any sections Entitled ``Acknowledgements'', and any sections Entitled ``Dedications''. You must delete all sections Entitled ``Endorsements.'' @item COLLECTIONS OF DOCUMENTS You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document. @item AGGREGATION WITH INDEPENDENT WORKS A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an ``aggregate'' if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate. @item TRANSLATION Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail. If a section in the Document is Entitled ``Acknowledgements'', ``Dedications'', or ``History'', the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title. @item TERMINATION You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. @item FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation 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. See @uref{http://www.gnu.org/copyleft/}. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License ``or any later version'' applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. @end enumerate @page @section ADDENDUM: How to use this License for your documents To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page: @smallexample @group Copyright (C) @var{year} @var{your name}. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''. @end group @end smallexample If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the ``with...Texts.'' line with this: @smallexample @group with the Invariant Sections being @var{list their titles}, with the Front-Cover Texts being @var{list}, and with the Back-Cover Texts being @var{list}. @end group @end smallexample If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation. If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software. @c Local Variables: @c ispell-local-pdict: "ispell-dict" @c End: ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/doc/gpl.texi����������������������������������������������������0000664�0000000�0000000�00000104652�12205151475�0020314�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������@node Copying, The GNU FDL, Extending Emms, Top @unnumbered GNU General Public License @center Version 3, 29 June 2007 @c This file is intended to be included in another file. @display Copyright @copyright{} 2007 Free Software Foundation, Inc. @url{http://fsf.org/} Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @end display @unnumberedsec 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. @iftex @unnumberedsec TERMS AND CONDITIONS @end iftex @ifinfo @center TERMS AND CONDITIONS @end ifinfo @enumerate 0 @item 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. @item 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. @item 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. @item 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. @item 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. @item 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: @enumerate a @item The work must carry prominent notices stating that you modified it, and giving a relevant date. @item 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''. @item 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. @item 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. @end enumerate 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. @item 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: @enumerate a @item 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. @item 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. @item 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. @item 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. @item 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. @end enumerate 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. @item 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: @enumerate a @item Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or @item 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 @item 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 @item Limiting the use for publicity purposes of names of licensors or authors of the material; or @item Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or @item 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. @end enumerate 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. @item 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. @item 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. @item 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. @item 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. @item 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. @item 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. @item 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. @item 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. @item 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. @item 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 enumerate @iftex @heading END OF TERMS AND CONDITIONS @end iftex @ifinfo @center END OF TERMS AND CONDITIONS @end ifinfo @unnumberedsec 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. @smallexample @var{one line to give the program's name and a brief idea of what it does.} Copyright (C) @var{year} @var{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 @url{http://www.gnu.org/licenses/}. @end smallexample 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: @smallexample @var{program} Copyright (C) @var{year} @var{name of author} This program comes with ABSOLUTELY NO WARRANTY; for details type @samp{show w}. This is free software, and you are welcome to redistribute it under certain conditions; type @samp{show c} for details. @end smallexample The hypothetical commands @samp{show w} and @samp{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 @url{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 @url{http://www.gnu.org/philosophy/why-not-lgpl.html}. ��������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/emms-print-metadata.1�������������������������������������������0000664�0000000�0000000�00000002312�12205151475�0022013�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH EMMS-PRINT-METADATA 1 "April 29, 2006" EMMS .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp <n> insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME emms-print-metadata \- Print information about music files .SH SYNOPSIS .B emms-print-metadata .RI file.ext .br .SH DESCRIPTION .\" TeX users may be more comfortable with the \fB<whatever>\fP and .\" \fI<whatever>\fP escape sequences to invoke bold face and italics, .\" respectively. \fBemms-print-metadata\fP will print metadata about music files to stdout, to be used primarily by EMMS, the Emacs MultiMedia System. .SH SEE ALSO .BR emms (info) .br .SH AUTHOR emms-print-metadata was written by Trent Buck. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/�����������������������������������������������������������0000775�0000000�0000000�00000000000�12205151475�0017031�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/Makefile���������������������������������������������������0000664�0000000�0000000�00000001176�12205151475�0020476�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������EMACS=emacs SITEFLAG=--no-site-file ALLSOURCE=$(wildcard *.el) ALLCOMPILED=$(wildcard *.elc) SPECIAL=emms-auto.el emms-maint.el SOURCE=$(filter-out $(SPECIAL),$(ALLSOURCE)) TARGET=$(patsubst %.el,%.elc,$(SOURCE)) .PHONY: all clean .PRECIOUS: %.elc all: emms-auto.el $(TARGET) emms-auto.el: emms-auto.in $(SOURCE) cp emms-auto.in emms-auto.el -rm -f emms-auto.elc @$(EMACS) -q $(SITEFLAG) -batch \ -l emms-maint.el \ -l emms-auto.el \ -f emms-generate-autoloads \ $(shell pwd)/emms-auto.el . %.elc: %.el @$(EMACS) -q $(SITEFLAG) -batch \ -l emms-maint.el \ -f batch-byte-compile $< clean: -rm -f *~ *.elc emms-auto.el ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-auto.in�����������������������������������������������0000664�0000000�0000000�00000000602�12205151475�0021266�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; -*-emacs-lisp-*- (defvar generated-autoload-file) (defvar command-line-args-left) (defun emms-generate-autoloads () (interactive) (require 'autoload) (setq generated-autoload-file (car command-line-args-left)) (setq command-line-args-left (cdr command-line-args-left)) (batch-update-autoloads)) (provide 'emms-auto) ;;; Generated autoloads follow (made by autoload.el). ������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-bookmarks.el������������������������������������������0000664�0000000�0000000�00000012434�12205151475�0022306�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-bookmarks.el --- Bookmarks for Emms. ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Yoni Rabkin <yonirabkin@member.fsf.org> ;; Keywords: emms, bookmark ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;; ;; You can use this to add ``temporal bookmarks'' (term by Lucas ;; Bonnet) to your media files. The interesting functions here are ;; `emms-bookmarks-next', `emms-bookmarks-prev', `emms-bookmarks-add' ;; (which pauses the player while you describe the bookmark) and ;; `emms-bookmarks-clear'. All of which do exactly what you think ;; they do. ;;; Code: ;; dependencies (require 'emms) (require 'emms-playing-time) (defvar emms-bookmarks-prev-overshoot 5 "Time in seconds for skipping a previous bookmark.") (defun emms-bookmarks-reset (track) "Remove all the bookmarks from TRACK." (emms-track-set track 'bookmarks nil)) (defun emms-bookmarks-straight-insertion-sort (item l acc) "Insert ITEM into the already sorted L, ACC should be nil." (if (null l) (append acc (list item)) (cond ((< (cdr item) (cdr (car l))) (append acc (list item (car l)) (cdr l))) (t (emms-bookmarks-straight-insertion-sort item (cdr l) (append acc (list (car l)))))))) (defun emms-bookmarks-get (track) "Return the bookmark property from TRACK." (emms-track-get track 'bookmarks)) (defun emms-bookmarks-set (track desc time) "Set bookmark property for TRACK, text DESC at TIME seconds." (let ((old-bookmarks (emms-track-get track 'bookmarks)) (new-bookmarks nil)) (setq new-bookmarks (emms-bookmarks-straight-insertion-sort (cons desc time) old-bookmarks nil)) (emms-track-set track 'bookmarks new-bookmarks))) (defun emms-bookmarks-set-current (desc) "Set bookmark property for the current track with text DESC." (emms-bookmarks-set (emms-playlist-current-selected-track) desc emms-playing-time)) (defun emms-bookmarks-search (time track test) "Return a bookmark based on heuristics. TIME should be a reference point in seconds. TRACK should be an Emms track. TEST should be a numerical comparator predicate." (let ((s (append (list (cons "time" time)) (copy-sequence (emms-bookmarks-get track))))) (sort s #'(lambda (a b) (funcall test (cdr a) (cdr b)))) (while (not (= time (cdar s))) (setq s (cdr s))) (when (cdr s) (car (cdr s))))) (defun emms-bookmarks-next-1 (time track) "Return the bookmark after TIME for TRACK, otherwise return nil." (emms-bookmarks-search time track #'<)) (defun emms-bookmarks-prev-1 (time track) "Return the bookmark before TIME for TRACK, otherwise return nil." (emms-bookmarks-search (- time emms-bookmarks-prev-overshoot) track #'>)) (defun emms-bookmarks-goto (search-f track failure-message) "Seek the player to a bookmark. SEARCH-F should be a function which returns a bookmark. TRACK should be an Emms track. FAILURE-MESSAGE should be a string." ;; note that when emms is paused then `emms-player-playing-p' => t (when (not emms-player-playing-p) (emms-start)) (let ((m (funcall search-f emms-playing-time track))) (if m (progn (emms-player-seek-to (cdr m)) (message "%s" (car m))) (message "%s" failure-message)))) ;; entry points (defun emms-bookmarks-next () "Seek to the next bookmark in the current track." (interactive) (emms-bookmarks-goto #'emms-bookmarks-next-1 (emms-playlist-current-selected-track) "No next bookmark")) (defun emms-bookmarks-prev () "Seek to the previous bookmark in the current track." (interactive) (emms-bookmarks-goto #'emms-bookmarks-prev-1 (emms-playlist-current-selected-track) "No previous bookmark")) (defmacro emms-bookmarks-with-paused-player (&rest body) "Eval BODY with player paused." `(progn (when (not emms-player-paused-p) (emms-pause)) ,@body (when emms-player-paused-p (emms-pause)))) ;; can't use `interactive' to promt the user here because we want to ;; pause the player before the prompt appears. (defun emms-bookmarks-add () "Add a new bookmark to the current track. This function pauses the player while prompting the user for a description of the bookmark. The function resumes the player after the prompt." (interactive) (emms-bookmarks-with-paused-player (let ((desc (read-string "Description: "))) (if (emms-playlist-current-selected-track) (emms-bookmarks-set-current desc) (error "No current track to bookmark"))))) (defun emms-bookmarks-clear () "Remove all the bookmarks from the current track." (interactive) (let ((this (emms-playlist-current-selected-track))) (when this (emms-bookmarks-reset this)))) (provide 'emms-bookmarks) ;;; emms-bookmarks.el ends here ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-browser.el��������������������������������������������0000664�0000000�0000000�00000212444�12205151475�0022004�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-browser.el --- a track browser supporting covers and filtering ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Damien Elmes <emacs@repose.cx> ;; Keywords: emms, mp3, mpeg, multimedia ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; This code allows you to browse the metadata cache and add tracks to ;; your playlist. To be properly useful, you should M-x ;; emms-add-directory-tree to all the files you own at least once so ;; that the cache is fully populated. ;; Usage ;; ------------------------------------------------------------------- ;; To use, run (emms-all) and then bind `emms-smart-browse' to a key, ;; like: ;; (global-set-key (kbd "<f2>") 'emms-smart-browse) ;; The 'smart browsing' code attempts to link the browser and playlist ;; windows together, so that closing one will close both. Activating ;; it will toggle between three states: ;; a) both windows displayed, with the browser focused ;; b) focus switched to the playlist window ;; c) the extra window closed, and both buffers buried ;; If you just want access to the browser, try M-x ;; emms-browse-by-TYPE, where TYPE is one of artist, album, composer, ;; genre or year. These commands can also be used while smart browsing to ;; change the browsing category. ;; If you don't want to activate the code with (emms-devel), you can ;; activate it manually with: ;; (require 'emms-browser) ;; Key bindings ;; ------------------------------------------------------------------- ;; C-j emms-browser-add-tracks-and-play ;; RET emms-browser-add-tracks ;; SPC emms-browser-toggle-subitems ;; / emms-isearch-buffer ;; 1 emms-browser-collapse-all ;; 2 emms-browser-expand-to-level-2 ;; 3 emms-browser-expand-to-level-3 ;; 4 emms-browser-expand-to-level-4 ;; < emms-browser-previous-filter ;; > emms-browser-next-filter ;; ? describe-mode ;; C emms-browser-clear-playlist ;; E emms-browser-expand-all ;; d emms-browser-view-in-dired ;; D emms-browser-delete-files ;; q emms-browser-bury-buffer ;; r emms-browser-goto-random ;; n next-line ;; p previous-line ;; C-/ emms-playlist-mode-undo ;; <C-return> emms-browser-add-tracks-and-play ;; <backtab> emms-browser-prev-non-track ;; <tab> emms-browser-next-non-track ;; s A emms-browser-search-by-album ;; s a emms-browser-search-by-artist ;; s c emms-browser-search-by-composer ;; s s emms-browser-search-by-names ;; s t emms-browser-search-by-title ;; s p emms-browser-search-by-performer ;; b 1 emms-browse-by-artist ;; b 2 emms-browse-by-album ;; b 3 emms-browse-by-genre ;; b 4 emms-browse-by-year ;; b 5 emms-browse-by-composer ;; b 6 emms-browse-by-performer ;; W a p emms-browser-lookup-album-on-pitchfork ;; W a w emms-browser-lookup-album-on-wikipedia ;; W A p emms-browser-lookup-artist-on-pitchfork ;; W A w emms-browser-lookup-artist-on-wikipedia ;; W C p emms-browser-lookup-composer-on-pitchfork ;; W C w emms-browser-lookup-composer-on-wikipedia ;; W P p emms-browser-lookup-performer-on-pitchfork ;; W P w emms-browser-lookup-performer-on-wikipedia ;; Displaying covers ;; ------------------------------------------------------------------- ;; The browser will attempt to display cover images if they're ;; available. By default it looks for images cover_small.jpg, ;; cover_med.jpg, etc. Customize emms-browser-covers to use your own ;; covers. Note that you'll probably want to resize your existing ;; covers to particular sizes. Suggested sizes are 100x100 for small, ;; and 200x200 for medium. ;; Also emacs by default will jump around a lot when scrolling a ;; buffer with images. Set the following variables to prevent that: ;; scroll-up-aggressively 0.0 ;; scroll-down-aggressively 0.0 ;; To show a 'no cover' image for albums which don't have a cover, add ;; the following code to your .emacs: ;; (setq emms-browser-default-covers ;; (list "/path/to/cover_small.jpg" nil nil) ;; (the medium and large images can be set too, if you want) ;; You can download an example 'no cover' image from: ;; http://repose.cx/cover_small.jpg ;; Filtering tracks ;; ------------------------------------------------------------------- ;; If you want to display a subset of your collection (such as a ;; directory of 80s music, only avi files, etc), then you can make ;; some filters using code like this: ;; ;; show everything ;; (emms-browser-make-filter "all" 'ignore) ;; ;; Set "all" as the default filter ;; (emms-browser-set-filter (assoc "all" emms-browser-filters)) ;; ;; show all files (no streamlists, etc) ;; (emms-browser-make-filter ;; "all-files" (emms-browser-filter-only-type 'file)) ;; ;; show only tracks in one folder ;; (emms-browser-make-filter ;; "80s" (emms-browser-filter-only-dir "~/Mp3s/80s")) ;; ;; show all tracks played in the last month ;; (emms-browser-make-filter ;; "last-month" (emms-browser-filter-only-recent 30)) ;; After executing the above commands, you can use M-x ;; emms-browser-show-all, emms-browser-show-80s, etc to toggle ;; between different collections. Alternatively you can use '<' and ;; '>' to cycle through the available filters. ;; The second argument to make-filter is a function which returns t if ;; a single track should be filtered. You can write your own filter ;; functions to check the type of a file, etc. ;; Some more examples: ;; ;; show only tracks not played in the last year ;; (emms-browser-make-filter "not-played" ;; (lambda (track) ;; (not (funcall (emms-browser-filter-only-recent 365) track)))) ;; ;; show all files that are not in the pending directory ;; (emms-browser-make-filter ;; "all" ;; (lambda (track) ;; (or ;; (funcall (emms-browser-filter-only-type 'file) track) ;; (not (funcall ;; (emms-browser-filter-only-dir "~/Media/pending") track))))) ;; Changing tree structure ;; ------------------------------------------------------------------- ;; You can change the way the tree is displayed by modifying ;; `emms-browser-next-mapping-type'. The following code displays ;; artist->track instead of artist->album->track when you switch to ;; the 'singles' filter. ;; (defadvice emms-browser-next-mapping-type ;; (after no-album (current-mapping)) ;; (when (eq ad-return-value 'info-album) ;; (setq ad-return-value 'info-title))) ;; (defun toggle-album-display () ;; (if (string= emms-browser-current-filter-name "singles") ;; (ad-activate 'emms-browser-next-mapping-type) ;; (ad-deactivate 'emms-browser-next-mapping-type))) ;; (add-hook 'emms-browser-filter-changed-hook 'toggle-album-display) ;; Changing display format ;; ------------------------------------------------------------------- ;; Format strings govern the way items are displayed in the browser ;; and playlist. You can customize these if you wish. ;; `emms-browser-default-format' controls the format to use when no ;; other format has been explicitly defined. By default, only track and ;; albums deviate from the default. ;; To customise the format of a particular type, find the name of the ;; field you want to use (eg `info-artist', `info-title', etc), and ;; insert that into emms-browser-<type>-format or ;; emms-browser-playlist-<type>-format. For example, if you wanted to ;; remove track numbers from tracks in both the browser and playlist, ;; you could do: ;; (defvar emms-browser-info-title-format "%i%n") ;; (defvar emms-browser-playlist-info-title-format ;; emms-browser-info-title-format) ;; The format specifiers available include: ;; %i indent relative to the current level ;; %n the value of the item - eg -info-artist might be "pink floyd" ;; %y the album year ;; %A the album name ;; %a the artist name of the track ;; %C the composer name of the track ;; %p the performer name of the track ;; %t the title of the track ;; %T the track number ;; %cS a small album cover ;; %cM a medium album cover ;; %cL a big album cover ;; Note that if you use track-related items like %t, it will take the ;; data from the first track. ;; Changing display faces ;; ------------------------------------------------------------------- ;; The faces used to display the various fields are also customizable. ;; They are in the format emms-browser-<type>-face, where type is one ;; of "year/genre", "artist", "composer", "performer", "album" or ;; "track". Note that faces lack the initial "info-" part. For example, ;; to change the artist face, type ;; M-x customize-face emms-browser-artist-face. ;; Deleting files ;; ------------------------------------------------------------------- ;; You can use the browser to delete tracks from your hard disk. ;; Because this is dangerous, it is disabled by default. ;; The following code will delete covers at the same time, and remove ;; parent directories if they're now empty. ;; (defun de-kill-covers-and-parents (dir tracks) ;; (when (> (length tracks) 1) ;; ;; if we're not deleting an individual file, delete covers too ;; (dolist (cover '("cover.jpg" ;; "cover_med.jpg" ;; "cover_small.jpg" ;; "folder.jpg")) ;; (condition-case nil ;; (delete-file (concat dir cover)) ;; (error nil))) ;; ;; try and delete empty parents - we actually do the work of the ;; ;; calling function here, too ;; (let (failed) ;; (while (and (not (string= dir "/")) ;; (not failed)) ;; (condition-case nil ;; (delete-directory dir) ;; (error (setq failed t))) ;; (setq dir (file-name-directory (directory-file-name dir))))))) ;; (add-hook 'emms-browser-delete-files-hook 'de-kill-covers-and-parents) ;;; Code: (require 'emms) (require 'emms-cache) (require 'emms-source-file) (require 'emms-playlist-sort) (require 'sort) (eval-when-compile (require 'cl)) ;; -------------------------------------------------- ;; Variables and configuration ;; -------------------------------------------------- (defgroup emms-browser nil "*The Emacs Multimedia System browser" :prefix "emms-browser-" :group 'multimedia :group 'applications) (defcustom emms-browser-default-browse-type 'info-artist "*The default browsing mode." :group 'emms-browser :type 'function) (defcustom emms-browser-make-name-function 'emms-browser-make-name-standard "*A function to make names for entries and subentries. Overriding this function allows you to customise how various elements are displayed. It is called with two arguments - track and type." :group 'emms-browser :type 'function) (defcustom emms-browser-get-track-field-function 'emms-browser-get-track-field-simple "*A function to get an element from a track. Change this to customize the way data is organized in the browser. For example, `emms-browser-get-track-field-use-directory-name' uses the directory name to determine the artist. This means that soundtracks, compilations and so on don't populate the artist view with lots of 1-track elements." :group 'emms-browser :type 'function) (defcustom emms-browser-covers '("cover_small.jpg" "cover_med.jpg" "cover_large.jpg") "*Control how cover images are found. Can be either a list of small, medium and large images (large currently not used), a function which takes a directory and one of the symbols `small', `medium' or `large', and should return a path to the cover, or nil to turn off cover loading." :group 'emms-browser :type '(choice list function boolean)) (defcustom emms-browser-default-covers nil "*A list of default images to use if a cover isn't found." :group 'emms-browser :type 'list) (defcustom emms-browser-comparison-test (if (fboundp 'define-hash-table-test) 'case-fold 'equal) "*A method for comparing entries in the cache. The default is to compare case-insensitively." :group 'emms-browser :type 'symbol) (defcustom emms-browser-track-sort-function 'emms-sort-natural-order-less-p "*How to sort tracks in the browser. Ues nil for no sorting." :group 'emms-browser :type 'function) (defcustom emms-browser-alpha-sort-function 'string< "*How to sort artists/albums/etc. in the browser. Use nil for no sorting." :group 'emms-browser :type 'function) (defcustom emms-browser-album-sort-function 'emms-browser-sort-by-year-or-name "*How to sort artists/albums/etc. in the browser. Use nil for no sorting." :group 'emms-browser :type 'function) (defcustom emms-browser-show-display-hook nil "*Hooks to run when starting or switching to a browser buffer." :group 'emms-browser :type 'hook) (defcustom emms-browser-hide-display-hook nil "*Hooks to run when burying or removing a browser buffer." :group 'emms-browser :type 'hook) (defcustom emms-browser-tracks-added-hook nil "*Hooks to run when tracks are added to the playlist." :group 'emms-browser :type 'hook) (defcustom emms-browser-filter-tracks-hook nil "*Given a track, return t if the track should be ignored." :group 'emms-browser :type 'hook) (defcustom emms-browser-filter-changed-hook nil "*Hook run after the filter has changed." :group 'emms-browser :type 'hook) (defcustom emms-browser-delete-files-hook nil "*Hook run after files have been deleted. This hook can be used to clean up extra files, such as album covers. Called once for each directory." :group 'emms-browser :type 'hook) (defvar emms-browser-buffer nil "The current browser buffer, if any.") (defvar emms-browser-buffer-name "*EMMS Browser*" "The default buffer name.") (defvar emms-browser-top-level-hash nil "The current mapping db, eg. artist -> track.") (make-variable-buffer-local 'emms-browser-top-level-hash) (defvar emms-browser-top-level-type nil "The current mapping type, eg. 'info-artist.") (make-variable-buffer-local 'emms-browser-top-level-type) (defvar emms-browser-current-indent nil "Used to override the current indent, for the playlist, etc.") (defvar emms-browser-current-filter-name nil "The name of the current filter in place, if any.") (defconst emms-browser-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "q") 'emms-browser-bury-buffer) (define-key map (kbd "/") 'emms-isearch-buffer) (define-key map (kbd "r") 'emms-browser-goto-random) (define-key map (kbd "n") 'next-line) (define-key map (kbd "p") 'previous-line) (define-key map (kbd "C") 'emms-browser-clear-playlist) (define-key map (kbd "?") 'describe-mode) (define-key map (kbd "C-/") 'emms-playlist-mode-undo) (define-key map (kbd "SPC") 'emms-browser-toggle-subitems) (define-key map (kbd "RET") 'emms-browser-add-tracks) (define-key map (kbd "<C-return>") 'emms-browser-add-tracks-and-play) (define-key map (kbd "C-j") 'emms-browser-add-tracks-and-play) (define-key map (kbd "<tab>") 'emms-browser-next-non-track) (define-key map (kbd "<backtab>") 'emms-browser-prev-non-track) (define-key map (kbd "d") 'emms-browser-view-in-dired) (define-key map (kbd "D") 'emms-browser-delete-files) (define-key map (kbd "E") 'emms-browser-expand-all) (define-key map (kbd "1") 'emms-browser-collapse-all) (define-key map (kbd "2") 'emms-browser-expand-to-level-2) (define-key map (kbd "3") 'emms-browser-expand-to-level-3) (define-key map (kbd "4") 'emms-browser-expand-to-level-4) (define-key map (kbd "b 1") 'emms-browse-by-artist) (define-key map (kbd "b 2") 'emms-browse-by-album) (define-key map (kbd "b 3") 'emms-browse-by-genre) (define-key map (kbd "b 4") 'emms-browse-by-year) (define-key map (kbd "b 5") 'emms-browse-by-composer) (define-key map (kbd "b 6") 'emms-browse-by-performer) (define-key map (kbd "s a") 'emms-browser-search-by-artist) (define-key map (kbd "s c") 'emms-browser-search-by-composer) (define-key map (kbd "s p") 'emms-browser-search-by-performer) (define-key map (kbd "s A") 'emms-browser-search-by-album) (define-key map (kbd "s t") 'emms-browser-search-by-title) (define-key map (kbd "s s") 'emms-browser-search-by-names) (define-key map (kbd "W A w") 'emms-browser-lookup-artist-on-wikipedia) (define-key map (kbd "W A p") 'emms-browser-lookup-artist-on-pitchfork) (define-key map (kbd "W C w") 'emms-browser-lookup-composer-on-wikipedia) (define-key map (kbd "W C p") 'emms-browser-lookup-composer-on-pitchfork) (define-key map (kbd "W P w") 'emms-browser-lookup-performer-on-wikipedia) (define-key map (kbd "W P p") 'emms-browser-lookup-performer-on-pitchfork) (define-key map (kbd "W a w") 'emms-browser-lookup-album-on-wikipedia) (define-key map (kbd "W a p") 'emms-browser-lookup-album-on-pitchfork) (define-key map (kbd ">") 'emms-browser-next-filter) (define-key map (kbd "<") 'emms-browser-previous-filter) map) "Keymap for `emms-browser-mode'.") (defconst emms-browser-search-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map emms-browser-mode-map) (define-key map (kbd "q") 'emms-browser-kill-search) map) "Keymap for `emms-browser-mode'.") ;; -------------------------------------------------- ;; Compatability functions ;; -------------------------------------------------- (eval-and-compile (if (fboundp 'with-selected-window) (defalias 'emms-browser-with-selected-window 'with-selected-window) (defmacro emms-browser-with-selected-window (window &rest body) ;; this emulates the behavior introduced earlier, though it ;; might be best to do something with `window' `(save-selected-window ,body))) (put 'emms-browser-with-selected-window 'lisp-indent-function 1) (put 'emms-browser-with-selected-window 'edebug-form-spec '(form body)) (if (fboundp 'run-mode-hooks) (defalias 'emms-browser-run-mode-hooks 'run-mode-hooks) (defalias 'emms-browser-run-mode-hooks 'run-hooks))) ;; -------------------------------------------------- ;; General mode setup ;; -------------------------------------------------- (defun emms-browser () "Launch or switch to the EMMS Browser." (interactive) (emms-browser-create-or-focus emms-browser-default-browse-type)) (defun emms-browser-create-or-focus (type) "Create a new browser buffer with BROWSE-FUNC, or switch. BROWSE-FUNC should fill the buffer with something of interest. An example function is `emms-browse-by-artist'." (let ((buf (emms-browser-get-buffer)) wind) (if buf ;; if the buffer is displayed, switch the window instead (progn (setq wind (get-buffer-window buf)) (if wind (select-window wind) (switch-to-buffer buf)) (emms-browser-run-mode-hooks 'emms-browser-show-display-hook)) ;; if there's no buffer, create a new window (emms-browser-create) (emms-browse-by type)))) (defun emms-browser-create () "Create a new emms-browser buffer and start emms-browser-mode." (emms-browser-new-buffer) (emms-browser-mode) (emms-browser-run-mode-hooks 'emms-browser-show-display-hook)) (defun emms-browser-mode (&optional no-update) "A major mode for the Emms browser. \\{emms-browser-mode-map}" ;; create a new buffer (interactive) (use-local-map emms-browser-mode-map) (setq major-mode 'emms-browser-mode mode-name "Emms-Browser") (setq buffer-read-only t) (unless no-update (setq emms-browser-buffer (current-buffer)))) (defun emms-browser-new-buffer () "Create a new browser buffer, and switch to it." (switch-to-buffer (generate-new-buffer emms-browser-buffer-name))) (defun emms-browser-clear () "Create or switch to a browser buffer, clearing it." (let ((buf (emms-browser-get-buffer))) (if buf (progn (switch-to-buffer buf) (emms-with-inhibit-read-only-t (delete-region (point-min) (point-max)))) (emms-browser-create)))) (defun emms-browser-get-buffer () "Return the current buffer if it exists, or nil." (unless (or (null emms-browser-buffer) (not (buffer-live-p emms-browser-buffer))) emms-browser-buffer)) (defun emms-browser-ensure-browser-buffer () (unless (eq major-mode 'emms-browser-mode) (error "Current buffer is not an emms-browser buffer"))) (defun emms-browser-bury-buffer () "Bury the browser buffer, running hooks." (interactive) (emms-browser-run-mode-hooks 'emms-browser-hide-display-hook) (bury-buffer)) ;; -------------------------------------------------- ;; Top-level browsing methods - by artist/album/etc ;; -------------------------------------------------- ;; Since the number of tracks may be rather large, we use a hash to ;; sort the top level elements into various categories. All ;; subelements will be stored in a bdata alist structure. (defmacro emms-browser-add-category (name type) "Create an interactive function emms-browse-by-NAME." (let ((funname (intern (concat "emms-browse-by-" name))) (funcdesc (concat "Browse by " name "."))) `(defun ,funname () ,funcdesc (interactive) (emms-browse-by ,type)))) (defun emms-browse-by (type) "Render a top level buffer based on TYPE." ;; FIXME: assumes we only browse by info-* (let* ((name (substring (symbol-name type) 5)) (modedesc (concat "Browsing by: " name)) (hash (emms-browser-make-hash-by type))) (when emms-browser-current-filter-name (setq modedesc (concat modedesc " [" emms-browser-current-filter-name "]"))) (emms-browser-clear) (rename-buffer modedesc) (emms-browser-render-hash hash type) (setq emms-browser-top-level-hash hash) (setq emms-browser-top-level-type type) (unless (> (hash-table-count hash) 0) (emms-browser-show-empty-cache-message)) (goto-char (point-min)))) (emms-browser-add-category "artist" 'info-artist) (emms-browser-add-category "composer" 'info-composer) (emms-browser-add-category "performer" 'info-performer) (emms-browser-add-category "album" 'info-album) (emms-browser-add-category "genre" 'info-genre) (emms-browser-add-category "year" 'info-year) (defun emms-browser-get-track-field (track type) "Return TYPE from TRACK. This can be customized to group different artists into one for compilations, etc." (funcall emms-browser-get-track-field-function track type)) (defun emms-browser-get-track-field-simple (track type) (emms-track-get track type "misc")) (defun emms-browser-get-track-field-use-directory-name (track type) (if (eq type 'info-artist) (emms-browser-get-artist-from-path track) (emms-track-get track type "misc"))) (defun emms-browser-get-artist-from-path (track) (let* ((path (emms-track-get track 'name)) (dir (file-name-directory path)) (basedir (file-name-nondirectory (directory-file-name (file-name-directory dir))))) (car (split-string basedir " - ")))) (defun emms-browser-make-hash-by (type) "Make a hash, mapping with TYPE, eg artist -> tracks." (let ((hash (make-hash-table :test emms-browser-comparison-test)) field existing-entry) (maphash (lambda (path track) (unless (run-hook-with-args-until-success 'emms-browser-filter-tracks-hook track) (setq field (emms-browser-get-track-field track type)) (when field (setq existing-entry (gethash field hash)) (if existing-entry (puthash field (cons track existing-entry) hash) (puthash field (list track) hash))))) emms-cache-db) hash)) (defun emms-browser-render-hash (db type) "Render a mapping (DB) into a browser buffer." (maphash (lambda (desc data) (emms-browser-insert-top-level-entry desc data type)) db) (emms-with-inhibit-read-only-t (let ((sort-fold-case t)) (sort-lines nil (point-min) (point-max))))) (defun case-fold-string= (a b) (eq t (compare-strings a nil nil b nil nil t))) (defun case-fold-string-hash (a) (sxhash (upcase a))) (when (fboundp 'define-hash-table-test) (define-hash-table-test 'case-fold 'case-fold-string= 'case-fold-string-hash)) (defun emms-browser-insert-top-level-entry (name tracks type) "Insert a single top level entry into the buffer." (emms-browser-ensure-browser-buffer) (let ((bdata (emms-browser-make-bdata-tree type 1 tracks name))) (emms-browser-insert-format bdata))) (defun emms-browser-show-empty-cache-message () "Display some help if the cache is empty." (emms-with-inhibit-read-only-t (insert " Welcome to EMMS. There are currently no files in the EMMS database. To browse music, you need to tell EMMS where your files are. Try the following commands: M-x emms-add-directory-tree: Add all music in a directory and its subdirectories. M-x emms-add-directory: Add all music in a directory M-x emms-add-file: Add a single music file. After you have added some files, wait for EMMS to say 'all track information loaded,' then return to the browser, and hit 'b 1' to refresh."))) ;; -------------------------------------------------- ;; Building a subitem tree ;; -------------------------------------------------- (defun emms-browser-next-mapping-type (current-mapping) "Return the next sensible mapping. Eg. if CURRENT-MAPPING is currently 'info-artist, return 'info-album." (cond ((eq current-mapping 'info-artist) 'info-album) ((eq current-mapping 'info-composer) 'info-album) ((eq current-mapping 'info-performer) 'info-album) ((eq current-mapping 'info-album) 'info-title) ((eq current-mapping 'info-genre) 'info-artist) ((eq current-mapping 'info-year) 'info-artist))) (defun emms-browser-make-bdata-tree (type level tracks name) "Build a tree of browser DB elements for tracks." (emms-browser-make-bdata (emms-browser-make-bdata-tree-recurse type level tracks) name type level)) (defun emms-browser-make-bdata-tree-recurse (type level tracks) "Build a tree of alists based on a list of tracks, TRACKS. For example, if TYPE is 'info-year, return an alist like: artist1 -> album1 -> *track* 1.." (let* ((next-type (emms-browser-next-mapping-type type)) (next-level (1+ level)) alist name new-db new-tracks) ;; if we're at a leaf, the db data is a list of tracks (if (eq type 'info-title) tracks ;; otherwise, make DBs from the sub elements (setq alist (emms-browser-make-sorted-alist next-type tracks)) (mapcar (lambda (entry) (setq name (emms-browser-make-name entry next-type)) (setq new-tracks (cdr entry)) (emms-browser-make-bdata (emms-browser-make-bdata-tree-recurse next-type next-level new-tracks) name next-type next-level)) alist)))) (defun emms-browser-make-name (entry type) "Return a name for ENTRY, used for making a bdata object." (let ((key (car entry)) (track (cadr entry)) artist title) ;; only the first track (cond ((eq type 'info-title) (setq artist (emms-track-get track 'info-artist)) (setq title (emms-track-get track 'info-title)) (if (not (and artist title)) key (concat artist " - " title))) (t key)))) (defun emms-browser-track-number (track) "Return a string representation of a track number. The string will end in a space. If no track number is available, return an empty string." (let ((tracknum (emms-track-get track 'info-tracknumber))) (if (or (not (stringp tracknum)) (string= tracknum "0")) "" (concat (if (eq (length tracknum) 1) (concat "0" tracknum) tracknum))))) (defun emms-browser-disc-number (track) "Return a string representation of a track number. The string will end in a space. If no track number is available, return an empty string." (let ((discnum (emms-track-get track 'info-discnumber))) (if (or (not (stringp discnum)) (string= discnum "0")) "" discnum))) (defun emms-browser-year-number (track) "Return a string representation of a track's year. This will be in the form '(1998) '." (let ((year (emms-track-get track 'info-year))) (if (or (not (stringp year)) (string= year "0")) "" (concat "(" year ") ")))) (defun emms-browser-track-duration (track) "Return a string representation of a track duration. If no duration is available, return an empty string." (let ((pmin (emms-track-get track 'info-playing-time-min)) (psec (emms-track-get track 'info-playing-time-sec)) (ptot (emms-track-get track 'info-playing-time))) (cond ((and pmin psec) (format "%02d:%02d" pmin psec)) (ptot (format "%02d:%02d" (/ ptot 60) (% ptot 60))) (t "")))) (defun emms-browser-make-bdata (data name type level) "Return a browser data item from ALIST. DATA should be a list of DB items, or a list of tracks. NAME is a name for the DB item. TYPE is a category the data is organised by, such as 'info-artist. LEVEL is the number of the sublevel the db item will be placed in." (list (cons 'type type) (cons 'level level) (cons 'name name) (cons 'data data))) (defun emms-browser-make-alist (type tracks) "Make an alist mapping of TYPE -> TRACKS. Items with no metadata for TYPE will be placed in 'misc'" (let (db key existing tracknum) (dolist (track tracks) (setq key (emms-browser-get-track-field track type)) (when (eq type 'info-title) ;; try and make every track unique (setq tracknum (emms-browser-track-number track)) (if (string= tracknum "") (setq key (file-name-nondirectory (emms-track-get track 'name))) (setq key (concat tracknum key)))) (setq existing (assoc key db)) (if existing (setcdr existing (cons track (cdr existing))) (push (cons key (list track)) db))) ;; sort the entries we've built (dolist (item db) (setcdr item (nreverse (cdr item)))) db)) (defun emms-browser-make-sorted-alist (type tracks) "Return a sorted alist of TRACKS. TYPE is the metadata to make the alist by - eg. if it's 'info-artist, an alist of artists will be made." (emms-browser-sort-alist (emms-browser-make-alist type tracks) type)) ;; -------------------------------------------------- ;; BDATA accessors and predicates ;; -------------------------------------------------- (defun emms-browser-bdata-level (bdata) (cdr (assq 'level bdata))) (defun emms-browser-bdata-name (bdata) (cdr (assq 'name bdata))) (defun emms-browser-bdata-type (bdata) (cdr (assq 'type bdata))) (defun emms-browser-bdata-data (bdata) (cdr (assq 'data bdata))) (defun emms-browser-bdata-p (obj) "True if obj is a BDATA object." (consp (assq 'data obj))) ;; -------------------------------------------------- ;; Sorting expanded entries ;; -------------------------------------------------- (defmacro emms-browser-sort-cadr (sort-func) "Return a function to sort an alist using SORT-FUNC. This sorting predicate will compare the cadr of each entry. SORT-FUNC should be a playlist sorting predicate like `emms-playlist-sort-by-natural-order'." `(lambda (a b) (funcall ,sort-func (cadr a) (cadr b)))) (defmacro emms-browser-sort-car (sort-func) "Return a function to sort an alist using SORT-FUNC. This sorting predicate will compare the car of each entry. SORT-FUNC should be a playlist sorting predicate like `emms-playlist-sort-by-natural-order'." `(lambda (a b) (funcall ,sort-func (car a) (car b)))) (defun emms-browser-sort-by-track (alist) "Sort an ALIST by the tracks in each entry. Uses `emms-browser-track-sort-function'." (if emms-browser-track-sort-function (sort alist (emms-browser-sort-cadr emms-browser-track-sort-function)) alist)) (defun emms-browser-sort-by-name (alist) "Sort ALIST by keys alphabetically. Uses `emms-browser-alpha-sort-function'." (if emms-browser-alpha-sort-function (sort alist (emms-browser-sort-car emms-browser-alpha-sort-function)) alist)) (defun emms-browser-sort-by-year-or-name (alist) "Sort based on year or name." (sort alist (emms-browser-sort-cadr 'emms-browser-sort-by-year-or-name-p))) (defun emms-browser-sort-by-year-or-name-p (a b) ;; FIXME: this is a bit of a hack (let ((a-desc (concat (emms-browser-year-number a) (emms-track-get a 'info-album "misc"))) (b-desc (concat (emms-browser-year-number b) (emms-track-get b 'info-album "misc")))) (string< a-desc b-desc))) (defun emms-browser-sort-alist (alist type) "Sort ALIST using the sorting function for TYPE." (let ((sort-func (cond ((or (eq type 'info-artist) (eq type 'info-composer) (eq type 'info-performer) (eq type 'info-year) (eq type 'info-genre)) 'emms-browser-sort-by-name) ((eq type 'info-album) emms-browser-album-sort-function) ((eq type 'info-title) 'emms-browser-sort-by-track) (t (message "Can't sort unknown mapping!"))))) (funcall sort-func alist))) ;; -------------------------------------------------- ;; Subitem operations on the buffer ;; -------------------------------------------------- (defun emms-browser-bdata-at-point () "Return the bdata object at point. Includes information at point (such as album name), and metadata." (get-text-property (point-at-bol) 'emms-browser-bdata)) (defun emms-browser-data-at-point () "Return the data stored under point. This will be a list of DB items." (emms-browser-bdata-data (emms-browser-bdata-at-point))) (defun emms-browser-level-at-point () "Return the current level at point." (emms-browser-bdata-level (emms-browser-bdata-at-point))) (defun emms-browser-tracks-at-point (&optional node) "Return a list of tracks at point." (let (tracks) (dolist (node (if node node (emms-browser-data-at-point))) (if (not (emms-browser-bdata-p node)) (setq tracks (cons node tracks)) (setq tracks (append tracks (emms-browser-tracks-at-point (emms-browser-bdata-data node)))))) tracks)) (defun emms-browser-expand-one-level () "Expand the current line by one sublevel." (interactive) (let* ((data (emms-browser-data-at-point))) (save-excursion (forward-line 1) (beginning-of-line) (dolist (data-item data) (emms-browser-insert-data-item data-item))))) (defun emms-browser-insert-data-item (data-item) "Insert DATA-ITEM into the buffer. This checks DATA-ITEM's level to determine how much to indent. The line will have a property emms-browser-bdata storing subitem information." (emms-browser-insert-format data-item)) (defun emms-browser-find-entry-more-than-level (level) "Move point to next entry more than LEVEL and return point. If no entry exits, return nil. Returns point if currently on a an entry more than LEVEL." (let ((old-pos (point)) level-at-point) (forward-line 1) (setq level-at-point (emms-browser-level-at-point)) (if (and level-at-point (> level-at-point level)) (point) (goto-char old-pos) nil))) (defun emms-browser-subitems-visible () "True if there are any subentries visible point." (let ((current-level (emms-browser-level-at-point)) new-level) (save-excursion (re-search-forward "\n" nil t) (when (setq new-level (emms-browser-level-at-point)) (> new-level current-level))))) (defun emms-browser-subitems-exist () "True if it's possible to expand the current line." (not (eq (emms-browser-bdata-type (emms-browser-bdata-at-point)) 'info-title))) (defun emms-browser-move-up-level (&optional direction) "Move up one level if possible. Return true if we were able to move up. If DIRECTION is 1, move forward, otherwise move backwards." (let ((moved nil) (continue t) (current-level (emms-browser-level-at-point))) (while (and continue (zerop (forward-line (or direction -1)))) (when (> current-level (emms-browser-level-at-point)) (setq moved t) (setq continue nil))) moved)) (defun emms-browser-toggle-subitems () "Show or hide (kill) subitems under the current line." (interactive) (if (emms-browser-subitems-visible) (emms-browser-kill-subitems) (if (emms-browser-subitems-exist) (emms-browser-show-subitems) (assert (emms-browser-move-up-level)) (emms-browser-kill-subitems)))) (defun emms-browser-show-subitems () "Show subitems under the current line." (unless (emms-browser-subitems-visible) (if (emms-browser-subitems-exist) (emms-browser-expand-one-level)))) (defun emms-browser-kill-subitems () "Remove all subitems under the current line. Stops at the next line at the same level, or EOF." (when (emms-browser-subitems-visible) (let ((current-level (emms-browser-level-at-point)) (next-line (point-at-bol 2))) (emms-with-inhibit-read-only-t (delete-region next-line (save-excursion (while (emms-browser-find-entry-more-than-level current-level)) (point-at-bol 2))))))) ;; -------------------------------------------------- ;; Dealing with the playlist (queuing songs, etc) ;; -------------------------------------------------- (defun emms-browser-playlist-insert-group (bdata) "Insert a group description into the playlist buffer." (let* ((type (emms-browser-bdata-type bdata)) (short-type (substring (symbol-name type) 5)) (name (emms-browser-format-line bdata 'playlist))) (with-current-emms-playlist (goto-char (point-max)) (insert name "\n")))) (defun emms-browser-playlist-insert-track (bdata) "Insert a track into the playlist buffer." (let ((name (emms-browser-format-line bdata 'playlist)) (track (car (emms-browser-bdata-data bdata)))) (with-current-emms-playlist (goto-char (point-max)) (insert name "\n")))) (defun emms-browser-playlist-insert-bdata (bdata starting-level) "Add all tracks in BDATA to the playlist." (let ((type (emms-browser-bdata-type bdata)) (name (emms-browser-bdata-name bdata)) (level (emms-browser-bdata-level bdata)) emms-browser-current-indent) ;; adjust the indentation relative to the starting level (when starting-level (setq level (- level (1- starting-level)))) ;; we temporarily rebind the current indent to the relative indent (setq emms-browser-current-indent (emms-browser-make-indent level)) ;; add a group heading? (unless (eq type 'info-title) (emms-browser-playlist-insert-group bdata)) ;; recurse or add tracks (dolist (item (emms-browser-bdata-data bdata)) (if (not (eq type 'info-title)) (emms-browser-playlist-insert-bdata item starting-level) (emms-browser-playlist-insert-track bdata))))) ;; -------------------------------------------------- ;; Expanding/contracting ;; -------------------------------------------------- (defun emms-browser-expand-to-level (level) "Expand to a depth specified by LEVEL. After expanding, jump to the currently marked entry." (goto-char (point-min)) (while (not (eq (buffer-end 1) (point))) (if (< (emms-browser-level-at-point) level) (emms-browser-show-subitems)) (emms-browser-next-non-track)) (emms-browser-pop-mark) (recenter '(4))) (defun emms-browser-mark-and-collapse () "Save the current top level element, and collapse." (emms-browser-mark-entry) (goto-char (point-max)) (while (not (eq (buffer-end -1) (point))) (emms-browser-prev-non-track) (emms-browser-kill-subitems))) (defun emms-browser-find-top-level () "Move up until reaching a top-level element." (while (not (eq (emms-browser-level-at-point) 1)) (forward-line -1))) (defun emms-browser-mark-entry () "Mark the current top level entry." (save-excursion (emms-browser-find-top-level) (emms-with-inhibit-read-only-t (add-text-properties (point-at-bol) (point-at-eol) (list 'emms-browser-mark t))))) (defun emms-browser-pop-mark () "Return to the last marked entry, and remove the mark." (goto-char (point-min)) (let ((pos (text-property-any (point-min) (point-max) 'emms-browser-mark t))) (if pos (progn (goto-char pos) (emms-with-inhibit-read-only-t (remove-text-properties (point-at-bol) (point-at-eol) (list 'emms-browser-mark)))) (message "No mark saved!")))) (defun emms-browser-go-to-parent () "Move point to the parent of the current node. Return point. If at level one, return the current point." (let ((current-level (emms-browser-level-at-point))) (unless (eq current-level 1) (while (<= current-level (emms-browser-level-at-point)) (forward-line -1))) (point))) (defun emms-browser-delete-current-node () "Remove the current node, and empty parents." ;; set the data to empty (setcdr (assq 'data (emms-browser-bdata-at-point)) nil) (emms-browser-delete-node-if-empty)) (defun emms-browser-delete-node-if-empty () "If empty, remove node and empty parents." (when (zerop (length (emms-browser-data-at-point))) (save-excursion (let ((child-bdata (emms-browser-bdata-at-point)) parent-bdata parent-point) ;; record the parent's position before we delete anything (save-excursion (setq parent-point (emms-browser-go-to-parent))) ;; delete the current line (when (emms-browser-subitems-visible) (emms-browser-kill-subitems)) (emms-with-inhibit-read-only-t (goto-char (point-at-bol)) (kill-line 1)) (unless (eq (emms-browser-bdata-level child-bdata) 1) ;; remove the node from the parent, and recurse (goto-char parent-point) (setq parent-bdata (emms-browser-bdata-at-point)) (setcdr (assq 'data parent-bdata) (delq child-bdata (emms-browser-bdata-data parent-bdata))) (emms-browser-delete-node-if-empty)))))) ;; -------------------------------------------------- ;; User-visible commands ;; -------------------------------------------------- (defun emms-browser-add-tracks () "Add all tracks at point. Return the previous point-max before adding." (interactive) (let ((first-new-track (with-current-emms-playlist (point-max))) (bdata (emms-browser-bdata-at-point))) (emms-browser-playlist-insert-bdata bdata (emms-browser-bdata-level bdata)) (run-hook-with-args 'emms-browser-tracks-added-hook first-new-track) first-new-track)) (defun emms-browser-add-tracks-and-play () "Add all tracks at point, and play the first added track." (interactive) (let ((old-pos (emms-browser-add-tracks))) (with-current-emms-playlist (goto-char old-pos) ;; if we're sitting on a group name, move forward (unless (emms-playlist-track-at (point)) (emms-playlist-next)) (emms-playlist-select (point))) ;; FIXME: is there a better way of doing this? (emms-stop) (emms-start))) (defun emms-isearch-buffer () "Isearch through the buffer." (interactive) (goto-char (point-min)) (when (isearch-forward) (unless (emms-browser-subitems-visible) (emms-browser-show-subitems)))) (defun emms-browser-next-non-track (&optional direction) "Jump to the next non-track element." (interactive) (let ((continue t)) (while (and continue (forward-line (or direction 1))) (unless (eq (emms-browser-bdata-type (emms-browser-bdata-at-point)) 'info-title) (setq continue))))) (defun emms-browser-prev-non-track () "Jump to the previous non-track element." (interactive) (emms-browser-next-non-track -1)) (defun emms-browser-expand-all () "Expand everything." (interactive) (emms-browser-expand-to-level 99)) (defun emms-browser-expand-to-level-2 () "Expand all top level items one level." (interactive) (emms-browser-mark-and-collapse) (emms-browser-expand-to-level 2)) (defun emms-browser-expand-to-level-3 () "Expand all top level items two levels." (interactive) (emms-browser-mark-and-collapse) (emms-browser-expand-to-level 3)) (defun emms-browser-expand-to-level-4 () "Expand all top level items three levels." (interactive) (emms-browser-mark-and-collapse) (emms-browser-expand-to-level 4)) (defun emms-browser-collapse-all () "Collapse everything, saving and restoring the mark." (interactive) (emms-browser-mark-and-collapse) (emms-browser-pop-mark) (recenter '(4))) (defvar emms-browser-seed-pending t "Do we need to seed (random)?") (defun emms-browser-goto-random () (interactive) (when emms-browser-seed-pending (random t) (setq emms-browser-seed-pending nil)) (goto-char (point-min)) (forward-line (1- (random (count-lines (point-min) (point-max)))))) (defun emms-browser-view-in-dired (&optional bdata) "View the current directory in dired." ;; FIXME: currently just grabs the directory from the first track (interactive) (if bdata (if (eq (emms-browser-bdata-type bdata) 'info-title) (let* ((track (car (emms-browser-bdata-data bdata))) (path (emms-track-get track 'name)) (dir (file-name-directory path))) (find-file dir)) (emms-browser-view-in-dired (car (emms-browser-bdata-data bdata)))) (emms-browser-view-in-dired (emms-browser-bdata-at-point)))) (defun emms-browser-delete-files () "Delete all files under point. Disabled by default." (interactive) (let ((tracks (emms-browser-tracks-at-point)) dirs path) (unless (yes-or-no-p (format "Really permanently delete these %d tracks? " (length tracks))) (error "Cancelled!")) (message "Deleting files..") (dolist (track tracks) (setq path (emms-track-get track 'name)) (delete-file path) (add-to-list 'dirs (file-name-directory path)) (emms-cache-del path)) ;; remove empty dirs (dolist (dir dirs) (run-hook-with-args 'emms-browser-delete-files-hook dir tracks) (condition-case nil (delete-directory dir) (error nil))) ;; remove the item from the browser (emms-browser-delete-current-node) (message "Deleting files..done"))) (put 'emms-browser-delete-files 'disabled t) (defun emms-browser-clear-playlist () (interactive) (with-current-emms-playlist (emms-playlist-clear))) (defun emms-browser-lookup (field url) (let ((data (emms-track-get (emms-browser-bdata-first-track (emms-browser-bdata-at-point)) field))) (when data (browse-url (concat url data))))) (defun emms-browser-lookup-wikipedia (field) (emms-browser-lookup field "http://en.wikipedia.org/wiki/Special:Search?search=")) (defun emms-browser-lookup-pitchfork (field) (emms-browser-lookup field "http://www.pitchforkmedia.com/search/record_reviews/query?query[keywords]=")) (defun emms-browser-lookup-artist-on-wikipedia () (interactive) (emms-browser-lookup-wikipedia 'info-artist)) (defun emms-browser-lookup-composer-on-wikipedia () (interactive) (emms-browser-lookup-wikipedia 'info-composer)) (defun emms-browser-lookup-performer-on-wikipedia () (interactive) (emms-browser-lookup-wikipedia 'info-performer)) (defun emms-browser-lookup-album-on-wikipedia () (interactive) (emms-browser-lookup-wikipedia 'info-album)) (defun emms-browser-lookup-artist-on-pitchfork () (interactive) (emms-browser-lookup-pitchfork 'info-artist)) (defun emms-browser-lookup-composer-on-pitchfork () (interactive) (emms-browser-lookup-pitchfork 'info-composer)) (defun emms-browser-lookup-performer-on-pitchfork () (interactive) (emms-browser-lookup-pitchfork 'info-performer)) (defun emms-browser-lookup-album-on-pitchfork () (interactive) (emms-browser-lookup-pitchfork 'info-album)) ;; -------------------------------------------------- ;; Linked browser and playlist windows ;; -------------------------------------------------- (defcustom emms-browser-switch-to-playlist-on-add nil "Whether to switch to to the playlist after adding files." :group 'emms-browser :type 'boolean) (defun emms-smart-browse () "Display browser and playlist. Toggle between selecting browser, playlist or hiding both. Tries to behave sanely if the user has manually changed the window configuration." (interactive) (add-to-list 'emms-browser-show-display-hook 'emms-browser-display-playlist) (add-to-list 'emms-browser-hide-display-hook 'emms-browser-hide-linked-window) ;; switch to the playlist window when adding tracks? (add-to-list 'emms-browser-tracks-added-hook (lambda (start-of-tracks) (interactive) (let (playlist-window) (when emms-browser-switch-to-playlist-on-add (emms-smart-browse)) ;; center on the first added track/group name (when (setq playlist-window (emms-browser-get-linked-window)) (emms-browser-with-selected-window playlist-window (goto-char start-of-tracks) (recenter '(4))))))) (let (wind buf) (cond ((eq major-mode 'emms-browser-mode) (setq buf (emms-browser-get-linked-buffer)) (setq wind (emms-browser-get-linked-window)) ;; if the playlist window is visible, select it (if wind (select-window wind) ;; otherwise display and select it (select-window (emms-browser-display-playlist)))) ((eq major-mode 'emms-playlist-mode) (setq wind (emms-browser-get-linked-window)) ;; if the playlist window is selected, and the browser is visible, ;; hide both (if wind (progn (select-window wind) (emms-browser-bury-buffer)) ;; otherwise bury both (bury-buffer) (emms-browser-hide-linked-window))) (t ;; show both (emms-browser))))) (defun emms-browser-get-linked-buffer () "Return linked buffer (eg browser if playlist is selected." (cond ((eq major-mode 'emms-browser-mode) (car (emms-playlist-buffer-list))) ((eq major-mode 'emms-playlist-mode) emms-browser-buffer))) (defun emms-browser-get-linked-window () "Return linked window (eg browser if playlist is selected." (let ((buf (emms-browser-get-linked-buffer))) (when buf (get-buffer-window buf)))) (defun emms-browser-display-playlist () "A hook to show the playlist when the browser is displayed. Returns the playlist window." (interactive) (let ((pbuf (emms-browser-get-linked-buffer)) (pwin (emms-browser-get-linked-window))) ;; if the window isn't alive.. (unless (window-live-p pwin) (save-selected-window (split-window-horizontally) (other-window 1) (if pbuf (switch-to-buffer pbuf) ;; there's no playlist - create one (setq pbuf (emms-playlist-current-clear)) (switch-to-buffer pbuf)) ;; make q in the playlist window hide the linked browser (when (boundp 'emms-playlist-mode-map) (define-key emms-playlist-mode-map (kbd "q") (lambda () (interactive) (emms-browser-hide-linked-window) (bury-buffer)))) (setq pwin (get-buffer-window pbuf)))) pwin)) (defun emms-browser-hide-linked-window () "Delete a playlist or browser window when the other is hidden." (interactive) (let ((other-buf (emms-browser-get-linked-buffer)) (other-win (emms-browser-get-linked-window))) (when (and other-win (window-live-p other-win)) (delete-window other-win)) ;; bury the buffer, or it becomes visible when we hide the ;; linked buffer (bury-buffer other-buf))) ;; -------------------------------------------------- ;; Searching ;; -------------------------------------------------- (defun emms-browser-filter-cache (search-list) "Return a list of tracks that match SEARCH-LIST. SEARCH-LIST is a list of cons pairs, in the form: ((field1 field2) string) If string matches any of the fields in a cons pair, it will be included." (let (tracks) (maphash (lambda (k track) (when (emms-browser-matches-p track search-list) (push track tracks))) emms-cache-db) tracks)) (defun emms-browser-matches-p (track search-list) (let (no-match matched) (dolist (item search-list) (setq matched nil) (dolist (field (car item)) (let ((track-field (emms-track-get track field ""))) (when (and track-field (string-match (cadr item) track-field)) (setq matched t)))) (unless matched (setq no-match t))) (not no-match))) (defun emms-browser-search-buffer-go () "Create a new search buffer, or clean the existing one." (switch-to-buffer (get-buffer-create "*emms-browser-search*")) (emms-browser-mode t) (use-local-map emms-browser-search-mode-map) (emms-with-inhibit-read-only-t (delete-region (point-min) (point-max)))) (defun emms-browser-search (fields) "Search for STR using FIELDS." (let* ((prompt (format "Searching with %S: " fields)) (str (read-string prompt))) (emms-browser-search-buffer-go) (emms-with-inhibit-read-only-t (emms-browser-render-search (emms-browser-filter-cache (list (list fields str))))) (emms-browser-expand-all) (goto-char (point-min)))) (defun emms-browser-render-search (tracks) (let ((entries (emms-browser-make-sorted-alist 'info-artist tracks))) (dolist (entry entries) (emms-browser-insert-top-level-entry (car entry) (cdr entry) 'info-artist)))) ;; hmm - should we be doing this? (defun emms-browser-kill-search () "Kill the buffer when q is hit." (interactive) (kill-buffer (current-buffer))) (defun emms-browser-search-by-artist () (interactive) (emms-browser-search '(info-artist))) (defun emms-browser-search-by-composer () (interactive) (emms-browser-search '(info-composer))) (defun emms-browser-search-by-performer () (interactive) (emms-browser-search '(info-performer))) (defun emms-browser-search-by-title () (interactive) (emms-browser-search '(info-title))) (defun emms-browser-search-by-album () (interactive) (emms-browser-search '(info-album))) (defun emms-browser-search-by-names () (interactive) (emms-browser-search '(info-artist info-composer info-performer info-title info-album))) ;; -------------------------------------------------- ;; Album covers ;; -------------------------------------------------- (defun emms-browser-get-cover-from-album (bdata &optional size) (assert (eq (emms-browser-bdata-type bdata) 'info-album)) (let* ((track1data (emms-browser-bdata-data bdata)) (track1 (car (emms-browser-bdata-data (car track1data)))) (path (emms-track-get track1 'name))) (emms-browser-get-cover-from-path path size))) (defun emms-browser-get-cover-from-path (path &optional size) "Return a cover filename, if it exists." (unless size (setq size 'medium)) (let* ((size-idx (cond ((eq size 'small) 0) ((eq size 'medium) 1) ((eq size 'large) 2))) (cover (cond ((functionp emms-browser-covers) (funcall emms-browser-covers (file-name-directory path) size)) ((and (listp emms-browser-covers) (nth size-idx emms-browser-covers)) (concat (file-name-directory path) (nth size-idx emms-browser-covers)))))) (if (and cover (file-readable-p cover)) cover ;; no cover found, use default (when emms-browser-default-covers (nth size-idx emms-browser-default-covers))))) (defun emms-browser-insert-cover (path) (insert (emms-browser-make-cover path))) (defun emms-browser-make-cover (path) (let* ((ext (file-name-extension path)) (type (cond ((string= ext "png") 'png) ((string= ext "xbm") 'xbm) ((string= ext "xpm") 'xpm) ((string= ext "pbm") 'pbm) ((string-match "e?ps" ext) 'postscript) ((string= ext "gif") 'gif) ((string= ext "tiff") 'tiff) (t 'jpeg)))) (emms-propertize " " 'display `(image :type ,type :margin 5 :file ,path) 'rear-nonsticky '(display)))) (defun emms-browser-get-cover-str (path size) (let ((cover (emms-browser-get-cover-from-path path size))) (if cover (emms-browser-make-cover cover) ;; we use a single space so that cover & no cover tracks line up ;; in a terminal " "))) ;; -------------------------------------------------- ;; Display formats ;; -------------------------------------------------- (defun emms-browser-bdata-first-track (bdata) "Return the first track from a given bdata. If > album level, most of the track data will not make sense." (let ((type (emms-browser-bdata-type bdata))) (if (eq type 'info-title) (car (emms-browser-bdata-data bdata)) ;; recurse (emms-browser-bdata-first-track (car (emms-browser-bdata-data bdata)))))) (defun emms-browser-insert-format (bdata) (emms-with-inhibit-read-only-t (insert (emms-browser-format-line bdata) "\n"))) (defun emms-browser-make-indent (level) (or emms-browser-current-indent (make-string (* 1 (1- level)) ?\s))) (defun emms-browser-format-elem (format-string elem) (cdr (assoc elem format-string))) (defun emms-browser-format-line (bdata &optional target) "Return a propertized string to be inserted in the buffer." (unless target (setq target 'browser)) (let* ((name (or (emms-browser-bdata-name bdata) "misc")) (level (emms-browser-bdata-level bdata)) (type (emms-browser-bdata-type bdata)) (indent (emms-browser-make-indent level)) (track (emms-browser-bdata-first-track bdata)) (path (emms-track-get track 'name)) (face (emms-browser-get-face bdata)) (format (emms-browser-get-format bdata target)) (props (list 'emms-browser-bdata bdata)) (format-choices `(("i" . ,indent) ("n" . ,name) ("y" . ,(emms-track-get track 'info-year)) ("A" . ,(emms-track-get track 'info-album)) ("a" . ,(emms-track-get track 'info-artist)) ("C" . ,(emms-track-get track 'info-composer)) ("p" . ,(emms-track-get track 'info-performer)) ("t" . ,(emms-track-get track 'info-title)) ("D" . ,(emms-browser-disc-number track)) ("T" . ,(emms-browser-track-number track)) ("d" . ,(emms-browser-track-duration track)) ("cS" . ,(emms-browser-get-cover-str path 'small)) ("cM" . ,(emms-browser-get-cover-str path 'medium)) ("cL" . ,(emms-browser-get-cover-str path 'large)))) str) (when (functionp format) (setq format (funcall format bdata format-choices))) (setq str (with-temp-buffer (insert format) (goto-char (point-min)) (let ((start (point-min))) ;; jump over any image (when (re-search-forward "%c[SML]" nil t) (setq start (point))) ;; jump over the indent (when (re-search-forward "%i" nil t) (setq start (point))) (add-text-properties start (point-max) (list 'face face))) (buffer-string))) (setq str (emms-browser-format-spec str format-choices)) ;; give tracks a 'boost' if they're not top-level ;; (covers take up an extra space) (when (and (eq type 'info-title) (not (string= indent ""))) (setq str (concat " " str))) ;; if we're in playlist mode, add a track (when (and (eq target 'playlist) (eq type 'info-title)) (setq props (append props `(emms-track ,track)))) ;; add properties to the whole string (add-text-properties 0 (length str) props str) str)) (defun emms-browser-get-face (bdata) "Return a suitable face for BDATA." (let* ((type (emms-browser-bdata-type bdata)) (name (cond ((or (eq type 'info-year) (eq type 'info-genre)) "year/genre") ((eq type 'info-artist) "artist") ((eq type 'info-composer) "composer") ((eq type 'info-performer) "performer") ((eq type 'info-album) "album") ((eq type 'info-title) "track")))) (intern (concat "emms-browser-" name "-face")))) ;; based on gnus code (defun emms-browser-format-spec (format specification) "Return a string based on FORMAT and SPECIFICATION. FORMAT is a string containing `format'-like specs like \"bash %u %k\", while SPECIFICATION is an alist mapping from format spec characters to values. Any text properties on a %-spec itself are propagated to the text that it generates." (with-temp-buffer (insert format) (goto-char (point-min)) (while (search-forward "%" nil t) (cond ;; Quoted percent sign. ((eq (char-after) ?%) (delete-char 1)) ;; Valid format spec. ((looking-at "\\([-0-9.]*\\)\\([a-zA-Z]+\\)") (let* ((num (match-string 1)) (spec (match-string 2)) (val (cdr (assoc spec specification)))) (unless val (error "Invalid format character: %s" spec)) ;; Pad result to desired length. (let ((text (format (concat "%" num "s") val))) ;; Insert first, to preserve text properties. (insert-and-inherit text) ;; Delete the specifier body. (delete-region (+ (match-beginning 0) (length text)) (+ (match-end 0) (length text))) ;; Delete the percent sign. (delete-region (1- (match-beginning 0)) (match-beginning 0))))) ;; Signal an error on bogus format strings. (t (error "Invalid format string")))) (buffer-string))) ;; -------------------------------------------------- ;; Display formats - defaults ;; -------------------------------------------------- ;; FIXME: optional format strings would avoid having to define a ;; function for specifiers which may be empty. (defvar emms-browser-default-format "%i%n" "indent + name") ;; tracks (defvar emms-browser-info-title-format 'emms-browser-track-artist-and-title-format) (defvar emms-browser-playlist-info-title-format 'emms-browser-track-artist-and-title-format) (defun emms-browser-get-format (bdata target) (let* ((type (emms-browser-bdata-type bdata)) (target-str (or (and (eq target 'browser) "") (concat (symbol-name target) "-"))) (sym (intern (concat "emms-browser-" target-str (symbol-name type) "-format")))) (if (boundp sym) (symbol-value sym) emms-browser-default-format))) (defun emms-browser-track-artist-and-title-format (bdata fmt) (concat "%i" (let ((track (emms-browser-format-elem fmt "T"))) (if (and track (not (string= track "0"))) "%T. " "")) "%n")) ;; albums - we define two formats, one for a small cover (browser), ;; and one for a medium sized cover (playlist). (defvar emms-browser-info-album-format 'emms-browser-year-and-album-fmt) (defvar emms-browser-playlist-info-album-format 'emms-browser-year-and-album-fmt-med) (defun emms-browser-year-and-album-fmt (bdata fmt) (concat "%i%cS" (let ((year (emms-browser-format-elem fmt "y"))) (if (and year (not (string= year "0"))) "(%y) " "")) "%n")) (defun emms-browser-year-and-album-fmt-med (bdata fmt) (concat "%i%cM" (let ((year (emms-browser-format-elem fmt "y"))) (if (and year (not (string= year "0"))) "(%y) " "")) "%n")) ;; -------------------------------------------------- ;; Display faces ;; -------------------------------------------------- (defmacro emms-browser-make-face (name dark-col light-col height) (let ((face-name (intern (concat "emms-browser-" name "-face")))) `(defface ,face-name '((((class color) (background dark)) (:foreground ,dark-col :height ,height)) (((class color) (background light)) (:foreground ,light-col :height ,height)) (((type tty) (class mono)) (:inverse-video t)) (t (:background ,dark-col))) ,(concat "Face for " name " in a browser/playlist buffer.") :group 'emms-browser-mode))) (emms-browser-make-face "year/genre" "#aaaaff" "#444477" 1.5) (emms-browser-make-face "artist" "#aaaaff" "#444477" 1.3) (emms-browser-make-face "composer" "#aaaaff" "#444477" 1.3) (emms-browser-make-face "performer" "#aaaaff" "#444477" 1.3) (emms-browser-make-face "album" "#aaaaff" "#444477" 1.1) (emms-browser-make-face "track" "#aaaaff" "#444477" 1.0) ;; -------------------------------------------------- ;; Filtering ;; -------------------------------------------------- (defvar emms-browser-filters nil "A list of available filters.") (defmacro emms-browser-make-filter (name func) "Make a user-level function for filtering tracks. This: - defines an interactive function M-x emms-browser-show-NAME. - defines a variable emms-browser-filter-NAME of (name . func). - adds the filter to emms-browser-filters." (let ((funcnam (intern (concat "emms-browser-show-" name))) (var (intern (concat "emms-browser-filter-" name))) (desc (concat "Filter the cache using rule '" name "'"))) `(progn (defvar ,var nil ,desc) (setq ,var (cons ,name ,func)) (add-to-list 'emms-browser-filters ,var) (defun ,funcnam () ,desc (interactive) (emms-browser-refilter ,var))))) (defun emms-browser-set-filter (filter) "Set the current filter to be used on next update. This does not refresh the current buffer." (setq emms-browser-filter-tracks-hook (cdr filter)) (setq emms-browser-current-filter-name (car filter)) (run-hooks 'emms-browser-filter-changed-hook)) (defun emms-browser-refilter (filter) "Filter and render the top-level tracks." (emms-browser-set-filter filter) (emms-browse-by (or emms-browser-top-level-type emms-browser-default-browse-type))) (defun emms-browser-next-filter (&optional reverse) "Redisplay with the next filter." (interactive) (let* ((list (if reverse (reverse emms-browser-filters) emms-browser-filters)) (key emms-browser-current-filter-name) (next (cadr (member (assoc key list) list)))) ;; wrapped (unless next (setq next (car list))) (emms-browser-refilter next))) (defun emms-browser-previous-filter () "Redisplay with the previous filter." (interactive) (emms-browser-next-filter t)) (defun emms-browser-filter-only-dir (path) "Generate a function which checks if a track is in path. If the track is not in path, return t." `(lambda (track) (not (string-match ,(concat "^" (expand-file-name path)) (emms-track-get track 'name))))) (defun emms-browser-filter-only-type (type) "Generate a function which checks a track's type. If the track is not of TYPE, return t." `(lambda (track) (not (eq (quote ,type) (emms-track-get track 'type))))) ;; seconds in a day (* 60 60 24) = 86400 (defun emms-browser-filter-only-recent (days) "Show only tracks played within the last number of DAYS." `(lambda (track) (let ((min-date (time-subtract (current-time) (seconds-to-time (* ,days 86400)))) last-played) (not (and (setq last-played (emms-track-get track 'last-played nil)) (time-less-p min-date last-played)))))) (provide 'emms-browser) ;;; emms-browser.el ends here ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-cache.el����������������������������������������������0000664�0000000�0000000�00000013750�12205151475�0021363�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-cache.el --- persistence for emms-track ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Damien Elmes <emacs@repose.cx> ;; Keywords: emms, mp3, mpeg, multimedia ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; The cache is a mapping of a full path name to information, and so ;; it is invalidated when you rename or move files about. It also ;; does not differentiate between file or uri tracks. ;; Because cache lookups are much faster than disk access, this works ;; much better with a later-do-interval of something like 0.001. Also ;; consider using synchronous mode, as it's quite fast now. ;; This code is activated by (emms-standard) and above. ;; To activate it by hand, use: ;; (emms-cache 1) ;;; Code: (require 'emms) (require 'emms-info) (when (fboundp 'define-hash-table-test) (define-hash-table-test 'string-hash 'string= 'sxhash)) (defvar emms-cache-db (make-hash-table :test (if (fboundp 'define-hash-table-test) 'string-hash 'equal)) "A mapping of paths to file info. This is used to cache over emacs sessions.") (defvar emms-cache-dirty nil "True if the cache has been updated since init.") (defcustom emms-cache-file (concat (file-name-as-directory emms-directory) "cache") "A file used to store cached file information over sessions." :group 'emms :type 'file) (defcustom emms-cache-file-coding-system 'utf-8 "Coding system used for saving `emms-cache-file'." :group 'emms :type 'coding-system) (defun emms-cache (arg) "Turn on Emms caching if ARG is positive, off otherwise." (interactive "p") (if (and arg (> arg 0)) (progn (unless emms-cache-dirty (emms-cache-restore)) (unless noninteractive (add-hook 'kill-emacs-hook 'emms-cache-save)) (setq emms-cache-get-function 'emms-cache-get) (setq emms-cache-set-function 'emms-cache-set) (setq emms-cache-modified-function 'emms-cache-dirty)) (remove-hook 'kill-emacs-hook 'emms-cache-save) (setq emms-cache-get-function nil) (setq emms-cache-set-function nil) (setq emms-cache-modified-function nil))) ;;;###autoload (defun emms-cache-enable () "Enable caching of Emms track data." (interactive) (emms-cache 1) (message "Emms cache enabled")) ;;;###autoload (defun emms-cache-disable () "Disable caching of Emms track data." (interactive) (emms-cache -1) (message "Emms cache disabled")) ;;;###autoload (defun emms-cache-toggle () "Toggle caching of Emms track data." (interactive) (if emms-cache-get-function (emms-cache-disable) (emms-cache-enable))) (defsubst emms-cache-dirty (&rest ignored) "Mark the cache as dirty." (setq emms-cache-dirty t)) (defun emms-cache-get (type path) "Return a cache element for PATH, or nil." (gethash path emms-cache-db)) ;; Note we ignore TYPE, as it's stored in TRACK (defun emms-cache-set (type path track) "Set PATH to TRACK in the cache." (puthash path track emms-cache-db) (emms-cache-dirty)) (defun emms-cache-del (path) "Remove a track from the cache, with key PATH." (remhash path emms-cache-db) (emms-cache-dirty)) (defun emms-cache-save () "Save the track cache to a file." (interactive) (when emms-cache-dirty (message "Saving emms track cache...") (set-buffer (get-buffer-create " emms-cache ")) (erase-buffer) (insert (concat ";;; .emms-cache -*- mode: emacs-lisp; coding: " (symbol-name emms-cache-file-coding-system) "; -*-\n")) (maphash (lambda (k v) (insert (format "(puthash %S '%S emms-cache-db)\n" k v))) emms-cache-db) (when (fboundp 'set-buffer-file-coding-system) (set-buffer-file-coding-system emms-cache-file-coding-system)) (unless (file-directory-p (file-name-directory emms-cache-file)) (make-directory (file-name-directory emms-cache-file))) (write-region (point-min) (point-max) emms-cache-file) (kill-buffer (current-buffer)) (message "Saving emms track cache...done") (setq emms-cache-dirty nil))) (defun emms-cache-restore () "Restore the track cache from a file." (interactive) (load emms-cache-file t nil t) (setq emms-cache-dirty nil)) (defun emms-cache-sync () "Sync the cache with the data on disc. Remove non-existent files, and update data for files which have been modified." (interactive) (message "Syncing emms track cache...") (let (removed) (maphash (lambda (path track) (when (eq (emms-track-get track 'type) 'file) ;; if no longer here, remove (if (not (file-exists-p path)) (progn (remhash path emms-cache-db) (setq removed t)) (let ((file-mtime (emms-info-track-file-mtime track)) (info-mtime (emms-track-get track 'info-mtime))) (when (or (not info-mtime) (emms-time-less-p info-mtime file-mtime)) (run-hook-with-args 'emms-info-functions track)))))) emms-cache-db) (when removed (setq emms-cache-dirty t))) (message "Syncing emms track cache...done")) (provide 'emms-cache) ;;; emms-cache.el ends here ������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-compat.el���������������������������������������������0000664�0000000�0000000�00000013222�12205151475�0021575�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-compat.el --- Compatibility routines for EMMS ;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Michael Olson <mwolson@gnu.org> ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; These are functions and macros that EMMS needs in order to be ;; compatible with various Emacs and XEmacs versions. ;;; Code: ;;; Miscellaneous (defun emms-propertize (string &rest properties) (if (fboundp 'propertize) (apply #'propertize string properties) (set-text-properties 0 (length string) properties string) string)) ;; Emacs accepts three arguments to `make-obsolete', but the XEmacs ;; version only takes two arguments (defun emms-make-obsolete (old-name new-name when) "Make the byte-compiler warn that OLD-NAME is obsolete. The warning will say that NEW-NAME should be used instead. WHEN should be a string indicating when the function was first made obsolete, either the file's revision number or an EMMS release version number." (if (featurep 'xemacs) (make-obsolete old-name new-name) (make-obsolete old-name new-name when))) ;;; Time and timers (defun emms-cancel-timer (timer) "Cancel the given TIMER." (when timer (cond ((fboundp 'cancel-timer) (cancel-timer timer)) ((fboundp 'delete-itimer) (delete-itimer timer))))) (defun emms-time-less-p (t1 t2) "Say whether time T1 is less than time T2." (or (< (car t1) (car t2)) (and (= (car t1) (car t2)) (< (nth 1 t1) (nth 1 t2))))) ;;; Highline (defun emms-activate-highlighting-mode () "Activate highline mode." (if (featurep 'xemacs) (progn (require 'highline) (highline-local-mode 1)) (progn (require 'hl-line) (hl-line-mode 1)))) (defun emms-line-highlight () "Highlight the current line. You must call emms-activate-highlighting-mode beforehand." (if (featurep 'xemacs) (highline-highlight-current-line) (hl-line-highlight))) ;;; Movement and position (defun emms-move-beginning-of-line (arg) "Move point to beginning of current line as displayed. If there's an image in the line, this disregards newlines which are part of the text that the image rests on." (if (fboundp 'move-beginning-of-line) (move-beginning-of-line arg) (if (numberp arg) (forward-line (1- arg)) (forward-line 0)))) (defun emms-line-number-at-pos (&optional pos) "Return (narrowed) buffer line number at position POS. If POS is nil, use current buffer location." (if (fboundp 'line-number-at-pos) (line-number-at-pos pos) (let ((opoint (or pos (point))) start) (save-excursion (goto-char (point-min)) (setq start (point)) (goto-char opoint) (forward-line 0) (1+ (count-lines start (point))))))) ;;; Regular expression matching (defun emms-replace-regexp-in-string (regexp replacement text &optional fixedcase literal) "Replace REGEXP with REPLACEMENT in TEXT. If fourth arg FIXEDCASE is non-nil, do not alter case of replacement text. If fifth arg LITERAL is non-nil, insert REPLACEMENT literally." (cond ((fboundp 'replace-regexp-in-string) (replace-regexp-in-string regexp replacement text fixedcase literal)) ((and (featurep 'xemacs) (fboundp 'replace-in-string)) (replace-in-string text regexp replacement literal)) (t (let ((repl-len (length replacement)) start) (save-match-data (while (setq start (string-match regexp text start)) (setq start (+ start repl-len) text (replace-match replacement fixedcase literal text))))) text))) (defun emms-match-string-no-properties (num &optional string) (if (fboundp 'match-string-no-properties) (match-string-no-properties num string) (match-string num string))) ;;; Common Lisp (defun emms-delete-if (predicate seq) "Remove all items satisfying PREDICATE in SEQ. This is a destructive function: it reuses the storage of SEQ whenever possible." ;; remove from car (while (when (funcall predicate (car seq)) (setq seq (cdr seq)))) ;; remove from cdr (let ((ptr seq) (next (cdr seq))) (while next (when (funcall predicate (car next)) (setcdr ptr (if (consp next) (cdr next) nil))) (setq ptr (cdr ptr)) (setq next (cdr ptr)))) seq) (defun emms-find-if (predicate seq) "Find the first item satisfying PREDICATE in SEQ. Return the matching item, or nil if not found." (catch 'found (dolist (el seq) (when (funcall predicate el) (throw 'found el))))) (defun emms-remove-if-not (predicate seq) "Remove all items not satisfying PREDICATE in SEQ. This is a non-destructive function; it makes a copy of SEQ to avoid corrupting the original SEQ." (let (newseq) (dolist (el seq) (when (funcall predicate el) (setq newseq (cons el newseq)))) (nreverse newseq))) (provide 'emms-compat) ;;; emms-compat.el ends here ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-cue.el������������������������������������������������0000664�0000000�0000000�00000011166�12205151475�0021073�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-cue.el --- Recognize cue sheet file ;; Copyright (C) 2009 Free Software Foundation, Inc. ;; Author: William Xu <william.xwl@gmail.com> ;; This file is part of EMMS. ;; EMMS 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. ;; EMMS 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 EMMS; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;; By parsing cue file, we will be able to play next/previous track from a ;; single .ape or .flac file. ;;; Code: (require 'emms-playing-time) (require 'emms-info) (defun emms-cue-next () "Play next track from .cue file." (interactive) (let ((cue-track (emms-cue-next-track))) (if (cdr cue-track) (progn (emms-seek-to (cdr cue-track)) (message "Will play: %s" (car cue-track))) (message "Nothing to seek or missing .cue file?")))) (defun emms-cue-previous () "Play previous track from .cue file." (interactive) (let ((cue-track (emms-cue-previous-track))) (if (cdr cue-track) (progn (emms-seek-to (cdr cue-track)) (message "Will play: %s" (car cue-track))) (message "Nothing to seek or missing .cue file?")))) (defun emms-cue-next-track (&optional previous-p) "Get title and offset of next track from .cue file. When PREVIOUS-P is t, get previous track info instead." (let* ((track (emms-playlist-current-selected-track)) (name (emms-track-get track 'name)) (cue (concat (file-name-sans-extension name)".cue"))) (when (file-exists-p cue) (with-temp-buffer (emms-insert-file-contents cue) (save-excursion (if previous-p (goto-char (point-max)) (goto-char (point-min))) (let ((offset nil) (title "") ;; We should search one more track far when getting previous ;; track. (one-more-track previous-p)) (while (and (not offset) (funcall (if previous-p 'search-backward-regexp 'search-forward-regexp) "INDEX 01 \\([0-9][0-9]\\):\\([0-9][0-9]\\):\\([0-9][0-9]\\)" nil t 1)) (let* ((min (string-to-number (match-string-no-properties 1))) (sec (string-to-number (match-string-no-properties 2))) (msec (string-to-number (match-string-no-properties 3))) (total-sec (+ (* min 60) sec (/ msec 100.0)))) (when (funcall (if previous-p '> '<) emms-playing-time total-sec) (if (not one-more-track) (progn (setq offset total-sec) (when (search-backward-regexp "TITLE \"\\(.*\\)\"" nil t 1) (setq title (match-string-no-properties 1)))) (setq one-more-track nil))))) (cons title offset))))))) (defun emms-cue-previous-track () "See `emms-cue-next-track'." (emms-cue-next-track t)) (defun emms-info-cueinfo (track) "Add track information to TRACK. This is a useful element for `emms-info-functions'." (when (and (eq 'file (emms-track-type track)) (string-match "\\.\\(ape\\|flac\\)\\'" (emms-track-name track))) (let ((cue (concat (file-name-sans-extension (emms-track-name track)) ".cue"))) (when (file-exists-p cue) (with-temp-buffer (emms-insert-file-contents cue) (save-excursion (mapc (lambda (i) (goto-char (point-min)) (when (let ((case-fold-search t)) (search-forward-regexp (concat (car i) " \\(.*\\)") nil t 1)) (emms-track-set track (cdr i) (replace-regexp-in-string "\\`\"\\|\"\\'" "" (match-string 1))))) '(("performer" . info-artist) ("title" . info-album) ("title" . info-title) ("rem date" . info-year))))))))) (provide 'emms-cue) ;;; emms-cue.el ends here ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-history.el��������������������������������������������0000664�0000000�0000000�00000011365�12205151475�0022021�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-history.el -- save all playlists when exiting emacs ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; ;; Author: Ye Wenbin <wenbinye@163.com> ;; This file is part of EMMS. ;; 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, or (at your option) ;; any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ;;; Commentary: ;; Saves all playlists when you close emacs. When you start it up again use ;; M-x emms-history-load to restore all saved playlists. ;; To use it put the following into your ~/.emacs: ;; ;; (require 'emms-history) ;; ;; If all playlists should be restored on startup add this, too: ;; ;; (emms-history-load) ;;; Code: (require 'emms) (eval-when-compile (require 'cl)) (defgroup emms-history nil "Saving and restoring all playlists when closing/restarting Emacs." :prefix "emms-history-" :group 'emms) (defcustom emms-history-file (concat (file-name-as-directory emms-directory) "history") "The file to save playlists in." :type 'string :group 'emms-history) (defcustom emms-history-start-playing nil "If non-nil emms starts playing the current track after `emms-history-load' was invoked." :type 'boolean :group 'emms-history) (defcustom emms-history-file-coding-system 'utf-8 "Coding system used for saving `emms-history-file'." :type 'coding-system :group 'emms-history) (defun emms-history-save () "Save all playlists that are open in this Emacs session." (interactive) (when (stringp emms-history-file) (let ((oldbuf emms-playlist-buffer) ;; print with no limit print-length print-level emms-playlist-buffer playlists) (save-excursion (dolist (buf (emms-playlist-buffer-list)) (set-buffer buf) (when (> (buffer-size) 0) ; make sure there is track in the buffer (setq emms-playlist-buffer buf playlists (cons (list (buffer-name) (or (and emms-playlist-selected-marker (marker-position emms-playlist-selected-marker)) (point-min)) (save-restriction (widen) (nreverse (emms-playlist-tracks-in-region (point-min) (point-max))))) playlists)))) (with-temp-buffer (insert (concat ";;; emms history -*- mode: emacs-lisp; coding: " (symbol-name emms-history-file-coding-system) "; -*-\n")) (insert "(\n;; active playlist\n") (prin1 (buffer-name oldbuf) (current-buffer)) (insert "\n;; playlists: ((BUFFER_NAME SELECT_POSITION TRACKS) ...)\n") (prin1 playlists (current-buffer)) (insert "\n;; play method\n") (prin1 `((emms-repeat-track . ,emms-repeat-track) (emms-repeat-playlist . ,emms-repeat-playlist)) (current-buffer)) (insert "\n)") (write-file emms-history-file)))))) (unless noninteractive (add-hook 'kill-emacs-hook 'emms-history-save)) (defun emms-history-load () "Restore all playlists in `emms-history-file'." (interactive) (when (and (stringp emms-history-file) (file-exists-p emms-history-file)) (let (history buf) (with-temp-buffer (emms-insert-file-contents emms-history-file) (setq history (read (current-buffer))) (dolist (playlist (cadr history)) (with-current-buffer (emms-playlist-new (car playlist)) (setq emms-playlist-buffer (current-buffer)) (if (string= (car playlist) (car history)) (setq buf (current-buffer))) (mapc 'emms-playlist-insert-track (nth 2 playlist)) (ignore-errors (emms-playlist-select (cadr playlist))))) (setq emms-playlist-buffer buf) (dolist (method (nth 2 history)) (set (car method) (cdr method))) (ignore-errors (when emms-history-start-playing (emms-start))))))) (provide 'emms-history) ;;; emms-history.el ends here ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-i18n.el�����������������������������������������������0000664�0000000�0000000�00000015771�12205151475�0021104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-i18n.el --- functions for handling coding systems ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Ye Wenbin <wenbinye@163.com> ;; This file is part of EMMS. ;; 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, or (at ;; your option) any later version. ;; This program is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ;;; Commentary: ;; When reading from process, first check the car part of ;; `emms-i18n-default-coding-system'; if non-nil, use this for ;; decoding, and never detect coding system; if nil, first call ;; `emms-i18n-coding-detect-functions' to get coding system, if ;; success, decode the result, otherwise, use ;; `emms-i18n-detect-coding-function', the Emacs detect coding ;; function, if the coding detected is not in ;; `emms-i18n-never-used-coding-system', decode it, otherwise use ;; locale-coding-system. ;; When writing/sending data to process, first check the cdr part of ;; `emms-i18n-default-coding-system', if non-nil, use this to encode ;; data, otherwise do nothing, that means use ;; `default-process-coding-system' or `process-coding-system-alist' to ;; encode data. ;; Put this file into your load-path and the following into your ;; ~/.emacs: ;; (require 'emms-i18n) ;;; Code: (provide 'emms-i18n) (eval-when-compile (require 'cl)) ;; TODO: Use defcustom. (defvar emms-i18n-never-used-coding-system '(raw-text undecided) "If the `emms-i18n-coding-detect-functions' return a coding system in this list, use `emms-i18n-default-coding-system' instead.") ;; TODO: Use defcustom. (defvar emms-i18n-coding-system-for-read 'utf-8 "If coding detect fails, use this for decoding.") ;; TODO: Use defcustom. (defvar emms-i18n-default-coding-system '(no-conversion . no-conversion) "If non-nil, use this for decoding and encoding.") ;; TODO: Use defcustom. (defvar emms-i18n-coding-detect-functions nil "A list of functions to call to detect codings.") ;; TODO: Use defcustom. (defvar emms-i18n-detect-max-size 10000 "Maximum amount of bytes to detect the coding system. nil means to scan the whole buffer.") (defun emms-i18n-iconv (from to str) "Convert string STR from FROM coding to TO coding." (if (and from to) (decode-coding-string (encode-coding-string str to) from) str)) (defun emms-i18n-iconv-region (beg end from to) (when (and from to) (save-restriction (narrow-to-region beg end) (encode-coding-region (point-min) (point-max) to) (decode-coding-region (point-min) (point-max) from)))) (defun emms-i18n-iconv-buffer (from to &optional buf) "Convert buffer BUF from FROM coding to TO coding. BUF defaults to the current buffer." (save-excursion (and buf (set-buffer buf)) (emms-i18n-iconv-region (point-min) (point-max) from to))) (defun emms-i18n-set-default-coding-system (read-coding write-coding) "Set `emms-i18n-default-coding-system'." (interactive "zSet coding system for read: \nzSet coding system for write: ") (setq emms-i18n-default-coding-system (cons (and (coding-system-p read-coding) read-coding) (and (coding-system-p write-coding) write-coding))) (message (concat (if (car emms-i18n-default-coding-system) (format "The coding system for reading is %S." (car emms-i18n-default-coding-system)) "Good, you want me to detect the coding system!") (format " The coding system for writing is %S." (or (cdr emms-i18n-default-coding-system) (cdr default-process-coding-system)))))) (defun emms-i18n-call-process-simple (&rest args) "Run a program and return the program result. If the car part of `emms-i18n-default-coding-system' is non-nil, the program result will be decoded using the car part of `emms-i18n-default-coding-system'. Otherwise, use `emms-i18n-coding-detect-functions' to detect the coding system of the result. If the `emms-i18n-coding-detect-functions' failed, use `emms-i18n-detect-coding-function' to detect coding system. If all the coding systems are nil or in `emms-i18n-never-used-coding-system', decode the result using `emms-i18n-coding-system-for-read'. ARGS are the same as in `call-process', except BUFFER should always have the value t. Otherwise the coding detection will not be performed." (let ((default-process-coding-system (copy-tree default-process-coding-system)) (process-coding-system-alist nil) exit pos) (when (eq (nth 2 args) 't) (setcar default-process-coding-system (car emms-i18n-default-coding-system)) (setq pos (point))) (setq exit (apply 'call-process args)) (when (and (eq (nth 2 args) 't) (eq (car emms-i18n-default-coding-system) 'no-conversion)) (save-restriction (narrow-to-region pos (point)) (decode-coding-region (point-min) (point-max) (emms-i18n-detect-buffer-coding-system)))) exit)) ;; TODO: Is this function useful? (defun emms-i18n-call-process (&rest args) "Run the program like `call-process'. If the cdr part of `emms-i18n-default-coding-system' is non-nil, the string in ARGS will be encoded by the cdr part of `emms-i18n-default-coding-system'; otherwise, all parameters are simply passed to `call-process'." (with-temp-buffer (if (cdr emms-i18n-default-coding-system) (let ((default-process-coding-system emms-i18n-default-coding-system) (process-coding-system-alist nil)) (apply 'call-process args)) (apply 'call-process args)))) (defun emms-i18n-detect-coding-function (size) (detect-coding-region (point) (+ (if (null emms-i18n-detect-max-size) size (min size emms-i18n-detect-max-size)) (point)) t)) (defun emms-i18n-detect-buffer-coding-system (&optional buf) "Before calling this function, make sure the buffer is literal." (let ((size (- (point-max) (point-min))) (func (append emms-i18n-coding-detect-functions 'emms-i18n-detect-coding-function)) coding) (save-excursion (and buf (set-buffer buf)) (goto-char (point-min)) (when (> size 0) (setq coding (run-hook-with-args-until-success 'func size)) (if (member (coding-system-base coding) emms-i18n-never-used-coding-system) (setq coding (emms-i18n-detect-coding-function size)))) (if (or (null coding) (member (coding-system-base coding) emms-i18n-never-used-coding-system)) emms-i18n-coding-system-for-read coding)))) ;;; emms-i18n.el ends here �������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-info-libtag.el����������������������������������������0000664�0000000�0000000�00000005704�12205151475�0022513�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-info-libtag.el --- Info-method for EMMS using libtag ;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, ;; 2009 Free Software Foundation, Inc. ;; Authors: Ulrik Jensen <terryp@daimi.au.dk> ;; Jorgen Schäfer <forcer@forcix.cx> ;; Keywords: ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; This code has been adapted from code found in mp3player.el, written ;; by Jean-Philippe Theberge (jphiltheberge@videotron.ca), Mario ;; Domgoergen (kanaldrache@gmx.de) and Jorgen Schäfer ;; <forcer@forcix.cx> ;; To activate this method for getting info, use something like: ;; (require 'emms-info-libtag) ;; (add-to-list 'emms-info-functions 'emms-info-libtag) ;; Note that you should remove emms-info-mp3info and emms-info-ogginfo ;; from the emms-info-functions list if you want to avoid ;; conflicts. For example, to set libtag as your exclusive info ;; provider: ;; (setq emms-info-functions '(emms-info-libtag)) ;;; Code: (require 'emms-info) (defvar emms-info-libtag-coding-system 'utf-8) (defvar emms-info-libtag-program-name "emms-print-metadata") (defun emms-info-libtag (track) (when (and (eq 'file (emms-track-type track)) (string-match "\\.\\([Mm][Pp]3\\|[oO][gG][gG]\\|[fF][lL][aA][cC]\\|[sS][pP][xX]\\)\\'" (emms-track-name track))) (with-temp-buffer (when (zerop (let ((coding-system-for-read 'utf-8)) (call-process emms-info-libtag-program-name nil '(t nil) nil (emms-track-name track)))) (goto-char (point-min)) ;; Crush the trailing whitespace (while (re-search-forward "[[:space:]]+$" nil t) (replace-match "" nil nil)) (goto-char (point-min)) (while (looking-at "^\\([^=\n]+\\)=\\(.*\\)$") (let ((name (intern-soft (match-string 1))) (value (match-string 2))) (when (> (length value) 0) (emms-track-set track name (if (eq name 'info-playing-time) (string-to-number value) value)))) (forward-line 1)))))) (provide 'emms-info-libtag) ;;; emms-info-libtag.el ends here ������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-info-metaflac.el��������������������������������������0000664�0000000�0000000�00000007521�12205151475�0023024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-info-metaflac.el --- Info-method for EMMS using metaflac ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Matthew Kennedy <mkennedy@gentoo.org> ;; Keywords: ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; This file 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 GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ;; Boston, MA 02110-1301 USA ;;; Commentary: ;; This code has been adapted from code found in emms-info-mp3info.el ;; written by Ulrik Jensen <terryp@daimi.au.dk> which contains the ;; following attribution: ;; This code has been adapted from code found in mp3player.el, written ;; by Jean-Philippe Theberge (jphiltheberge@videotron.ca), Mario ;; Domgoergen (kanaldrache@gmx.de) and Jorgen Schäfer ;; <forcer@forcix.cx> ;; To activate this method for getting info, use something like: ;; (require 'emms-info-metaflac) ;; (add-to-list 'emms-info-methods-list 'emms-info-metaflac) ;;; Code: (eval-when-compile (require 'cl)) (require 'emms-info) (defvar emms-info-metaflac-version "0.1 $Revision: 1.10 $" "EMMS info metaflac version string.") ;; $Id: emms-info-mp3info.el,v 1.10 2005/08/12 18:01:16 xwl Exp $ (defgroup emms-info-metaflac nil "An EMMS-info method for getting/setting FLAC tags, using the external metaflac program" :group 'emms-info) (defcustom emms-info-metaflac-program-name "metaflac" "*The name/path of the metaflac program." :type 'string :group 'emms-info-metaflac) (defcustom emms-info-metaflac-options '("--no-utf8-convert" "--show-tag=TITLE" "--show-tag=ARTIST" "--show-tag=ALBUM" "--show-tag=NOTE" "--show-tag=YEAR" "--show-tag=TRACKNUMBER" "--show-tag=DISCNUMBER" "--show-tag=GENRE") "The argument to pass to `emms-info-metaflac-program-name'." :type '(repeat string) :group 'emms-info-metaflac) (defun emms-info-metaflac (track) "Get the FLAC tag of file TRACK, using `emms-info-metaflac-program' and return an emms-info structure representing it." (when (and (eq 'file (emms-track-type track)) (string-match "\\.\\(flac\\|FLAC\\)\\'" (emms-track-name track))) (with-temp-buffer (when (zerop (apply 'call-process emms-info-metaflac-program-name nil t nil "--show-total-samples" "--show-sample-rate" (append emms-info-metaflac-options (list (emms-track-name track))))) (goto-char (point-min)) (emms-track-set track 'info-playing-time (/ (string-to-number (buffer-substring (point) (line-end-position))) (progn (forward-line 1) (string-to-number (buffer-substring (point) (line-end-position)))))) (forward-line 1) (while (looking-at "^\\([^=\n]+\\)=\\(.*\\)$") (let ((name (intern (concat "info-" (downcase (match-string 1))))) (value (match-string 2))) (when (> (length value) 0) (emms-track-set track name (if (eq name 'info-playing-time) (string-to-number value) value)))) (forward-line 1)))))) (provide 'emms-info-metaflac) ;;; emms-info-metaflac.el ends here �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-info-mp3info.el���������������������������������������0000664�0000000�0000000�00000007237�12205151475�0022627�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-info-mp3info.el --- Info-method for EMMS using mp3info ;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, ;; 2009 Free Software Foundation, Inc. ;; Authors: Ulrik Jensen <terryp@daimi.au.dk> ;; Jorgen Schäfer <forcer@forcix.cx> ;; Keywords: ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; This code has been adapted from code found in mp3player.el, written ;; by Jean-Philippe Theberge (jphiltheberge@videotron.ca), Mario ;; Domgoergen (kanaldrache@gmx.de) and Jorgen Schäfer ;; <forcer@forcix.cx> ;; To activate this method for getting info, use something like: ;; (require 'emms-info-mp3info) ;; (add-to-list 'emms-info-functions 'emms-info-mp3info) ;;; Code: (require 'emms-info) (defvar emms-info-mp3info-version "0.2 $Revision: 1.10 $" "EMMS info mp3info version string.") ;; $Id: emms-info-mp3info.el,v 1.10 2005/08/12 18:01:16 xwl Exp $ (defgroup emms-info-mp3info nil "An EMMS-info method for getting/setting ID3v1 tags, using the external mp3info program" :group 'emms-info) (defcustom emms-info-mp3info-coding-system 'utf-8 "*Coding system used in the output of mp3info." :type 'coding-system :group 'emms-info-mp3info) (defcustom emms-info-mp3info-program-name "mp3info" "*The name/path of the mp3info tag program." :type 'string :group 'emms-info-mp3info) (defcustom emms-info-mp3find-arguments `("-p" ,(concat "info-artist=%a\\n" "info-title=%t\\n" "info-album=%l\\n" "info-tracknumber=%n\\n" "info-year=%y\\n" "info-genre=%g\\n" "info-note=%c\\n" "info-playing-time=%S\\n")) "The argument to pass to `emms-info-mp3info-program-name'. This should be a list of info-flag=value lines." :type '(repeat string) :group 'emms-info-mp3info) (defun emms-info-mp3info (track) "Add track information to TRACK. This is a useful element for `emms-info-functions'." (when (and (eq 'file (emms-track-type track)) (string-match "\\.[Mm][Pp]3\\'" (emms-track-name track))) (with-temp-buffer (when (zerop (apply (if (fboundp 'emms-i18n-call-process-simple) 'emms-i18n-call-process-simple 'call-process) emms-info-mp3info-program-name nil t nil (append emms-info-mp3find-arguments (list (emms-track-name track))))) (goto-char (point-min)) (while (looking-at "^\\([^=\n]+\\)=\\(.*\\)$") (let ((name (intern (match-string 1))) (value (match-string 2))) (when (> (length value) 0) (emms-track-set track name (if (eq name 'info-playing-time) (string-to-number value) value)))) (forward-line 1)))))) (provide 'emms-info-mp3info) ;;; emms-info-mp3info.el ends here �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-info-ogginfo.el���������������������������������������0000664�0000000�0000000�00000006146�12205151475�0022702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-info-ogginfo.el --- Emms information from Ogg Vorbis files. ;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Jorgen Schaefer <forcer@forcix.cx> ;; Yoni Rabkin <yonirabkin@member.fsf.org> ;; This file is part of EMMS. ;; EMMS 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. ;; EMMS 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 EMMS; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;; ;;; Code: (require 'emms-info) (defgroup emms-info-ogginfo nil "An EMMS-info method for getting, using the external ogginfo program" :group 'emms-info) (defcustom emms-info-ogginfo-coding-system 'utf-8 "*Coding system used in the output of ogginfo." :type 'coding-system :group 'emms-info-ogginfo) (defcustom emms-info-ogginfo-program-name "ogginfo" "*The name/path of the ogginfo tag program." :type 'string :group 'emms-info-ogginfo) (defun emms-info-ogginfo (track) "Add track information to TRACK. This is a useful element for `emms-info-functions'." (when (and (eq 'file (emms-track-type track)) (string-match "\\.[Oo][Gg][Gg]\\'" (emms-track-name track))) (with-temp-buffer (call-process emms-info-ogginfo-program-name nil t nil (emms-track-name track)) ;; play time, emms-info-ogg.el [U. Jensen] (goto-char (point-min)) (when (re-search-forward "Playback length: \\([0-9]*\\)m:\\([0-9]*\\)" nil t) (let* ((minutes (string-to-number (match-string 1))) (seconds (string-to-number (match-string 2))) (ptime-total (+ (* minutes 60) seconds)) (ptime-min minutes) (ptime-sec seconds)) (emms-track-set track 'info-playing-time ptime-total) (emms-track-set track 'info-playing-time-min ptime-min) (emms-track-set track 'info-playing-time-sec ptime-sec) (emms-track-set track 'info-file (emms-track-name track)))) ;; all the rest of the info available (goto-char (point-min)) (when (re-search-forward "^.*\\.\\.\\.$" (point-max) t) (while (zerop (forward-line 1)) (when (looking-at "^\t\\(.*?\\)=\\(.*\\)$") ; recognize the first '=' (let ((a (match-string 1)) (b (match-string 2))) (when (and (< 0 (length a)) (< 0 (length b))) (emms-track-set track (intern (downcase (concat "info-" (match-string 1)))) (match-string 2)))))))))) (provide 'emms-info-ogginfo) ;;; emms-info-ogginfo.el ends here ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-info.el�����������������������������������������������0000664�0000000�0000000�00000011531�12205151475�0021246�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-info.el --- Retrieving track information ;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation Inc. ;; Author: Jorgen Schaefer <forcer@forcix.cx> ;; This file is part of EMMS. ;; EMMS 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. ;; EMMS 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 EMMS; if not, write to the Free Software ;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ;; 02110-1301, USA. ;;; Commentary: ;; This EMMS module provides a way to add information for a track. ;; This can use an ID3 or OGG comment like syntax. ;; The code will add info symbols to the track. The following symbols ;; are defined: ;; info-artist - string naming the artist ;; info-composer - string naming the composer ;; info-performer - string naming the performer ;; info-title - string naming the title of the song ;; info-album - string naming the album ;; info-tracknumber - string(?) naming the track number ;; info-discnumber - string naming the disc number ;; info-year - string naming the year ;; info-note - string of free-form entry ;; info-genre - string naming the genre ;; info-playing-time - number giving the seconds of playtime ;;; Code: (require 'emms) (require 'later-do) (defgroup emms-info nil "*Track information. ID3, OGG, etc." :group 'emms) (defcustom emms-info-auto-update t "*Non-nil when EMMS should update track information if the file changes. This will cause hard drive activity on track loading. If this is too annoying for you, set this variable to nil." :type 'boolean :group 'emms-info) (defcustom emms-info-asynchronously t "*Non-nil when track information should be loaded asynchronously. This requires `later-do', which should come with EMMS." :type 'boolean :group 'emms-info) (defcustom emms-info-report-each-num-tracks 200 "*Non-zero will report progress information every number of tracks. The default is to display a message every 200 tracks. This variable is only used when adding tracks asynchronously." :type 'integer :group 'emms-info) (defcustom emms-info-functions nil "*Functions which add information to tracks. Each is called with a track as argument." :type 'hook :group 'emms-info) (defvar emms-info-asynchronous-tracks 0 "Number of tracks we're waiting for to be done.") (defun emms-info-initialize-track (track) "Initialize TRACK with emms-info information. This is a suitable value for `emms-track-initialize-functions'." (if (not emms-info-asynchronously) (emms-info-really-initialize-track track) (setq emms-info-asynchronous-tracks (1+ emms-info-asynchronous-tracks)) (later-do 'emms-info-really-initialize-track track))) (defun emms-info-really-initialize-track (track) "Really initialize TRACK. Return t when the track got changed." (let ((file-mtime (when emms-info-auto-update (emms-info-track-file-mtime track))) (info-mtime (emms-track-get track 'info-mtime)) (name (emms-track-get track 'name))) ;; if the file's been modified or is new (when (or (not file-mtime) (not info-mtime) (emms-time-less-p info-mtime file-mtime)) (run-hook-with-args 'emms-info-functions track) ;; not set by info functions (when file-mtime (emms-track-set track 'info-mtime file-mtime)) (emms-track-updated track)) (when emms-info-asynchronously (setq emms-info-asynchronous-tracks (1- emms-info-asynchronous-tracks)) (if (zerop emms-info-asynchronous-tracks) (message "EMMS: All track information loaded.") (unless (zerop emms-info-report-each-num-tracks) (if (zerop (mod emms-info-asynchronous-tracks emms-info-report-each-num-tracks)) (message "EMMS: %d tracks to go.." emms-info-asynchronous-tracks))))))) (defun emms-info-track-file-mtime (track) "Return the mtime of the file of TRACK, if any. Return nil otherwise." (if (eq (emms-track-type track) 'file) (nth 5 (file-attributes (emms-track-name track))) nil)) (defun emms-info-track-description (track) "Return a description of TRACK." (let ((artist (emms-track-get track 'info-artist)) (title (emms-track-get track 'info-title))) (cond ((and artist title) (concat artist " - " title)) (title title) (t (emms-track-simple-description track))))) (provide 'emms-info) ;;; emms-info.el ends here �����������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-last-played.el����������������������������������������0000664�0000000�0000000�00000011667�12205151475�0022544�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-last-played.el --- Support for last-played-time of a track ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Lucas Bonnet <lucas@rincevent.net> ;; Keywords: emms, mp3, mpeg, multimedia ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; Records when the track was last played. ;; Big portions of the time handling fuctions are copied from ;; gnus-util.el, and slightly adapted. ;;; Code: (require 'emms) (defvar emms-last-played-keep-count t "Specifies if EMMS should record the number of times you play a track. Set it to t if you want such a feature, and to nil if you don't.") (defvar emms-last-played-format-alist '(((emms-last-played-seconds-today) . "%k:%M") (604800 . "%a %k:%M") ;;that's one week ((emms-last-played-seconds-month) . "%a %d") ((emms-last-played-seconds-year) . "%b %d") (t . "%b %d '%y")) ;;this one is used when no ;;other does match "Specifies date format depending on when a track was last played. This is an alist of items (AGE . FORMAT). AGE can be a number (of seconds) or a Lisp expression evaluating to a number. When the age of the track is less than this number, then use `format-time-string' with the corresponding FORMAT for displaying the date of the track. If AGE is not a number or a Lisp expression evaluating to a non-number, then the corresponding FORMAT is used as a default value. Note that the list is processed from the beginning, so it should be sorted by ascending AGE. Also note that items following the first non-number AGE will be ignored. You can use the functions `emms-last-played-seconds-today', `emms-last-played-seconds-month' and `emms-last-played-seconds-year' in the AGE spec. They return the number of seconds passed since the start of today, of this month, of this year, respectively.") (defun emms-last-played-update-track (track) "Updates the last-played time of TRACK." (emms-track-set track 'last-played (current-time))) (defun emms-last-played-increment-count (track) "Increments the play-count property of TRACK. If non-existent, it is set to 1." (let ((play-count (emms-track-get track 'play-count))) (if play-count (emms-track-set track 'play-count (1+ play-count)) (emms-track-set track 'play-count 1)))) (defun emms-last-played-update-current () "Updates the current track." (emms-last-played-update-track (emms-playlist-current-selected-track)) (if emms-last-played-keep-count (emms-last-played-increment-count (emms-playlist-current-selected-track)))) (defun emms-last-played-seconds-today () "Return the number of seconds passed today." (let ((now (decode-time (current-time)))) (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600)))) (defun emms-last-played-seconds-month () "Return the number of seconds passed this month." (let ((now (decode-time (current-time)))) (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600) (* (- (car (nthcdr 3 now)) 1) 3600 24)))) (defun emms-last-played-seconds-year () "Return the number of seconds passed this year." (let ((now (decode-time (current-time))) (days (format-time-string "%j" (current-time)))) (+ (car now) (* (car (cdr now)) 60) (* (car (nthcdr 2 now)) 3600) (* (- (string-to-number days) 1) 3600 24)))) (defun emms-last-played-format-date (messy-date) "Format the messy-date according to emms-last-played-format-alist. Returns \" ? \" if there's bad input or if an other error occurs. Input should look like this: \"Sun, 14 Oct 2001 13:34:39 +0200\"." (condition-case () (let* ((messy-date (float-time messy-date)) (now (float-time (current-time))) ;;If we don't find something suitable we'll use this one (my-format "%b %d '%y")) (let* ((difference (- now messy-date)) (templist emms-last-played-format-alist) (top (eval (caar templist)))) (while (if (numberp top) (< top difference) (not top)) (progn (setq templist (cdr templist)) (setq top (eval (caar templist))))) (if (stringp (cdr (car templist))) (setq my-format (cdr (car templist))))) (format-time-string (eval my-format) (seconds-to-time messy-date))) (error "Never."))) (provide 'emms-last-played) ;;; emms-last-played.el ends here �������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-lastfm-client.el��������������������������������������0000664�0000000�0000000�00000115237�12205151475�0023065�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-lastfm-client.el --- Last.FM Music API ;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc. ;; Author: Yoni Rabkin <yonirabkin@member.fsf.org> ;; Keywords: emms, lastfm ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; see the file COPYING. If not, write to the Free ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ;; MA 02110-1301, USA. ;;; Commentary: ;; ;; Definitive information on how to setup and use this package is ;; provided in the wonderful Emms manual, in the /doc directory of the ;; Emms distribution. ;;; Code: (require 'md5) (require 'parse-time) (require 'emms) (require 'emms-source-file) (require 'xml) (defcustom emms-lastfm-client-username nil "Valid Last.fm account username." :group 'emms-lastfm :type 'string) (defcustom emms-lastfm-client-api-key nil "Key for the Last.fm API." :group 'emms-lastfm :type 'string) (defcustom emms-lastfm-client-api-secret-key nil "Secret key for the Last.fm API." :group 'emms-lastfm :type 'string) (defvar emms-lastfm-client-api-session-key nil "Session key for the Last.fm API.") (defvar emms-lastfm-client-track nil "Latest Last.fm track.") (defvar emms-lastfm-client-submission-api t "Use the Last.fm submission API if true, otherwise don't.") (defvar emms-lastfm-client-token nil "Authorization token for API.") (defvar emms-lastfm-client-api-base-url "http://ws.audioscrobbler.com/2.0/" "URL for API calls.") (defvar emms-lastfm-client-session-key-file (concat (file-name-as-directory emms-directory) "emms-lastfm-client-sessionkey") "File for storing the Last.fm API session key.") (defvar emms-lastfm-client-cache-directory (file-name-as-directory (concat (file-name-as-directory emms-directory) "emms-lastfm-client-cache")) "File for storing Last.fm cache data.") (defvar emms-lastfm-client-playlist-valid nil "True if the playlist hasn't expired.") (defvar emms-lastfm-client-playlist-timer nil "Playlist timer object.") (defvar emms-lastfm-client-playlist nil "Latest Last.fm playlist.") (defvar emms-lastfm-client-track nil "Latest Last.fm track.") (defvar emms-lastfm-client-original-next-function nil "Original `-next-function'.") (defvar emms-lastfm-client-playlist-buffer-name "*Emms Last.fm*" "Name for non-interactive Emms Last.fm buffer.") (defvar emms-lastfm-client-playlist-buffer nil "Non-interactive Emms Last.fm buffer.") (defvar emms-lastfm-client-inhibit-cleanup nil "If true, do not perform clean-up after `emms-stop'.") (defvar emms-lastfm-client-image-size "mega" "Default size for artist information images.") (defvar emms-lastfm-client-artist-info-buffer-name "*Emms Last.fm Artist Info*" "Name for displaying artist information.") (defvar emms-lastfm-client-api-method-dict '((auth-get-token . ("auth.gettoken" emms-lastfm-client-auth-get-token-ok emms-lastfm-client-auth-get-token-failed)) (auth-get-session . ("auth.getsession" emms-lastfm-client-auth-get-session-ok emms-lastfm-client-auth-get-session-failed)) (radio-tune . ("radio.tune" emms-lastfm-client-radio-tune-ok emms-lastfm-client-radio-tune-failed)) (radio-getplaylist . ("radio.getplaylist" emms-lastfm-client-radio-getplaylist-ok emms-lastfm-client-radio-getplaylist-failed)) (track-love . ("track.love" emms-lastfm-client-track-love-ok emms-lastfm-client-track-love-failed)) (track-ban . ("track.ban" emms-lastfm-client-track-ban-ok emms-lastfm-client-track-ban-failed)) (artist-getinfo . ("artist.getinfo" emms-lastfm-client-artist-getinfo-ok emms-lastfm-client-artist-getinfo-failed))) "Mapping symbols to method calls. This is a list of cons pairs where the CAR is the symbol name of the method and the CDR is a list whose CAR is the method call string, CADR is the function to call on a success and CADDR is the function to call on failure.") ;;; ------------------------------------------------------------------ ;;; API method call ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-get-method (method) "Return the associated method cons for the symbol METHOD." (let ((m (cdr (assoc method emms-lastfm-client-api-method-dict)))) (if (not m) (error "method not in dictionary: %s" method) m))) (defun emms-lastfm-client-get-method-name (method) "Return the associated method string for the symbol METHOD." (let ((this (nth 0 (emms-lastfm-client-get-method method)))) (if (not this) (error "no name string registered for method: %s" method) this))) (defun emms-lastfm-client-get-method-ok (method) "Return the associated OK function for METHOD. This function is called when the method call returns successfully." (let ((this (nth 1 (emms-lastfm-client-get-method method)))) (if (not this) (error "no OK function registered for method: %s" method) this))) (defun emms-lastfm-client-get-method-fail (method) "Return the associated fail function for METHOD. This function is called when the method call returns a failure status message." (let ((this (nth 2 (emms-lastfm-client-get-method method)))) (if (not this) (error "no fail function registered for method: %s" method) this))) (defun emms-lastfm-client-encode-arguments (arguments) "Encode ARGUMENTS in UTF-8 for the Last.fm API." (let ((result nil)) (while arguments (setq result (append result (list (cons (encode-coding-string (caar arguments) 'utf-8) (encode-coding-string (cdar arguments) 'utf-8))))) (setq arguments (cdr arguments))) result)) (defun emms-lastfm-client-construct-arguments (str arguments) "Return a concatenation of arguments for the URL." (cond ((not arguments) str) (t (emms-lastfm-client-construct-arguments (concat str "&" (caar arguments) "=" (url-hexify-string (cdar arguments))) (cdr arguments))))) (defun emms-lastfm-client-construct-method-call (method arguments) "Return a complete URL method call for METHOD with ARGUMENTS. This function includes the cryptographic signature." (concat emms-lastfm-client-api-base-url "?" "method=" (emms-lastfm-client-get-method-name method) (emms-lastfm-client-construct-arguments "" arguments) "&api_sig=" (emms-lastfm-client-construct-signature method arguments))) (defun emms-lastfm-client-construct-write-method-call (method arguments) "Return a complete POST body method call for METHOD with ARGUMENTS. This function includes the cryptographic signature." (concat "method=" (emms-lastfm-client-get-method-name method) (emms-lastfm-client-construct-arguments "" arguments) "&api_sig=" (emms-lastfm-client-construct-signature method arguments))) ;;; ------------------------------------------------------------------ ;;; Response handler ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-handle-response (method xml-response) "Dispatch the handler functions of METHOD for XML-RESPONSE." (let ((status (cdr (assoc 'status (nth 1 (car xml-response))))) (data (cdr (cdr (car xml-response))))) (when (not status) (error "error parsing status from: %s" xml-response)) (cond ((string= status "failed") (funcall (emms-lastfm-client-get-method-fail method) data)) ((string= status "ok") (funcall (emms-lastfm-client-get-method-ok method) data)) (t (error "unknown response status %s" status))))) ;;; ------------------------------------------------------------------ ;;; Unathorized request token for an API account ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-construct-urt () "Return a request for an Unauthorized Request Token." (let ((arguments (emms-lastfm-client-encode-arguments `(("api_key" . ,emms-lastfm-client-api-key))))) (emms-lastfm-client-construct-method-call 'auth-get-token arguments))) (defun emms-lastfm-client-make-call-urt () "Make method call for Unauthorized Request Token." (let* ((url-request-method "POST")) (let ((response (url-retrieve-synchronously (emms-lastfm-client-construct-urt)))) (emms-lastfm-client-handle-response 'auth-get-token (with-current-buffer response (xml-parse-region (point-min) (point-max))))))) ;; example response: ((lfm ((status . \"ok\")) \"\" (token nil ;; \"31cab3398a9b46cf7231ef84d73169cf\"))) ;;; ------------------------------------------------------------------ ;;; Signatures ;;; ------------------------------------------------------------------ ;; ;; From [http://www.last.fm/api/desktopauth]: ;; ;; Construct your api method signatures by first ordering all the ;; parameters sent in your call alphabetically by parameter name and ;; concatenating them into one string using a <name><value> ;; scheme. So for a call to auth.getSession you may have: ;; ;; api_keyxxxxxxxxmethodauth.getSessiontokenxxxxxxx ;; ;; Ensure your parameters are utf8 encoded. Now append your secret ;; to this string. Finally, generate an md5 hash of the resulting ;; string. For example, for an account with a secret equal to ;; 'mysecret', your api signature will be: ;; ;; api signature = md5("api_keyxxxxxxxxmethodauth.getSessiontokenxxxxxxxmysecret") ;; ;; Where md5() is an md5 hashing operation and its argument is the ;; string to be hashed. The hashing operation should return a ;; 32-character hexadecimal md5 hash. (defun emms-lastfm-client-construct-lexi (arguments) "Return ARGUMENTS sorted in lexicographic order." (let ((lexi (sort arguments '(lambda (a b) (string< (car a) (car b))))) (out "")) (while lexi (setq out (concat out (caar lexi) (cdar lexi))) (setq lexi (cdr lexi))) out)) (defun emms-lastfm-client-construct-signature (method arguments) "Return request signature for METHOD and ARGUMENTS." (let ((complete-arguments (append arguments `(("method" . ,(emms-lastfm-client-get-method-name method)))))) (md5 (concat (emms-lastfm-client-construct-lexi complete-arguments) emms-lastfm-client-api-secret-key)))) ;;; ------------------------------------------------------------------ ;;; General error handling ;;; ------------------------------------------------------------------ ;; Each method call provides its own error codes, but if we don't want ;; to code a handler for a method we call this instead: (defun emms-lastfm-client-default-error-handler (data) "Default method failure handler." (let ((errorcode (cdr (assoc 'code (nth 1 (cadr data))))) (message (nth 2 (cadr data)))) (when (not (and errorcode message)) (error "failed to read errorcode or message: %s %s" errorcode message)) (error "method call failed with code %s: %s" errorcode message))) ;;; ------------------------------------------------------------------ ;;; Request authorization from the user ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-ask-for-auth () "Open a Web browser for authorizing the application." (when (not (and emms-lastfm-client-api-key emms-lastfm-client-token)) (error "API key and authorization token needed.")) (browse-url (format "http://www.last.fm/api/auth/?api_key=%s&token=%s" emms-lastfm-client-api-key emms-lastfm-client-token))) ;;; ------------------------------------------------------------------ ;;; Parse XSPF ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-xspf-header (data) "Return an alist representing the XSPF header of DATA." (let (out (orig data)) (setq data (cadr data)) (while data (when (and (car data) (listp (car data)) (= (length (car data)) 3)) (setq out (append out (list (cons (nth 0 (car data)) (nth 2 (car data))))))) (setq data (cdr data))) (if (not out) (error "failed to parse XSPF header from: %s" orig) out))) (defun emms-lastfm-client-xspf-tracklist (data) "Return the start of the track-list in DATE." (nthcdr 3 (nth 11 (cadr data)))) (defun emms-lastfm-client-xspf-header-date (header-alist) "Return the date parameter from HEADER-ALIST." (let ((out (cdr (assoc 'date header-alist)))) (if (not out) (error "could not read date from header alist: %s" header-alist) out))) (defun emms-lastfm-client-xspf-header-expiry (header-alist) "Return the expiry parameter from HEADER-ALIST." (let ((out (cdr (assoc 'link header-alist)))) (if (not out) (error "could not read expiry from header alist: %s" header-alist) out))) (defun emms-lastfm-client-xspf-header-creator (header-alist) "Return the creator parameter from HEADER-ALIST." (let ((out (cdr (assoc 'creator header-alist)))) (if (not out) (error "could not read creator from header alist: %s" header-alist) out))) (defun emms-lastfm-client-xspf-playlist (data) "Return the playlist from the XSPF DATA." (let ((playlist (car (nthcdr 11 data)))) (if (not playlist) (error "could not read playlist from: %s" data) playlist))) ;; note: the result of this function can be used with ;; `emms-lastfm-client-xspf-get' as well (defun emms-lastfm-client-xspf-extension (track) "Return the Extension portion of TRACK." (let ((this (copy-sequence track)) (cont t)) (while (and cont this) (when (consp this) (let ((head (car this))) (when (consp head) (when (equal 'extension (car head)) (setq cont nil))))) (when cont (setq this (cdr this)))) (if this (car this) (error "could not find track extension data")))) (defun emms-lastfm-client-xspf-get (node track) "Return data associated with NODE in TRACK." (let ((result nil)) (while track (when (consp track) (let ((this (car track))) (when (and (consp this) (= (length this) 3) (symbolp (nth 0 this)) (stringp (nth 2 this)) (equal (nth 0 this) node)) (setq result (nth 2 this))))) (setq track (cdr track))) (if (not result) nil result))) ;;; ------------------------------------------------------------------ ;;; Timers ;;; ------------------------------------------------------------------ ;; timed playlist invalidation is a part of the Last.fm API (defun emms-lastfm-client-set-timer (header) "Start timer countdown to playlist invalidation" (when (not header) (error "can't set timer with no header data")) (let ((expiry (parse-integer (emms-lastfm-client-xspf-header-expiry header)))) (setq emms-lastfm-client-playlist-valid t) (when emms-lastfm-client-playlist-timer (cancel-timer emms-lastfm-client-playlist-timer)) (setq emms-lastfm-client-playlist-timer (run-at-time expiry nil '(lambda () (cancel-timer emms-lastfm-client-playlist-timer) (setq emms-lastfm-client-playlist-valid nil)))))) ;;; ------------------------------------------------------------------ ;;; Player ;;; ------------------------------------------------------------------ ;; this should return `nil' to the track-manager when the playlist has ;; been exhausted (defun emms-lastfm-client-consume-next-track () "Pop and return the next track from the playlist or nil." (when emms-lastfm-client-playlist (if emms-lastfm-client-playlist-valid (let ((track (car emms-lastfm-client-playlist))) ;; we can only request each track once so we pop it off the ;; playlist (setq emms-lastfm-client-playlist (if (stringp (cdr emms-lastfm-client-playlist)) (cddr emms-lastfm-client-playlist) (cdr emms-lastfm-client-playlist))) track) (error "playlist invalid")))) (defun emms-lastfm-client-set-lastfm-playlist-buffer () "Set `emms-playlist-buffer' to a be an Emms lastfm buffer." (when (buffer-live-p emms-lastfm-client-playlist-buffer) (kill-buffer emms-lastfm-client-playlist-buffer)) (setq emms-lastfm-client-playlist-buffer (emms-playlist-new emms-lastfm-client-playlist-buffer-name)) (setq emms-playlist-buffer emms-lastfm-client-playlist-buffer)) (defun emms-lastfm-client-load-next-track () "Queue the next track from Last.fm." (with-current-buffer emms-lastfm-client-playlist-buffer (let ((inhibit-read-only t)) (widen) (delete-region (point-min) (point-max))) (if emms-lastfm-client-playlist (let ((track (emms-lastfm-client-consume-next-track))) (setq emms-lastfm-client-track track) (setq emms-lastfm-scrobbler-track-play-start-timestamp (emms-lastfm-scrobbler-timestamp)) (let ((emms-lastfm-client-inhibit-cleanup t)) (emms-play-url (emms-lastfm-client-xspf-get 'location track)))) (emms-lastfm-client-make-call-radio-getplaylist) (emms-lastfm-client-load-next-track)))) (defun emms-lastfm-client-love-track () "Submit the currently playing track with a `love' rating." (interactive) (when emms-lastfm-client-track (emms-lastfm-scrobbler-make-async-submission-call (emms-lastfm-client-convert-track emms-lastfm-client-track) 'love) ;; the following submission API call looks redundant but ;; isn't; indeed, it might be done away with in a future ;; version of the Last.fm API (see API docs) (emms-lastfm-client-make-call-track-love))) (defun emms-lastfm-client-ban-track () "Submit currently playing track with a `ban' rating and skip." (interactive) (when emms-lastfm-client-track (emms-lastfm-scrobbler-make-async-submission-call (emms-lastfm-client-convert-track emms-lastfm-client-track) 'ban) ;; the following submission API call looks redundant but ;; isn't; see `...-love-track' (emms-lastfm-client-make-call-track-ban) (emms-lastfm-client-load-next-track))) ;; call this `-track-advance' to avoid confusion with Emms' ;; `-next-track-' mechanism (defun emms-lastfm-client-track-advance (&optional first) "Move to the next track in the playlist." (interactive) (when (equal emms-playlist-buffer emms-lastfm-client-playlist-buffer) (when (and emms-lastfm-client-submission-api (not first)) (let ((result (emms-lastfm-scrobbler-make-async-submission-call (emms-lastfm-client-convert-track emms-lastfm-client-track) nil))))) (emms-lastfm-client-load-next-track))) (defun emms-lastfm-client-next-function () "Replacement function for `emms-next-noerror'." (if (equal emms-playlist-buffer emms-lastfm-client-playlist-buffer) (emms-lastfm-client-track-advance) (funcall emms-lastfm-client-original-next-function))) (defun emms-lastfm-client-clean-after-stop () "Kill the emms-lastfm buffer." (when (and (equal emms-playlist-buffer emms-lastfm-client-playlist-buffer) (not emms-lastfm-client-inhibit-cleanup)) (kill-buffer emms-lastfm-client-playlist-buffer) (setq emms-lastfm-client-playlist-buffer nil))) (defun emms-lastfm-client-play-playlist () "Entry point to play tracks from Last.fm." (emms-lastfm-client-set-lastfm-playlist-buffer) (when (not (equal emms-player-next-function 'emms-lastfm-client-next-function)) (add-to-list 'emms-player-stopped-hook 'emms-lastfm-client-clean-after-stop) (setq emms-lastfm-client-original-next-function emms-player-next-function) (setq emms-player-next-function 'emms-lastfm-client-next-function)) (emms-lastfm-client-track-advance t)) ;; stolen from Tassilo Horn's original emms-lastfm.el (defun emms-lastfm-client-read-artist () "Read an artist name from the user." (let ((artists nil)) (when (boundp 'emms-cache-db) (maphash #'(lambda (file track) (let ((artist (emms-track-get track 'info-artist))) (when artist (add-to-list 'artists artist)))) emms-cache-db)) (if artists (emms-completing-read "Artist: " artists) (read-string "Artist: ")))) (defun emms-lastfm-client-initialize-session () "Run per-session functions." (emms-lastfm-client-check-session-key)) (defun emms-lastfm-client-info () "Display information about the latest track." (interactive) (emms-lastfm-client-make-call-artist-getinfo)) ;;; ------------------------------------------------------------------ ;;; Stations ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-play-user-station (username url) "Play URL for USERNAME." (when (not (and username url)) (error "username and url must be set")) (emms-lastfm-client-initialize-session) (emms-lastfm-client-make-call-radio-tune (format url username)) (emms-lastfm-client-make-call-radio-getplaylist) (emms-lastfm-scrobbler-handshake) (emms-lastfm-client-play-playlist)) (defun emms-lastfm-client-play-similar-artists (artist) "Play a Last.fm station with music similar to ARTIST." (interactive (list (emms-lastfm-client-read-artist))) (when (not (stringp artist)) (error "not a string: %s" artist)) (emms-lastfm-client-initialize-session) (emms-lastfm-client-make-call-radio-tune (format "lastfm://artist/%s/similarartists" artist)) (emms-lastfm-client-make-call-radio-getplaylist) (emms-lastfm-scrobbler-handshake) (emms-lastfm-client-play-playlist)) (defun emms-lastfm-client-play-recommended () "Play a Last.fm station with \"recommended\" tracks." (interactive) (emms-lastfm-client-play-user-station emms-lastfm-client-username "lastfm://user/%s/recommended")) (defun emms-lastfm-client-play-loved () "Play a Last.fm station with \"loved\" tracks." (interactive) (emms-lastfm-client-play-user-station emms-lastfm-client-username "lastfm://user/%s/loved")) (defun emms-lastfm-client-play-mix () "Play the \"Mix\" station the current user." (interactive) (emms-lastfm-client-play-user-station emms-lastfm-client-username "lastfm://user/%s/mix")) (defun emms-lastfm-client-play-neighborhood () "Play a Last.fm station with \"neighborhood\" tracks." (interactive) (emms-lastfm-client-play-user-station emms-lastfm-client-username "lastfm://user/%s/neighbours")) (defun emms-lastfm-client-play-library () "Play a Last.fm station with \"library\" tracks." (interactive) (emms-lastfm-client-play-user-station emms-lastfm-client-username "lastfm://user/%s/personal")) (defun emms-lastfm-client-play-user-loved (user) (interactive "sLast.fm username: ") (emms-lastfm-client-play-user-station user "lastfm://user/%s/loved")) (defun emms-lastfm-client-play-user-neighborhood (user) (interactive "sLast.fm username: ") (emms-lastfm-client-play-user-station user "lastfm://user/%s/neighbours")) (defun emms-lastfm-client-play-user-library (user) (interactive "sLast.fm username: ") (emms-lastfm-client-play-user-station user "lastfm://user/%s/personal")) ;;; ------------------------------------------------------------------ ;;; Information ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-convert-track (track) "Convert a Last.fm track to an Emms track." (let ((emms-track (emms-dictionary '*track*))) (emms-track-set emms-track 'name (emms-lastfm-client-xspf-get 'location track)) (emms-track-set emms-track 'info-artist (emms-lastfm-client-xspf-get 'creator track)) (emms-track-set emms-track 'info-title (emms-lastfm-client-xspf-get 'title track)) (emms-track-set emms-track 'info-album (emms-lastfm-client-xspf-get 'album track)) (emms-track-set emms-track 'info-playing-time (/ (parse-integer (emms-lastfm-client-xspf-get 'duration track)) 1000)) (emms-track-set emms-track 'type 'lastfm-streaming) emms-track)) (defun emms-lastfm-client-show-track (track) "Return description of TRACK." (decode-coding-string (format emms-show-format (emms-track-description (emms-lastfm-client-convert-track track))) 'utf-8)) (defun emms-lastfm-client-show () "Display a description of the current track." (interactive) (if emms-player-playing-p (message (emms-lastfm-client-show-track emms-lastfm-client-track)) nil)) ;;; ------------------------------------------------------------------ ;;; Desktop application authorization [http://www.last.fm/api/desktopauth] ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-user-authorization () "Ask user to authorize the application." (interactive) (emms-lastfm-client-make-call-urt) (emms-lastfm-client-ask-for-auth)) (defun emms-lastfm-client-get-session () "Retrieve and store session key." (interactive) (emms-lastfm-client-make-call-get-session) (emms-lastfm-client-save-session-key emms-lastfm-client-api-session-key)) ;;; ------------------------------------------------------------------ ;;; method: auth.getToken [http://www.last.fm/api/show?service=265] ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-auth-get-token-ok (data) "Function called when auth.getToken succeeds." (setq emms-lastfm-client-token (nth 2 (cadr data))) (if (or (not emms-lastfm-client-token) (not (= (length emms-lastfm-client-token) 32))) (error "could not read token from response %s" data) (message "Emms Last.FM auth.getToken method call success."))) (defun emms-lastfm-client-auth-get-token-failed (data) "Function called when auth.getToken fails." (emms-lastfm-client-default-error-handler data)) ;;; ------------------------------------------------------------------ ;;; method: auth.getSession [http://www.last.fm/api/show?service=125] ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-construct-get-session () "Return an auth.getSession request string." (let ((arguments (emms-lastfm-client-encode-arguments `(("token" . ,emms-lastfm-client-token) ("api_key" . ,emms-lastfm-client-api-key))))) (emms-lastfm-client-construct-method-call 'auth-get-session arguments))) (defun emms-lastfm-client-make-call-get-session () "Make auth.getSession call." (let* ((url-request-method "POST")) (let ((response (url-retrieve-synchronously (emms-lastfm-client-construct-get-session)))) (emms-lastfm-client-handle-response 'auth-get-session (with-current-buffer response (xml-parse-region (point-min) (point-max))))))) (defun emms-lastfm-client-save-session-key (key) "Store KEY." (let ((buffer (find-file-noselect emms-lastfm-client-session-key-file))) (set-buffer buffer) (erase-buffer) (insert key) (save-buffer) (kill-buffer buffer))) (defun emms-lastfm-client-load-session-key () "Return stored session key." (let ((file (expand-file-name emms-lastfm-client-session-key-file))) (setq emms-lastfm-client-api-session-key (if (file-readable-p file) (with-temp-buffer (emms-insert-file-contents file) (goto-char (point-min)) (buffer-substring-no-properties (point) (point-at-eol))) nil)))) (defun emms-lastfm-client-check-session-key () "Signal an error condition if there is no session key." (if emms-lastfm-client-api-session-key emms-lastfm-client-api-session-key (if (emms-lastfm-client-load-session-key) emms-lastfm-client-api-session-key (error "no session key for API access")))) (defun emms-lastfm-client-auth-get-session-ok (data) "Function called on DATA if auth.getSession succeeds." (let ((session-key (nth 2 (nth 5 (cadr data))))) (cond (session-key (setq emms-lastfm-client-api-session-key session-key) (message "Emms Last.fm session key retrieval successful")) (t (error "failed to parse session key data %s" data))))) (defun emms-lastfm-client-auth-get-session-failed (data) "Function called on DATA if auth.getSession fails." (emms-lastfm-client-default-error-handler data)) ;;; ------------------------------------------------------------------ ;;; method: radio.tune [http://www.last.fm/api/show?service=160] ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-construct-radio-tune (station) "Return a request to tune to STATION." (let ((arguments (emms-lastfm-client-encode-arguments `(("sk" . ,emms-lastfm-client-api-session-key) ("station" . ,station) ("api_key" . ,emms-lastfm-client-api-key))))) (emms-lastfm-client-construct-write-method-call 'radio-tune arguments))) (defun emms-lastfm-client-make-call-radio-tune (station) "Make call to tune to STATION." (let ((url-request-method "POST") (url-request-extra-headers `(("Content-type" . "application/x-www-form-urlencoded"))) (url-request-data (emms-lastfm-client-construct-radio-tune station))) (let ((response (url-retrieve-synchronously emms-lastfm-client-api-base-url))) (emms-lastfm-client-handle-response 'radio-tune (with-current-buffer response (xml-parse-region (point-min) (point-max))))))) (defun emms-lastfm-client-radio-tune-failed (data) "Function called on DATA when tuning fails." (emms-lastfm-client-default-error-handler data)) (defun emms-lastfm-client-radio-tune-ok (data) "Set the current radio station according to DATA." (let ((response (cdr (cadr data))) data) (while response (when (and (listp (car response)) (car response) (= (length (car response)) 3)) (add-to-list 'data (cons (caar response) (car (cdr (cdr (car response))))))) (setq response (cdr response))) (when (not data) (error "could not parse station information %s" data)) data)) ;;; ------------------------------------------------------------------ ;;; method: radio.getPlaylist [http://www.last.fm/api/show?service=256] ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-construct-radio-getplaylist () "Return a request for a playlist from the tuned station." (let ((arguments (emms-lastfm-client-encode-arguments `(("sk" . ,emms-lastfm-client-api-session-key) ("api_key" . ,emms-lastfm-client-api-key))))) (emms-lastfm-client-construct-write-method-call 'radio-getplaylist arguments))) (defun emms-lastfm-client-make-call-radio-getplaylist () "Make call for playlist from the tuned station." (let ((url-request-method "POST") (url-request-extra-headers `(("Content-type" . "application/x-www-form-urlencoded"))) (url-request-data (emms-lastfm-client-construct-radio-getplaylist))) (let ((response (url-retrieve-synchronously emms-lastfm-client-api-base-url))) (emms-lastfm-client-handle-response 'radio-getplaylist (with-current-buffer response (xml-parse-region (point-min) (point-max))))))) (defun emms-lastfm-client-radio-getplaylist-failed (data) "Function called on DATA when retrieving a playlist fails." 'stub-needs-to-handle-playlist-issues (emms-lastfm-client-default-error-handler data)) (defun emms-lastfm-client-list-filter (l) "Remove strings from the roots of list L." (let (acc) (while l (when (listp (car l)) (push (car l) acc)) (setq l (cdr l))) (reverse acc))) (defun emms-lastfm-client-radio-getplaylist-ok (data) "Function called on DATA when retrieving a playlist succeeds." (let ((header (emms-lastfm-client-xspf-header data)) (tracklist (emms-lastfm-client-xspf-tracklist data))) (emms-lastfm-client-set-timer header) (setq emms-lastfm-client-playlist (emms-lastfm-client-list-filter tracklist)))) ;;; ------------------------------------------------------------------ ;;; method: track.love [http://www.last.fm/api/show?service=260] ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-construct-track-love () "Return a request for setting current track rating to `love'." (let ((arguments (emms-lastfm-client-encode-arguments `(("sk" . ,emms-lastfm-client-api-session-key) ("api_key" . ,emms-lastfm-client-api-key) ("track" . ,(emms-lastfm-client-xspf-get 'title emms-lastfm-client-track)) ("artist" . ,(emms-lastfm-client-xspf-get 'creator emms-lastfm-client-track)))))) (emms-lastfm-client-construct-write-method-call 'track-love arguments))) (defun emms-lastfm-client-make-call-track-love () "Make call for setting track rating to `love'." (let ((url-request-method "POST") (url-request-extra-headers `(("Content-type" . "application/x-www-form-urlencoded"))) (url-request-data (emms-lastfm-client-construct-track-love))) (let ((response (url-retrieve-synchronously emms-lastfm-client-api-base-url))) (emms-lastfm-client-handle-response 'track-love (with-current-buffer response (xml-parse-region (point-min) (point-max))))))) (defun emms-lastfm-client-track-love-failed (data) "Function called with DATA when setting `love' rating fails." 'stub-needs-to-handle-track-love-issues (emms-lastfm-client-default-error-handler data)) (defun emms-lastfm-client-track-love-ok (data) "Function called with DATA after `love' rating succeeds." 'track-love-succeed) ;;; ------------------------------------------------------------------ ;;; method: artist.getInfo [http://www.last.fm/api/show?service=267] ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-cache-file (url) "Download a file from URL and return a pathname." (make-directory emms-lastfm-client-cache-directory t) (let ((files (directory-files emms-lastfm-client-cache-directory t))) (dolist (file files) (when (file-regular-p file) (delete-file file))) (call-process "wget" nil nil nil url "-P" (expand-file-name emms-lastfm-client-cache-directory)) (car (directory-files emms-lastfm-client-cache-directory t ".\\(jpg\\|png\\)")))) (defun emms-lastfm-client-display-artist-getinfo (artist-name lastfm-url artist-image stats-listeners stats-playcount bio-summary bio-complete) "Display a buffer with the artist information." (let ((buf (get-buffer-create emms-lastfm-client-artist-info-buffer-name))) (with-current-buffer buf (let ((inhibit-read-only t)) (delete-region (point-min) (point-max)) (insert-image (create-image (emms-lastfm-client-cache-file artist-image))) (insert (format "\n\n%s\n\n" (decode-coding-string artist-name 'utf-8))) (insert (format "Last.fm page: %s\n\n" lastfm-url)) (insert (format "Listeners: %s\n" stats-listeners)) (insert (format "Plays: %s\n\n" stats-playcount)) (let ((p (point))) (insert (format "<p>%s</p>" bio-complete)))) (setq buffer-read-only t) (text-mode) (goto-char (point-min))) (switch-to-buffer buf))) (defun emms-lastfm-client-parse-artist-getinfo (data) "Parse the artist information." (when (or (not data) (not (listp data))) (error "no artist info to parse")) (let ((c (copy-seq (nth 1 data))) artist-name lastfm-url artist-image stats-listeners stats-playcount bio-summary bio-complete) (while c (let ((entry (car c))) (when (listp entry) (let ((name (nth 0 entry)) (value (nth 2 entry))) (cond ((equal name 'name) (setq artist-name value)) ((equal name 'url) (setq lastfm-url value)) ((equal name 'image) (let ((size (cdar (nth 1 entry)))) (when (string-equal emms-lastfm-client-image-size size) (setq artist-image value)))) ((equal name 'stats) (setq stats-listeners (nth 2 (nth 3 entry)) stats-playcount (nth 2 (nth 5 entry)))) ((equal name 'bio) (setq bio-summary (nth 2 (nth 5 entry)) bio-complete (nth 2 (nth 7 entry)))))))) (setq c (cdr c))) (emms-lastfm-client-display-artist-getinfo artist-name lastfm-url artist-image stats-listeners stats-playcount bio-summary bio-complete))) (defun emms-lastfm-client-construct-artist-getinfo () "Return a request for getting info about an artist." (let ((arguments (emms-lastfm-client-encode-arguments `(("sk" . ,emms-lastfm-client-api-session-key) ("api_key" . ,emms-lastfm-client-api-key) ("autocorrect" . "1") ("artist" . ,(emms-lastfm-client-xspf-get 'creator emms-lastfm-client-track)))))) (emms-lastfm-client-construct-write-method-call 'artist-getinfo arguments))) (defun emms-lastfm-client-make-call-artist-getinfo () "Make a call for artist info." (let ((url-request-method "POST") (url-request-extra-headers `(("Content-type" . "application/x-www-form-urlencoded"))) (url-request-data (emms-lastfm-client-construct-artist-getinfo))) (let ((response (url-retrieve-synchronously emms-lastfm-client-api-base-url))) (emms-lastfm-client-handle-response 'artist-getinfo (with-current-buffer response (xml-parse-region (point-min) (point-max))))))) (defun emms-lastfm-client-artist-getinfo-failed (data) "Function called with DATA when setting `ban' rating fails." 'stub-needs-to-handle-artist-getinfo-issues (emms-lastfm-client-default-error-handler data)) (defun emms-lastfm-client-artist-getinfo-ok (data) "Function called with DATA after `ban' rating succeeds." (emms-lastfm-client-parse-artist-getinfo data)) ;;; ------------------------------------------------------------------ ;;; method: track.ban [http://www.last.fm/api/show?service=261] ;;; ------------------------------------------------------------------ (defun emms-lastfm-client-construct-track-ban () "Return a request for setting current track rating to `ban'." (let ((arguments (emms-lastfm-client-encode-arguments `(("sk" . ,emms-lastfm-client-api-session-key) ("api_key" . ,emms-lastfm-client-api-key) ("track" . ,(emms-lastfm-client-xspf-get 'title emms-lastfm-client-track)) ("artist" . ,(emms-lastfm-client-xspf-get 'creator emms-lastfm-client-track)))))) (emms-lastfm-client-construct-write-method-call 'track-ban arguments))) (defun emms-lastfm-client-make-call-track-ban () "Make call for setting track rating to `ban'." (let ((url-request-method "POST") (url-request-extra-headers `(("Content-type" . "application/x-www-form-urlencoded"))) (url-request-data (emms-lastfm-client-construct-track-ban))) (let ((response (url-retrieve-synchronously emms-lastfm-client-api-base-url))) (emms-lastfm-client-handle-response 'track-ban (with-current-buffer response (xml-parse-region (point-min) (point-max))))))) (defun emms-lastfm-client-track-ban-failed (data) "Function called with DATA when setting `ban' rating fails." 'stub-needs-to-handle-track-ban-issues (emms-lastfm-client-default-error-handler data)) (defun emms-lastfm-client-track-ban-ok (data) "Function called with DATA after `ban' rating succeeds." 'track-ban-succeed) (provide 'emms-lastfm-client) ;;; emms-lastfm-client.el ends here �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-lastfm-scrobbler.el�����������������������������������0000664�0000000�0000000�00000034405�12205151475�0023561�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-lastfm-scrobbler.el --- Last.FM Music API ;; Copyright (C) 2009, 2010 Free Software Foundation, Inc. ;; Authors: Bram van der Kroef <bram@fortfrances.com>, Yoni Rabkin ;; <yonirabkin@member.fsf.org> ;; Keywords: emms, lastfm ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; see the file COPYING. If not, write to the Free ;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ;; MA 02110-1301, USA. ;;; Code: ;;; ------------------------------------------------------------------ ;;; Submission API [http://www.last.fm/api/submissions] ;;; ------------------------------------------------------------------ (require 'emms) (require 'emms-playing-time) (require 'emms-lastfm-client) ;; Variables referenced from emms-lastfm-client: ;; emms-lastfm-client-username, emms-lastfm-client-api-key, ;; emms-lastfm-client-api-secret-key, emms-lastfm-client-api-session-key, ;; emms-lastfm-client-track ;; Functions referenced: ;; emms-lastfm-client-xspf-get, emms-lastfm-client-xspf-extension, ;; emms-lastfm-client-initialize-session (defcustom emms-lastfm-scrobbler-submit-track-types '(file) "Specify what types of tracks to submit to Last.fm. The default is to only submit files. To submit every track to Last.fm, set this to t." :type '(choice (const :tag "All" t) (set :tag "Types" (const :tag "Files" file) (const :tag "URLs" url) (const :tag "Playlists" playlist) (const :tag "Streamlists" streamlist) (const :tag "Last.fm streams" lastfm-streaming))) :group 'emms-lastfm) (defvar emms-lastfm-scrobbler-submission-protocol-number "1.2.1" "Version of the submissions protocol to which Emms conforms.") (defvar emms-lastfm-scrobbler-published-version "1.0" "Version of this package published to the Last.fm service.") (defvar emms-lastfm-scrobbler-submission-session-id nil "Scrobble session id, for now-playing and submission requests.") (defvar emms-lastfm-scrobbler-submission-now-playing-url nil "URL that should be used for a now-playing request.") (defvar emms-lastfm-scrobbler-submission-url nil "URL that should be used for submissions") (defvar emms-lastfm-scrobbler-client-identifier "emm" "Client identifier for Emms (Last.fm define this, not us).") (defvar emms-lastfm-scrobbler-track-play-start-timestamp nil "UTC timestamp.") ;; 1.3 Authentication Token for Web Services Authentication: token = ;; md5(shared_secret + timestamp) (defun emms-lastfm-scrobbler-make-token-for-web-services (timestamp) (when (not (and emms-lastfm-client-api-secret-key timestamp)) (error "secret and timestamp needed to make an auth token")) (md5 (concat emms-lastfm-client-api-secret-key timestamp))) ;; Handshake: The initial negotiation with the submissions server to ;; establish authentication and connection details for the session. (defun emms-lastfm-scrobbler-handshake () "Make handshake call." (let* ((url-request-method "GET")) (let ((response (url-retrieve-synchronously (emms-lastfm-scrobbler-make-handshake-call)))) (emms-lastfm-scrobbler-handle-handshake (with-current-buffer response (buffer-substring-no-properties (point-min) (point-max))))))) (defun emms-lastfm-scrobbler-make-handshake-call () "Return a submission protocol handshake string." (when (not (and emms-lastfm-scrobbler-submission-protocol-number emms-lastfm-scrobbler-client-identifier emms-lastfm-scrobbler-published-version emms-lastfm-client-username)) (error "missing variables to generate handshake call")) (let ((timestamp (emms-lastfm-scrobbler-timestamp))) (concat "http://post.audioscrobbler.com/?hs=true" "&p=" emms-lastfm-scrobbler-submission-protocol-number "&c=" emms-lastfm-scrobbler-client-identifier "&v=" emms-lastfm-scrobbler-published-version "&u=" emms-lastfm-client-username "&t=" timestamp "&a=" (emms-lastfm-scrobbler-make-token-for-web-services timestamp) "&api_key=" emms-lastfm-client-api-key "&sk=" emms-lastfm-client-api-session-key))) (defun emms-lastfm-scrobbler-handle-handshake (response) (let ((ok200 "HTTP/1.1 200 OK")) (when (not (string= ok200 (substring response 0 15))) (error "server not responding correctly")) (with-temp-buffer (insert response) (goto-char (point-min)) (re-search-forward "\n\n") (let ((status (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (cond ((string= status "OK") (forward-line) (setq emms-lastfm-scrobbler-submission-session-id (buffer-substring-no-properties (point-at-bol) (point-at-eol))) (forward-line) (setq emms-lastfm-scrobbler-submission-now-playing-url (buffer-substring-no-properties (point-at-bol) (point-at-eol))) (forward-line) (setq emms-lastfm-scrobbler-submission-url (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) ((string= status "BANNED") (error "this version of Emms has been BANNED")) ((string= status "BADAUTH") (error "bad authentication paramaters to handshake")) ((string= status "BADTIME") (error "handshake timestamp diverges too much")) (t (error "unhandled handshake failure"))))))) (defun emms-lastfm-scrobbler-assert-submission-handshake () (when (not (and emms-lastfm-scrobbler-submission-session-id emms-lastfm-scrobbler-submission-now-playing-url emms-lastfm-scrobbler-submission-url)) (error "cannot use submission API before handshake"))) (defun emms-lastfm-scrobbler-hexify-encode (str) "UTF-8 encode and URL-hexify STR." (url-hexify-string (encode-coding-string str 'utf-8))) (defun emms-lastfm-scrobbler-timestamp () "Return a UNIX UTC timestamp." (format-time-string "%s")) (defun emms-lastfm-scrobbler-get-response-status () "Check the http header and return the body" (let ((ok200 "HTTP/1.1 200 OK")) (if (< (point-max) 1) (error "No response from submission server")) (if (not (string= ok200 (buffer-substring-no-properties (point-min) 16))) (error "submission server not responding correctly")) (goto-char (point-min)) (re-search-forward "\n\n") (buffer-substring-no-properties (point-at-bol) (point-at-eol)))) (defun emms-lastfm-scrobbler-submission-data (track rating) "Format the url parameters containing the track artist, title, rating, time the track was played, etc." ;; (emms-lastfm-scrobbler-assert-submission-handshake) (setq rating (cond ((equal 'love rating) "L") ((equal 'ban rating) "B") ((equal 'skip rating) "S") (t ""))) (let ((artist (emms-track-get track 'info-artist)) (title (emms-track-get track 'info-title)) (album (or (emms-track-get track 'info-album) "")) (track-number (emms-track-get track 'info-tracknumber)) (musicbrainz-id "") (track-length (number-to-string (or (emms-track-get track 'info-playing-time) 0)))) (if (and artist title) (concat "s=" (emms-lastfm-scrobbler-hexify-encode emms-lastfm-scrobbler-submission-session-id) "&a[0]=" (emms-lastfm-scrobbler-hexify-encode artist) "&t[0]=" (emms-lastfm-scrobbler-hexify-encode title) "&i[0]=" (emms-lastfm-scrobbler-hexify-encode emms-lastfm-scrobbler-track-play-start-timestamp) "&o[0]=" (if (equal (emms-track-type track) 'lastfm-streaming) (concat "L" (emms-lastfm-scrobbler-hexify-encode (emms-lastfm-client-xspf-get 'trackauth (emms-lastfm-client-xspf-extension emms-lastfm-client-track)))) "P") "&r[0]=" (emms-lastfm-scrobbler-hexify-encode rating) "&l[0]=" track-length "&b[0]=" (emms-lastfm-scrobbler-hexify-encode album) "&n[0]=" track-number "&m[0]=" musicbrainz-id) (error "Track title and artist must be known.")))) (defun emms-lastfm-scrobbler-nowplaying-data (track) "Format the parameters for the Now playing submission." ;; (emms-lastfm-scrobbler-assert-submission-handshake) (let ((artist (emms-track-get track 'info-artist)) (title (emms-track-get track 'info-title)) (album (or (emms-track-get track 'info-album) "")) (track-number (emms-track-get track 'info-tracknumber)) (musicbrainz-id "") (track-length (number-to-string (or (emms-track-get track 'info-playing-time) 0)))) (if (and artist title) (concat "s=" (emms-lastfm-scrobbler-hexify-encode emms-lastfm-scrobbler-submission-session-id) "&a=" (emms-lastfm-scrobbler-hexify-encode artist) "&t=" (emms-lastfm-scrobbler-hexify-encode title) "&b=" (emms-lastfm-scrobbler-hexify-encode album) "&l=" track-length "&n=" track-number "&m=" musicbrainz-id) (error "Track title and artist must be known.")))) (defun emms-lastfm-scrobbler-allowed-track-type (track) "Check if the track-type is one of the allowed types" (let ((track-type (emms-track-type track))) (or (eq emms-lastfm-scrobbler-submit-track-types t) (and (listp emms-lastfm-scrobbler-submit-track-types) (memq track-type emms-lastfm-scrobbler-submit-track-types))))) ;;; ------------------------------------------------------------------ ;;; EMMS hooks ;;; ------------------------------------------------------------------ (defun emms-lastfm-scrobbler-start-hook () "Update the now playing info displayed on the user's last.fm page. This doesn't affect the user's profile, so it con be done even for tracks that should not be submitted." ;; wait 5 seconds for the stop hook to submit the last track (sit-for 5) (let ((current-track (emms-playlist-current-selected-track))) (setq emms-lastfm-scrobbler-track-play-start-timestamp (emms-lastfm-scrobbler-timestamp)) (if (emms-lastfm-scrobbler-allowed-track-type current-track) (emms-lastfm-scrobbler-make-async-nowplaying-call current-track)))) (defun emms-lastfm-scrobbler-stop-hook () "Submit the track to last.fm if it has been played for 240 seconds or half the length of the track." (let ((current-track (emms-playlist-current-selected-track))) (let ((track-length (emms-track-get current-track 'info-playing-time))) (when (and track-length (emms-lastfm-scrobbler-allowed-track-type current-track)) (when (and ;; track must be longer than 30 secs (> track-length 30) ;; track must be played for more than 240 secs or ;; half the tracks length, whichever comes first. (> emms-playing-time (min 240 (/ track-length 2)))) (emms-lastfm-scrobbler-make-async-submission-call current-track nil)))))) (defun emms-lastfm-scrobbler-enable () "Enable the Last.fm scrobbler and submit the tracks EMMS plays to last.fm" (interactive) (emms-lastfm-client-initialize-session) (if (not emms-lastfm-scrobbler-submission-session-id) (emms-lastfm-scrobbler-handshake)) (add-hook 'emms-player-started-hook 'emms-lastfm-scrobbler-start-hook t) (add-hook 'emms-player-stopped-hook 'emms-lastfm-scrobbler-stop-hook) (add-hook 'emms-player-finished-hook 'emms-lastfm-scrobbler-stop-hook)) (defun emms-lastfm-scrobbler-disable () "Stop submitting to last.fm" (interactive) (remove-hook 'emms-player-started-hook 'emms-lastfm-scrobbler-start-hook) (remove-hook 'emms-player-stopped-hook 'emms-lastfm-scrobbler-stop-hook) (remove-hook 'emms-player-finished-hook 'emms-lastfm-scrobbler-stop-hook)) ;;; ------------------------------------------------------------------ ;;; Asynchronous Submission ;;; ------------------------------------------------------------------ (defun emms-lastfm-scrobbler-make-async-submission-call (track rating) "Make asynchronous submission call." (let ((flarb (emms-lastfm-scrobbler-submission-data track rating))) (let* ((url-request-method "POST") (url-request-data flarb) (url-request-extra-headers `(("Content-type" . "application/x-www-form-urlencoded")))) (url-retrieve emms-lastfm-scrobbler-submission-url #'emms-lastfm-scrobbler-async-submission-callback (list (cons track rating)))))) (defun emms-lastfm-scrobbler-async-submission-callback (status &optional cbargs) "Pass response of asynchronous submission call to handler." (emms-lastfm-scrobbler-assert-submission-handshake) (let ((response (emms-lastfm-scrobbler-get-response-status))) ;; From the API docs: This indicates that the ;; submission request was accepted for processing. It ;; does not mean that the submission was valid, but ;; only that the authentication and the form of the ;; submission was validated. (let ((track (car cbargs))) (cond ((string= response "OK") (message "Last.fm: Submitted %s" (emms-track-get track 'info-title))) ((string= response "BADSESSION") (emms-lastfm-scrobbler-handshake) (emms-lastfm-scrobbler-make-async-submission-call (car cbargs) (cdr cbargs))) (t (error "unhandled submission failure")))))) (defun emms-lastfm-scrobbler-make-async-nowplaying-call (track) "Make asynchronous now-playing submission call." (emms-lastfm-scrobbler-assert-submission-handshake) (let* ((url-request-method "POST") (url-request-data (emms-lastfm-scrobbler-nowplaying-data track)) (url-request-extra-headers `(("Content-type" . "application/x-www-form-urlencoded")))) (url-retrieve emms-lastfm-scrobbler-submission-now-playing-url #'emms-lastfm-scrobbler-async-nowplaying-callback (list (cons track nil))))) (defun emms-lastfm-scrobbler-async-nowplaying-callback (status &optional cbargs) "Pass response of asynchronous now-playing submission call to handler." (let ((response (emms-lastfm-scrobbler-get-response-status))) (cond ((string= response "OK") nil) ((string= response "BADSESSION") (emms-lastfm-scrobbler-handshake) (emms-lastfm-scrobbler-make-async-nowplaying-call (car cbargs))) (t (error "unhandled submission failure"))))) (provide 'emms-lastfm-scrobbler) ;;; emms-lastfm-scrobbler.el ends here. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-lyrics.el���������������������������������������������0000664�0000000�0000000�00000046500�12205151475�0021624�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-lyrics.el --- Display lyrics synchronically ;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2013 Free Software Foundation, Inc. ;; Author: William Xu <william.xwl@gmail.com> ;; Keywords: emms music lyrics ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;; This package enables you to play music files and display lyrics ;; synchronically! :-) Plus, it provides a `emms-lyrics-mode' for ;; making lyric files. ;; Put this file into your load-path and the following into your ;; ~/.emacs: ;; (require 'emms-lyrics) ;; ;; Then either `M-x emms-lyrics-enable' or add (emms-lyrics 1) in ;; your .emacs to enable. ;;; TODO: ;; 1. Maybe the lyric setup should run before `emms-start'. ;; 2. Give a user a chance to choose when finding out multiple lyrics. ;; 3. Search .lrc format lyrics from internet ? ;;; Code: (require 'emms) (require 'emms-player-simple) (require 'emms-source-file) (require 'time-date) (require 'emms-url) (require 'emms-compat) ;;; User Customization (defgroup emms-lyrics nil "Lyrics module for EMMS." :group 'emms) (defcustom emms-lyrics-display-on-modeline t "If non-nil, display lyrics on mode line." :type 'boolean :group 'emms-lyrics) (defcustom emms-lyrics-display-on-minibuffer nil "If non-nil, display lyrics on minibuffer." :type 'boolean :group 'emms-lyrics) (defcustom emms-lyrics-display-buffer nil "Non-nil will create deciated `emms-lyrics-buffer' to display lyrics." :type 'boolean :group 'emms-lyrics) (defcustom emms-lyrics-dir "~/music/lyrics" "Local lyrics repository. `emms-lyrics-find-lyric' will look for lyrics in current directory(i.e., same as the music file) and this directory." :type 'string :group 'emms-lyrics) (defcustom emms-lyrics-display-format " %s " "Format for displaying lyrics." :type 'string :group 'emms-lyrics) (defcustom emms-lyrics-coding-system nil "Coding system for reading lyrics files. If all your lyrics use the same coding system, you can set this variable to that value; else you'd better leave it to nil, and rely on `prefer-coding-system', `file-coding-system-alist' or \(info \"(emacs)File Variables\"), sorted by priority increasingly." :type 'coding-system :group 'emms-lyrics) (defcustom emms-lyrics-mode-hook nil "Normal hook run after entering Emms Lyric mode." :type 'hook :group 'emms-lyrics) (defcustom emms-lyrics-find-lyric-function 'emms-lyrics-find-lyric "Function for finding lyric files." :type 'symbol :group 'emms-lyrics) (defcustom emms-lyrics-scroll-p t "Non-nil value will enable lyrics scrolling on mode line. Note: Even if this is set to t, it also depends on `emms-lyrics-display-on-modeline' to be t." :type 'boolean :group 'emms-lyrics) (defcustom emms-lyrics-scroll-timer-interval 0.4 "Interval between scroller timers. The shorter, the faster." :type 'number :group 'emms-lyrics) ;;; User Interfaces (defvar emms-lyrics-display-p t "If non-nil, will diplay lyrics.") (defvar emms-lyrics-mode-line-string "" "Current lyric.") (defvar emms-lyrics-buffer nil "Buffer to show lyrics.") ;;;###autoload (defun emms-lyrics-enable () "Enable displaying emms lyrics." (interactive) (emms-lyrics 1) (message "emms lyrics enabled.")) ;;;###autoload (defun emms-lyrics-disable () "Disable displaying emms lyrics." (interactive) (emms-lyrics -1) (message "EMMS lyrics disabled")) ;;;###autoload (defun emms-lyrics-toggle () "Toggle displaying emms lyrics." (interactive) (if emms-lyrics-display-p (emms-lyrics-disable) (emms-lyrics-enable))) (defun emms-lyrics-toggle-display-on-minibuffer () "Toggle display lyrics on minibbufer." (interactive) (if emms-lyrics-display-on-minibuffer (progn (setq emms-lyrics-display-on-minibuffer nil) (message "Disable lyrics on minibufer")) (setq emms-lyrics-display-on-minibuffer t) (message "Enable lyrics on minibufer"))) (defun emms-lyrics-toggle-display-on-modeline () "Toggle display lyrics on mode line." (interactive) (if emms-lyrics-display-on-modeline (progn (setq emms-lyrics-display-on-modeline nil emms-lyrics-mode-line-string "") (message "Disable lyrics on mode line")) (setq emms-lyrics-display-on-modeline t) (message "Enable lyrics on mode line"))) (defun emms-lyrics-toggle-display-buffer () "Toggle showing/hiding `emms-lyrics-buffer'." (interactive) (let ((w (get-buffer-window emms-lyrics-buffer))) (if w (delete-window w) (save-selected-window (pop-to-buffer emms-lyrics-buffer) (set-window-dedicated-p w t))))) (defun emms-lyrics (arg) "Turn on emms lyrics display if ARG is positive, off otherwise." (interactive "p") (if (and arg (> arg 0)) (progn (setq emms-lyrics-display-p t) (add-hook 'emms-player-started-hook 'emms-lyrics-start) (add-hook 'emms-player-stopped-hook 'emms-lyrics-stop) (add-hook 'emms-player-finished-hook 'emms-lyrics-stop) (add-hook 'emms-player-paused-hook 'emms-lyrics-pause) (add-hook 'emms-player-seeked-functions 'emms-lyrics-seek) (add-hook 'emms-player-time-set-functions 'emms-lyrics-sync)) (emms-lyrics-stop) (setq emms-lyrics-display-p nil) (emms-lyrics-restore-mode-line) (remove-hook 'emms-player-started-hook 'emms-lyrics-start) (remove-hook 'emms-player-stopped-hook 'emms-lyrics-stop) (remove-hook 'emms-player-finished-hook 'emms-lyrics-stop) (remove-hook 'emms-player-paused-hook 'emms-lyrics-pause) (remove-hook 'emms-player-seeked-functions 'emms-lyrics-seek) (remove-hook 'emms-player-time-set-functions 'emms-lyrics-sync))) (defun emms-lyrics-visit-lyric () "Visit playing track's lyric file. If we can't find it from local disk, then search it from internet." (interactive) (let* ((track (emms-playlist-current-selected-track)) (name (emms-track-get track 'name)) (lrc (funcall emms-lyrics-find-lyric-function (emms-replace-regexp-in-string (concat "\\." (file-name-extension name) "\\'") ".lrc" (file-name-nondirectory name))))) (if (and lrc (file-exists-p lrc) (not (string= lrc ""))) (find-file lrc) (message "lyric file does not exist, search it from internet...") (let ((title (emms-track-get track 'title)) (filename (file-name-sans-extension (file-name-nondirectory name))) (url "")) (unless title (setq title filename)) (cond ((string-match "\\cc" title) ; chinese lyrics ;; Since tag info might be encoded using various coding ;; systems, we'd better fall back on filename. (setq url (format "http://mp3.baidu.com/m?f=ms&rn=10&tn=baidump3lyric&ct=150994944&word=%s&lm=-1" (emms-url-quote-plus (encode-coding-string filename 'gb2312))))) (t ; english lyrics (setq url (format "http://search.lyrics.astraweb.com/?word=%s" ;;"http://www.lyrics007.com/cgi-bin/s.cgi?q=" (emms-url-quote-plus title))))) (browse-url url) (message "lyric file does not exist, search it from internet...done"))))) ;;; EMMS Lyrics (defvar emms-lyrics-alist nil "a list of the form: '((time0 . lyric0) (time1 . lyric1)...)). In short, at time-i, display lyric-i.") (defvar emms-lyrics-timers nil "timers for displaying lyric.") (defvar emms-lyrics-start-time nil "emms lyric start time.") (defvar emms-lyrics-pause-time nil "emms lyric pause time.") (defvar emms-lyrics-elapsed-time 0 "How long time has emms lyric played.") (defvar emms-lyrics-scroll-timers nil "Lyrics scroller timers.") (defun emms-lyrics-read-file (file &optional catchup) "Read a lyric file(LRC format). Optional CATCHUP is for recognizing `emms-lyrics-catchup'. FILE should end up with \".lrc\", its content looks like one of the following: [1:39]I love you, Emacs! [00:39]I love you, Emacs! [00:39.67]I love you, Emacs! FILE should be under the same directory as the music file, or under `emms-lyrics-dir'." (or catchup (setq file (funcall emms-lyrics-find-lyric-function file))) (when (and file (file-exists-p file)) (with-temp-buffer (let ((coding-system-for-read emms-lyrics-coding-system)) (emms-insert-file-contents file) (while (search-forward-regexp "\\[[0-9:.]+\\].*" nil t) (let ((lyric-string (match-string 0)) (time 0) (lyric "")) (setq lyric (emms-replace-regexp-in-string ".*\\]" "" lyric-string)) (while (string-match "\\[[0-9:.]+\\]" lyric-string) (let* ((time-string (match-string 0 lyric-string)) (semi-pos (string-match ":" time-string))) (setq time (+ (* (string-to-number (substring time-string 1 semi-pos)) 60) (string-to-number (substring time-string (1+ semi-pos) (1- (length time-string)))))) (setq lyric-string (substring lyric-string (length time-string))) (setq emms-lyrics-alist (append emms-lyrics-alist `((,time . ,lyric)))) (setq time 0))))) (setq emms-lyrics-alist (sort emms-lyrics-alist (lambda (a b) (< (car a) (car b)))))) t))) (defun emms-lyrics-create-buffer () "Create `emms-lyrics-buffer' dedicated to lyrics. " ;; leading white space in buffer name to hide the buffer (setq emms-lyrics-buffer (get-buffer-create " *EMMS Lyrics*")) (set-buffer emms-lyrics-buffer) (setq buffer-read-only nil cursor-type nil) (erase-buffer) (mapc (lambda (time-lyric) (insert (cdr time-lyric) "\n")) emms-lyrics-alist) (goto-char (point-min)) (emms-activate-highlighting-mode) (setq buffer-read-only t)) (defun emms-lyrics-start () "Start displaying lryics." (setq emms-lyrics-start-time (current-time) emms-lyrics-pause-time nil emms-lyrics-elapsed-time 0) (when (let ((file (emms-track-get (emms-playlist-current-selected-track) 'name))) (emms-lyrics-read-file (emms-replace-regexp-in-string (concat "\\." (file-name-extension file) "\\'") ".lrc" (file-name-nondirectory file)))) (when emms-lyrics-display-buffer (emms-lyrics-create-buffer)) (emms-lyrics-set-timer))) (defun emms-lyrics-catchup (lrc) "Catchup with later downloaded LRC file(full path). If you write some lyrics crawler, which is running asynchronically, then this function would be useful to call when the crawler finishes its job." (let ((old-start emms-lyrics-start-time)) (setq emms-lyrics-start-time (current-time) emms-lyrics-pause-time nil emms-lyrics-elapsed-time 0) (emms-lyrics-read-file lrc t) (emms-lyrics-set-timer) (emms-lyrics-seek (float-time (time-since old-start))))) (defun emms-lyrics-stop () "Stop displaying lyrics." (interactive) (when emms-lyrics-alist (mapc #'emms-cancel-timer emms-lyrics-timers) (if (or (not emms-player-paused-p) emms-player-stopped-p) (setq emms-lyrics-alist nil emms-lyrics-timers nil emms-lyrics-mode-line-string "")))) (defun emms-lyrics-pause () "Pause displaying lyrics." (if emms-player-paused-p (setq emms-lyrics-pause-time (current-time)) (when emms-lyrics-pause-time (setq emms-lyrics-elapsed-time (+ (float-time (time-subtract emms-lyrics-pause-time emms-lyrics-start-time)) emms-lyrics-elapsed-time))) (setq emms-lyrics-start-time (current-time))) (when emms-lyrics-alist (if emms-player-paused-p (emms-lyrics-stop) (emms-lyrics-set-timer)))) (defun emms-lyrics-seek (sec) "Seek forward or backward SEC seconds lyrics." (setq emms-lyrics-elapsed-time (+ emms-lyrics-elapsed-time (float-time (time-since emms-lyrics-start-time)) sec)) (when (< emms-lyrics-elapsed-time 0) ; back to start point (setq emms-lyrics-elapsed-time 0)) (setq emms-lyrics-start-time (current-time)) (when emms-lyrics-alist (let ((paused-orig emms-player-paused-p)) (setq emms-player-paused-p t) (emms-lyrics-stop) (setq emms-player-paused-p paused-orig)) (emms-lyrics-set-timer))) (defun emms-lyrics-sync (sec) "Synchronize the lyric display at SEC seconds." (setq emms-lyrics-start-time (current-time) emms-lyrics-elapsed-time 0) (emms-lyrics-seek sec)) (defun emms-lyrics-set-timer () "Set timers for displaying lyrics." (setq emms-lyrics-timers '()) (let ((lyrics-alist emms-lyrics-alist) (line 0)) (while lyrics-alist (let ((time (- (caar lyrics-alist) emms-lyrics-elapsed-time)) (lyric (cdar lyrics-alist)) (next-time (and (cdr lyrics-alist) (- (car (cadr lyrics-alist)) emms-lyrics-elapsed-time))) (next-lyric (and (cdr lyrics-alist) (cdr (cadr lyrics-alist))))) (setq line (1+ line)) (when (> time 0) (setq emms-lyrics-timers (append emms-lyrics-timers (list (run-at-time (format "%d sec" time) nil 'emms-lyrics-display-handler lyric next-lyric line (and next-time (- next-time time))))))) (setq lyrics-alist (cdr lyrics-alist)))))) (defun emms-lyrics-mode-line () "Add lyric to the mode line." (or global-mode-string (setq global-mode-string '(""))) (unless (member 'emms-lyrics-mode-line-string global-mode-string) (setq global-mode-string (append global-mode-string '(emms-lyrics-mode-line-string))))) (defun emms-lyrics-restore-mode-line () "Restore the mode line." (setq global-mode-string (remove 'emms-lyrics-mode-line-string global-mode-string)) (force-mode-line-update)) (defun emms-lyrics-display-handler (lyric next-lyric line diff) "DIFF is the timestamp differences between current LYRIC and NEXT-LYRIC; LINE corresponds line number for LYRIC in `emms-lyrics-buffer'." (emms-lyrics-display (format emms-lyrics-display-format lyric) line) (when (and emms-lyrics-display-on-modeline emms-lyrics-scroll-p) (emms-lyrics-scroll lyric next-lyric diff))) (defun emms-lyrics-display (lyric line) "Display LYRIC now. See `emms-lyrics-display-on-modeline' and `emms-lyrics-display-on-minibuffer' on how to config where to display." (when emms-lyrics-alist (when emms-lyrics-display-on-modeline (emms-lyrics-mode-line) (setq emms-lyrics-mode-line-string lyric) ;; (setq emms-lyrics-mode-line-string ; make it fit scroller width ;; (concat emms-lyrics-mode-line-string ;; (make-string ;; (abs (- emms-lyrics-scroll-width (length lyric))) ;; (string-to-char " ")))) (force-mode-line-update)) (when emms-lyrics-display-on-minibuffer (unless (minibuffer-window-active-p (selected-window)) (message lyric))) (when emms-lyrics-display-buffer (with-current-buffer emms-lyrics-buffer (when line (goto-char (point-min)) (forward-line (1- line)) (emms-line-highlight)))))) (defun emms-lyrics-find-lyric (file) "Return full path of found lrc FILE, or nil if not found. Use `emms-source-file-directory-tree-function' to find lrc FILE under current directory and `emms-lyrics-dir'. e.g., (emms-lyrics-find-lyric \"abc.lrc\")" (let* ((track (emms-playlist-current-selected-track)) (lyric-under-curr-dir (concat (file-name-directory (emms-track-get track 'name)) file))) (or (and (eq (emms-track-type track) 'file) (file-exists-p lyric-under-curr-dir) lyric-under-curr-dir) (car (funcall emms-source-file-directory-tree-function emms-lyrics-dir file))))) ;; (setq emms-lyrics-scroll-width 20) (defun emms-lyrics-scroll (lyric next-lyric diff) "Scroll LYRIC to left smoothly in DIFF seconds. DIFF is the timestamp differences between current LYRIC and NEXT-LYRIC." (setq diff (floor diff)) (setq emms-lyrics-scroll-timers '()) (let ((scrolled-lyric (concat lyric " " next-lyric)) (time 0) (pos 0)) (catch 'return (while (< time diff) (setq emms-lyrics-scroll-timers (append emms-lyrics-scroll-timers (list (run-at-time time nil 'emms-lyrics-display (if (>= (length lyric) pos) (substring scrolled-lyric pos) (throw 'return t)) nil)))) (setq time (+ time emms-lyrics-scroll-timer-interval)) (setq pos (1+ pos)))))) ;;; emms-lyrics-mode (defvar emms-lyrics-mode-map (let ((map (make-sparse-keymap))) (define-key map "p" 'emms-lyrics-previous-line) (define-key map "n" 'emms-lyrics-next-line) (define-key map "i" 'emms-lyrics-insert-time) map) "Keymap for `emms-lyrics-mode'.") (defun emms-lyrics-rem* (x y) "The remainder of X divided by Y, with the same sign as X." (let* ((q (floor x y)) (rem (- x (* y q)))) (if (= rem 0) 0 (if (eq (>= x 0) (>= y 0)) rem (- rem y))))) (defun emms-lyrics-insert-time () "Insert lyric time in the form: [01:23.21], then goto the beginning of next line." (interactive) (let* ((total (+ (float-time (time-subtract (current-time) emms-lyrics-start-time)) emms-lyrics-elapsed-time)) (min (/ (* (floor (/ total 60)) 100) 100)) (sec (/ (floor (* (emms-lyrics-rem* total 60) 100)) 100.0))) (insert (emms-replace-regexp-in-string " " "0" (format "[%2d:%2d]" min sec)))) (emms-lyrics-next-line)) (defun emms-lyrics-next-line () "Goto the beginning of next line." (interactive) (forward-line 1)) (defun emms-lyrics-previous-line () "Goto the beginning of previous line." (interactive) (forward-line -1)) (define-derived-mode emms-lyrics-mode nil "Emms Lyric" "Major mode for creating lyric files. \\{emms-lyrics-mode-map}" (run-hooks 'emms-lyrics-mode-hook)) (provide 'emms-lyrics) ;;; emms-lyrics.el ends here ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-maint.el����������������������������������������������0000664�0000000�0000000�00000000035�12205151475�0021420�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������(add-to-list 'load-path ".") ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-mark.el�����������������������������������������������0000664�0000000�0000000�00000023571�12205151475�0021254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-mark.el --- mark track like dired ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; ;; Author: Ye Wenbin <wenbinye@163.com> ;; This file is part of EMMS. ;; 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, or (at your option) ;; any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ;;; Commentary: ;; Provide mark operation to tracks ;; Put this file into your load-path and the following into your ~/.emacs: ;; (require 'emms-mark) ;; To activate it for the current buffer only, do: ;; (emms-mark-mode) ;; To make this the default EMMS mode, do: ;; (setq emms-playlist-default-major-mode 'emms-mark-mode) ;;; Code: (provide 'emms-mark) (require 'emms) (require 'emms-playlist-mode) (eval-when-compile (require 'cl)) ;;{{{ set new description-function (defun emms-mark-track-description (track) "Return a description of the current track." (assert (not (eq (default-value 'emms-track-description-function) 'emms-mark-track-description)) nil (concat "Do not set `emms-track-selection-function' to be" " emms-mark-track-description.")) (concat " " (funcall (default-value 'emms-track-description-function) track))) (defun emms-mark-update-descriptions () "Update the track descriptions in the current buffer." (emms-with-inhibit-read-only-t (save-excursion (goto-char (point-min)) (emms-walk-tracks (emms-playlist-update-track))))) ;;}}} ;;{{{ functions to mark tracks (defvar emms-mark-char ?*) (defvar emms-mark-face-alist '((?* . font-lock-warning-face) (?\040 . emms-playlist-track-face))) (defun emms-mark-track (&optional arg) "Mark the current track. If ARG is positive, also mark the next ARG-1 tracks as well. If ARG is negative, also mark the previous ARG-1 tracks." (interactive "p") (or arg (setq arg 1)) (let ((face (assoc-default emms-mark-char emms-mark-face-alist)) buffer-read-only track) (save-excursion (beginning-of-line) (while (and (not (eobp)) (/= arg 0)) (setq track (get-text-property (point) 'emms-track)) (delete-char 1) (insert (emms-propertize (string emms-mark-char) 'emms-track track)) (backward-char 1) (if (> arg 0) ;; Propertizing forward... (put-text-property (point) (progn (forward-line 1) (point)) 'face face) ;; ... and backward (let ((start (save-excursion (end-of-line) (point)))) (put-text-property (progn (beginning-of-line) (point)) start 'face face)) (forward-line -1)) (setq arg (if (> arg 0) (1- arg) (1+ arg))))))) (defun emms-mark-unmark-track (&optional arg) "Unmark the current track. If ARG is positive, also unmark the next ARG-1 tracks as well. If ARG is negative, also unmark the previous ARG-1 tracks." (interactive "p") (let ((emms-mark-char ?\040)) (emms-mark-track arg))) (defun emms-mark-forward (arg) "Mark one or more tracks and move the point past the newly-marked tracks. See `emms-mark-track' for further details." (interactive "p") (emms-mark-track arg) (forward-line arg)) (defun emms-mark-unmark-forward (arg) "Unmark one or more tracks and move the point past the tracks. See `emms-mark-unmark-track' for further details." (interactive "p") (emms-mark-unmark-track arg) (forward-line arg)) (defun emms-mark-all () "Mark all tracks in the current buffer." (interactive) (save-excursion (goto-char (point-min)) (emms-mark-track (count-lines (point-min) (point-max))))) (defun emms-mark-unmark-all () "Unmark all tracks in the current buffer." (interactive) (emms-mark-do-with-marked-track 'emms-mark-unmark-track)) (defun emms-mark-regexp (regexp arg) "Mark all tracks matching REGEXP. A prefix argument means to unmark them instead." (interactive (list (read-from-minibuffer (if current-prefix-arg "Mark tracks matching: " "Unmark tracks matching: ")) current-prefix-arg)) (let ((emms-mark-char (if arg ?\040 ?*))) (save-excursion (goto-char (point-min)) (while (re-search-forward regexp nil t) (emms-mark-track 1) (forward-line 1))))) (defun emms-mark-toggle () "Toggle all marks in the current buffer." (interactive) (save-excursion (goto-char (point-min)) (let (buffer-read-only) (while (not (eobp)) (if (eq ?\040 (following-char)) (emms-mark-track) (emms-mark-unmark-track)) (forward-line 1))))) (defsubst emms-mark-has-markedp () "Return non-nil if the playlist has a marked line, nil otherwise." (save-excursion (goto-char (point-min)) (re-search-forward (format "^[%c]" emms-mark-char) nil t))) ;;}}} ;;{{{ functions to operate marked tracks (defun emms-mark-do-with-marked-track (func &optional move) "Call FUNC on every marked line in current playlist. The function specified by FUNC takes no argument, so if the track on the marked line is needed, use `emms-playlist-track-at' to get it. The function can also modify the playlist buffer, such as deleting the current line. If the function doesn't move forward, be sure to set the second parameter MOVE to non-nil. Otherwise the function will never exit the loop." (let ((regexp (format "^[%c]" emms-mark-char)) (newfunc func)) (if move (setq newfunc (lambda () (funcall func) (forward-line 1)))) (save-excursion (goto-char (point-min)) (while (re-search-forward regexp nil t) (backward-char 1) ; move to beginning of line (funcall newfunc))))) (defun emms-mark-mapcar-marked-track (func &optional move) "This function does the same thing as `emms-mark-do-with-marked-track', the only difference being that this function collects the result of FUNC." (let ((regexp (format "^[%c]" emms-mark-char)) result (newfunc func)) (if move (setq newfunc (lambda () (let ((res (funcall func))) (forward-line 1) res)))) (save-excursion (goto-char (point-min)) (while (re-search-forward regexp nil t) (backward-char 1) ; move to beginning of line (setq result (cons (funcall newfunc) result))) (nreverse result)))) (defun emms-mark-delete-marked-tracks () "Delete all tracks that have been marked in the current buffer." (interactive) (emms-with-inhibit-read-only-t (emms-mark-do-with-marked-track (lambda nil (delete-region (point) (progn (forward-line 1) (point))))))) (defun emms-mark-kill-marked-tracks () "Kill all tracks that have been marked in the current buffer." (interactive) (let (tracks buffer-read-only) (emms-mark-do-with-marked-track (lambda nil (setq tracks (concat tracks (delete-and-extract-region (point) (progn (forward-line 1) (point))))))) (kill-new tracks))) (defun emms-mark-copy-marked-tracks () "Copy all tracks that have been marked in the current buffer." (interactive) (let (tracks) (emms-mark-do-with-marked-track (lambda nil (setq tracks (concat tracks (buffer-substring (point) (progn (forward-line 1) (point))))))) (kill-new tracks))) ;;}}} ;;{{{ mode stuff (defconst emms-mark-mode-map (let ((map (make-sparse-keymap))) (define-key map "W" 'emms-mark-copy-marked-tracks) (define-key map "K" 'emms-mark-kill-marked-tracks) (define-key map "D" 'emms-mark-delete-marked-tracks) (define-key map "m" 'emms-mark-forward) (define-key map "u" 'emms-mark-unmark-forward) (define-key map "U" 'emms-mark-unmark-all) (define-key map "t" 'emms-mark-toggle) (define-key map "%m" 'emms-mark-regexp) map) "Keymap for `emms-mark-mode'.") (defun emms-mark-mode () "An EMMS major mode that allows tracks to be marked like dired. \\{emms-mark-mode-map}" (interactive) (if (eq major-mode 'emms-mark-mode) ;; do nothing if we're already in emms-mark-mode nil ;; start emms-playlist-mode exactly once (setq emms-playlist-buffer-p t) (unless (eq major-mode 'emms-playlist-mode) (emms-playlist-mode)) ;; use inherited keymap (set-keymap-parent emms-mark-mode-map (current-local-map)) (use-local-map emms-mark-mode-map) (setq major-mode 'emms-mark-mode mode-name "Emms-Mark") ;; show a blank space at beginning of each line (set (make-local-variable 'emms-track-description-function) 'emms-mark-track-description) (emms-mark-update-descriptions))) (defun emms-mark-mode-disable () "Disable `emms-mark-mode' and return to `emms-playlist-mode'." (interactive) (if (not (eq major-mode 'emms-mark-mode)) ;; do nothing if we're not in emms-mark-mode nil ;; call emms-playlist-mode, saving important variables (let ((selected emms-playlist-selected-marker)) (emms-playlist-mode) (setq emms-playlist-selected-marker selected) (emms-playlist-mode-overlay-selected)) ;; update display (emms-mark-update-descriptions))) ;;}}} ;;; emms-mark.el ends here ���������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-metaplaylist-mode.el����������������������������������0000664�0000000�0000000�00000013765�12205151475�0023760�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-metaplaylist-mode.el --- A major mode for lists of Emms playlists ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Yoni Rabkin <yonirabkin@member.fsf.org> ;; This file is part of EMMS. ;; EMMS 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. ;; EMMS 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 EMMS; if not, write to the Free Software ;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ;; 02110-1301, USA. ;;; Commentary: ;; ;; `emms-metaplaylist-mode' creates an interactive list of all the ;; Emms playlist buffers. The currently active buffer is ;; highlighted. You can choose a buffer from the list with RET and get ;; taken there. ;;; Code: (require 'emms) (require 'emms-playlist-mode) ;;; -------------------------------------------------------- ;;; Variables, customisation and faces ;;; -------------------------------------------------------- (defgroup emms-metaplaylist-mode nil "*The Emacs Multimedia System meta-playlist mode." :prefix "emms-metaplaylist-mode-" :group 'multimedia) (defcustom emms-metaplaylist-mode-buffer-name "*Emms Playlists*" "*Name of the buffer in which Emms playlists will be listed." :type 'string :group 'emms-metaplaylist-mode) (defcustom emms-metaplaylist-mode-hooks nil "*List of hooks to run on entry to emms-metaplaylist-mode." :type 'list :group 'emms-metaplaylist-mode) (defface emms-metaplaylist-mode-face '((((class color) (background dark)) (:foreground "AntiqueWhite3")) (((class color) (background light)) (:foreground "red3")) (((type tty) (class mono)) (:inverse-video t)) (t (:background "WhiteSmoke"))) "Face for the buffer names in the playlists buffer." :group 'emms-metaplaylist-mode) (defface emms-metaplaylist-mode-current-face '((((class color) (background dark)) (:foreground "red2")) (((class color) (background light)) (:background "red3" :foreground "white")) (((type tty) (class mono)) (:inverse-video t)) (t (:background "red3"))) "Face for the current buffer name in the playlists buffer." :group 'emms-metaplaylist-mode) ;;; -------------------------------------------------------- ;;; Keymap ;;; -------------------------------------------------------- (defconst emms-metaplaylist-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map text-mode-map) (define-key map (kbd "n") 'next-line) (define-key map (kbd "p") 'previous-line) (define-key map (kbd "RET") 'emms-metaplaylist-mode-goto-current) (define-key map (kbd "q") 'kill-this-buffer) (define-key map (kbd "?") 'describe-mode) (define-key map (kbd "SPC") 'emms-metaplaylist-set-active) (define-key map (kbd "c") 'emms-metaplaylist-new-buffer) map) "Keymap for `emms-metaplaylist-mode'.") ;;; -------------------------------------------------------- ;;; Metaplaylist ;;; -------------------------------------------------------- (defun emms-metaplaylist-mode-goto-current () "Switch to the buffer at point." (interactive) (switch-to-buffer (buffer-substring (point-at-bol) (point-at-eol)))) ;; Since there will never be a significantly large amount of playlist ;; buffers co-existing at once, we allow ourselves not to keep ;; state. We regenerate the playlists buffer anew on demand. (defun emms-metaplaylist-mode-create () "Create or recreate the meta-playlist buffer." (let ((name emms-metaplaylist-mode-buffer-name) (playlists (emms-playlist-buffer-list))) (if playlists (progn (condition-case nil (kill-buffer name) (error nil)) (get-buffer-create name) (with-current-buffer name (emms-metaplaylist-mode) (save-excursion (mapc (lambda (buf) (let ((inhibit-read-only t)) (insert (buffer-name buf)) (add-text-properties (point-at-bol) (point-at-eol) (list 'face (if (eq buf emms-playlist-buffer) 'emms-metaplaylist-mode-current-face 'emms-metaplaylist-mode-face))) (newline))) playlists)) (current-buffer))) ; return the buffer as lisp obj (error "No Emms playlist buffers")))) ;;; -------------------------------------------------------- ;;; Playlist Management : creation, deletion (?) ;;; -------------------------------------------------------- (defun emms-metaplaylist-new-buffer (buffer-name) "Creates a new buffer called buffer-name, which will be ready to host EMMS tracks." (interactive "sBuffer Name: ") (if(get-buffer buffer-name) (error "Buffer must not exist.") (let ((buf (get-buffer-create buffer-name))) (with-current-buffer buf (setq emms-playlist-buffer-p t))) (message "Buffer created"))) (defun emms-metaplaylist-set-active () (interactive) (emms-playlist-set-playlist-buffer (get-buffer (buffer-substring (point-at-bol) (point-at-eol)))) (let ((ici (point))) (emms-metaplaylist-mode-go) (goto-char ici))) ;;; -------------------------------------------------------- ;;; Mode entry ;;; -------------------------------------------------------- (defun emms-metaplaylist-mode-go () "Single entry point to the metaplaylist interface." (interactive) (emms-metaplaylist-mode-create) (switch-to-buffer emms-metaplaylist-mode-buffer-name)) (defun emms-metaplaylist-mode () "A major mode for Emms playlists." ;; (interactive) (kill-all-local-variables) (use-local-map emms-metaplaylist-mode-map) (setq major-mode 'emms-metaplaylist-mode mode-name "Emms-MetaPlaylist") (setq buffer-read-only t) (run-hooks 'emms-metaplaylist-mode-hooks)) (provide 'emms-metaplaylist-mode) ;;; emms-metaplaylist-mode.el ends here �����������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-mode-line-icon.el�������������������������������������0000664�0000000�0000000�00000004603�12205151475�0023114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; emms-mode-line-icon.el --- show an icon in the Emacs mode-line ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Version: 1.1 ;; Keywords: emms ;; Author: Daniel Brockman <daniel@brockman.se> ;; Maintainer: Lucas Bonnet <lucas@rincevent.net> ;; This file is part of EMMS. ;; EMMS 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. ;; EMMS 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 EMMS; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ;; Commentary: ;; This EMMS extension shows an icon in the mode-line next to the ;; info-tag. ;; Code: (require 'emms-mode-line) (defvar emms-mode-line-icon-color "black" "Color of the little icon displayed in the mode-line.") (defvar emms-mode-line-icon-before-format "" "String to put before the icon, in the mode-line. For example, if you want to have something like : \[ <icon> Foo - The Foo Song ] You should set it to \"[\", and set emms-mode-line-format to \"%s ]\"") (defvar emms-mode-line-icon-image-cache `(image :type xpm :ascent center :data ,(concat "/* XPM */ static char *note[] = { /* width height num_colors chars_per_pixel */ \" 10 11 2 1\", /* colors */ \". c " emms-mode-line-icon-color "\", \"# c None s None\", /* pixels */ \"###...####\", \"###.#...##\", \"###.###...\", \"###.#####.\", \"###.#####.\", \"#...#####.\", \"....#####.\", \"#..######.\", \"#######...\", \"######....\", \"#######..#\"};"))) (defun emms-mode-line-icon-function () (concat " " emms-mode-line-icon-before-format (emms-propertize "NP:" 'display emms-mode-line-icon-image-cache) (emms-mode-line-playlist-current))) (setq emms-mode-line-mode-line-function 'emms-mode-line-icon-function) ;; This is needed for text properties to work in the mode line. (put 'emms-mode-line-string 'risky-local-variable t) (provide 'emms-mode-line-icon) ;;; emms-mode-line-icone.el ends here �����������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-mode-line.el������������������������������������������0000664�0000000�0000000�00000012257�12205151475�0022172�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-mode-line.el --- Mode-Line and titlebar infos for emms ;; Copyright (C) 2004, 2005, 2006, 2007, 2008, ;; 2009 Free Software Foundation, Inc. ;; Author: Mario Domgörgen <kanaldrache@gmx.de> ;; Keywords: multimedia ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; ;; To activate put simply the following line in your Emacs: ;; ;; (require 'emms-mode-line) ;; (emms-mode-line 1) ;;; Code: (require 'emms) (defgroup emms-mode-line nil "Showing information on mode-line and titlebar" :prefix "emms-mode-line-" :group 'emms) (defcustom emms-mode-line-mode-line-function 'emms-mode-line-playlist-current "Function for showing infos in mode-line or nil if don't want to." :type '(choice (const :tag "Don't show info on mode-line" nil) function) :group 'emms-mode-line) (defcustom emms-mode-line-titlebar-function nil "Function for showing infos in titlebar or nil if you don't want to." :type '(choice (const :tag "Don't show info on titlebar" nil) function) :group 'emms-mode-line) (defcustom emms-mode-line-format " [ %s ] " "String used for displaying the current track in mode-line and titlebar." :type 'string :group 'emms) (defun emms-mode-line-playlist-current () "Format the currently playing song." (format emms-mode-line-format (emms-track-description (emms-playlist-current-selected-track)))) (defvar emms-mode-line-active-p nil "If non-nil, emms mode line is active.") (defvar emms-mode-line-string "") (defvar emms-mode-line-initial-titlebar frame-title-format) (defun emms-mode-line (arg) "Turn on `emms-mode-line' if ARG is positive, off otherwise." (interactive "p") (or global-mode-string (setq global-mode-string '(""))) (if (and arg (> arg 0)) (progn (setq emms-mode-line-active-p t) (add-hook 'emms-track-updated-functions 'emms-mode-line-alter) (add-hook 'emms-player-finished-hook 'emms-mode-line-blank) (add-hook 'emms-player-stopped-hook 'emms-mode-line-blank) (add-hook 'emms-player-started-hook 'emms-mode-line-alter) (when (and emms-mode-line-mode-line-function (not (member 'emms-mode-line-string global-mode-string))) (setq global-mode-string (append global-mode-string '(emms-mode-line-string)))) (when emms-player-playing-p (emms-mode-line-alter))) (setq emms-mode-line-active-p nil) (remove-hook 'emms-track-updated-functions 'emms-mode-line-alter) (remove-hook 'emms-player-finished-hook 'emms-mode-line-blank) (remove-hook 'emms-player-stopped-hook 'emms-mode-line-blank) (remove-hook 'emms-player-started-hook 'emms-mode-line-alter) (emms-mode-line-restore-titlebar) (emms-mode-line-restore-mode-line))) ;;;###autoload (defun emms-mode-line-enable () "Turn on `emms-mode-line'." (interactive) (emms-mode-line 1) (message "emms mode line enabled")) ;;;###autoload (defun emms-mode-line-disable () "Turn off `emms-mode-line'." (interactive) (emms-mode-line -1) (message "emms mode line disabled")) ;;;###autoload (defun emms-mode-line-toggle () "Toggle `emms-mode-line'." (interactive) (if emms-mode-line-active-p (emms-mode-line-disable) (emms-mode-line-enable))) (defun emms-mode-line-alter (&optional track) "Alter mode-line/titlebar. Optional TRACK is used to be compatible with `emms-track-updated-functions'. It's simply ignored currently." (emms-mode-line-alter-mode-line) (emms-mode-line-alter-titlebar)) (defun emms-mode-line-alter-mode-line () "Update the mode-line with song info." (when (and emms-mode-line-mode-line-function emms-player-playing-p) (setq emms-mode-line-string (funcall emms-mode-line-mode-line-function)) (force-mode-line-update))) (defun emms-mode-line-alter-titlebar () "Update the titlebar with song info." (when emms-mode-line-titlebar-function (setq frame-title-format (list "" emms-mode-line-initial-titlebar (funcall emms-mode-line-titlebar-function))))) (defun emms-mode-line-blank () "Blank mode-line and titlebar but not quit `emms-mode-line'." (setq emms-mode-line-string nil) (force-mode-line-update) (emms-mode-line-restore-titlebar)) (defun emms-mode-line-restore-mode-line () "Restore the mode-line." (when emms-mode-line-mode-line-function (setq global-mode-string (remove 'emms-mode-line-string global-mode-string)) (force-mode-line-update))) (defun emms-mode-line-restore-titlebar () "Restore the mode-line." (when emms-mode-line-titlebar-function (setq frame-title-format (list emms-mode-line-initial-titlebar)))) (provide 'emms-mode-line) ;;; emms-mode-line.el ends here �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-player-mpd.el�����������������������������������������0000664�0000000�0000000�00000140733�12205151475�0022374�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-player-mpd.el --- MusicPD support for EMMS ;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Michael Olson <mwolson@gnu.org> ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;;; Benefits of MusicPD ;; MusicPD features crossfade, very little skipping, minor CPU usage, ;; many clients, many supported output formats, fast manipulation via ;; network processes, and good abstraction of client and server. ;;; MusicPD setup ;; If you want to set up a local MusicPD server, you'll need to have ;; mpd installed. If you want to use a remote server instance, no ;; installation is needed. ;; The website is at http://musicpd.org/. Debian packages are ;; available. It is recommended to use mpd version 0.12.0 or higher. ;; ;; Copy the example configuration for mpd into ~/.mpdconf and edit it ;; to your needs. Use your top level music directory for ;; music_directory. If your playlists use absolute file names, be ;; certain that music_directory has the leading directory part. ;; ;; Before you try to play anything, but after setting up the above, ;; run `mkdir ~/.mpd && mpd --create-db' to create MusicPD's track ;; database. ;; ;; Check to see if mpd is running. It must be running as a daemon for ;; you to be able to play anything. Launch it by executing "mpd". It ;; can be killed later with "mpd --kill" (or just "killall mpd" if ;; you're not using the latest development version). ;;; EMMS setup ;; Add the following to your config. ;; ;; (require 'emms-player-mpd) ;; Adjust `emms-player-mpd-server-name' and ;; `emms-player-mpd-server-port' to match the location and port of ;; your MusicPD server. ;; ;; (setq emms-player-mpd-server-name "localhost") ;; (setq emms-player-mpd-server-port "6600") ;; If your MusicPD setup requires a password, you will need to do the ;; following. ;; ;; (setq emms-player-mpd-server-password "mypassword") ;; To get track info from MusicPD, do the following. ;; ;; (add-to-list 'emms-info-functions 'emms-info-mpd) ;; To change the volume using MusicPD, do the following. ;; ;; (setq emms-volume-change-function 'emms-volume-mpd-change) ;; Add 'emms-player-mpd to the top of `emms-player-list'. ;; ;; (add-to-list 'emms-player-list 'emms-player-mpd) ;; If you use absolute file names in your m3u playlists (which is most ;; likely), make sure you set `emms-player-mpd-music-directory' to the ;; value of "music_directory" from your MusicPD config. There are ;; additional options available as well, but the defaults should be ;; sufficient for most uses. ;; You can set `emms-player-mpd-sync-playlist' to nil if your master ;; EMMS playlist contains only stored playlists. ;; If at any time you wish to replace the current EMMS playlist buffer ;; with the contents of the MusicPD playlist, type ;; M-x emms-player-mpd-connect. ;; ;; This will also run the relevant seek functions, so that if you use ;; emms-playing-time, the displayed time will be accurate. ;;; Contributors ;; Adam Sjøgren implemented support for changing the volume. (require 'emms-player-simple) (require 'emms-source-playlist) ; for emms-source-file-parse-playlist (require 'tq) (eval-when-compile (condition-case nil (progn (require 'url) ; load if available (require 'emms-url)) (error nil))) (defgroup emms-player-mpd nil "EMMS player for MusicPD." :group 'emms-player :prefix "emms-player-mpd-") (defcustom emms-player-mpd (emms-player 'emms-player-mpd-start 'emms-player-mpd-stop 'emms-player-mpd-playable-p) "*Parameters for the MusicPD player." :type '(cons symbol alist) :group 'emms-player-mpd) (defcustom emms-player-mpd-music-directory nil "The value of 'music_directory' in your MusicPD configuration file. You need this if your playlists use absolute file names, otherwise leave it set to nil." ;; The :format part ensures that entering directories happens on the ;; next line, where there is more space to work with :type '(choice :format "%{%t%}:\n %[Value Menu%] %v" (const nil) directory) :group 'emms-player-mpd) (defun emms-player-mpd-get-supported-regexp () "Returns a regexp of file extensions that MusicPD supports, or nil if we cannot figure it out." (let ((out (shell-command-to-string "mpd --version"))) ;; 0.17.x (if (string-match "Decoders plugins:$" out) (let* ((b (match-end 0)) (e (string-match "Output plugins:$" out)) (plugs (split-string (substring out b e) "\n" t)) (plugs (mapcan (lambda (x) (and (string-match " +\\[.*\\] +\\(.+\\)$" x) (split-string (match-string 1 x) nil t))) plugs)) (b (and (string-match "Protocols:$" out) (match-end 0))) (prots (and b (substring out (+ 2 b) -1))) (prots (split-string (or prots "") nil t))) (concat "\\(\\.\\(m3u\\|pls\\|" (regexp-opt (delq nil plugs)) "\\)\\'\\)\\|\\(\\`" (regexp-opt (delete "file://" prots)) "\\)")) (let ((found-start nil) (supported nil)) (if (string-match "Supported decoders:\\([^0]+?\\)Supported outputs:" out) ;; 0.15.x (setq supported (replace-regexp-in-string "\\[.+?\\]" "" (match-string 1 out))) ;; < 0.15 (setq out (split-string out "\n")) (while (car out) (cond ((string= (car out) "Supported formats:") (setq found-start t)) ((string= (car out) "") (setq found-start nil)) (found-start (setq supported (concat supported (car out))))) (setq out (cdr out)))) ;; Create regexp (when (and (stringp supported) (not (string= supported ""))) (concat "\\`http://\\|\\.\\(m3u\\|pls\\|" (regexp-opt (delq nil (split-string supported))) "\\)\\'")))))) (defcustom emms-player-mpd-supported-regexp ;; Use a sane default, just in case (or (emms-player-mpd-get-supported-regexp) (concat "\\`http://\\|" (emms-player-simple-regexp "m3u" "ogg" "flac" "mp3" "wav" "mod" "au" "aiff"))) "Formats supported by MusicPD." :type 'regexp :set (function (lambda (sym value) (set sym value) (emms-player-set emms-player-mpd 'regex value))) :group 'emms-player-mpd) (defcustom emms-player-mpd-connect-function 'open-network-stream "Function used to initiate the connection to MusicPD. It should take same arguments as `open-network-stream' does." :type 'function :group 'emms-player-mpd) (defcustom emms-player-mpd-server-name (or (getenv "MPD_HOST") "localhost") "The MusicPD server that we should connect to." :type 'string :group 'emms-player-mpd) (defcustom emms-player-mpd-server-port (or (getenv "MPD_PORT") "6600") "The port of the MusicPD server that we should connect to." :type '(choice number string) :group 'emms-player-mpd) (defcustom emms-player-mpd-server-password nil "The password for the MusicPD server that we should connect to." :type '(choice (const :tag "None" nil) string) :group 'emms-player-mpd) (defcustom emms-player-mpd-check-interval 1 "How often to check to see whether MusicPD has advanced to the next song. This may be an integer, a floating point number, or nil. If set to nil, this check will not be periodically performed. This variable is used only if `emms-player-mpd-sync-playlist' is non-nil." :type '(choice (const :tag "Disable check" nil) number) :group 'emms-player-mpd) (defcustom emms-player-mpd-verbose nil "Whether to provide notifications for server connection events and errors." :type 'boolean :group 'emms-player-mpd) (defcustom emms-player-mpd-sync-playlist t "Whether to synchronize the EMMS playlist with the MusicPD playlist. If your EMMS playlist contains music files rather than playlists, leave this set to non-nil. If your EMMS playlist contains stored playlists, set this to nil." :type 'boolean :group 'emms-player-mpd) (emms-player-set emms-player-mpd 'regex emms-player-mpd-supported-regexp) (emms-player-set emms-player-mpd 'pause 'emms-player-mpd-pause) (emms-player-set emms-player-mpd 'resume 'emms-player-mpd-pause) (emms-player-set emms-player-mpd 'seek 'emms-player-mpd-seek) (emms-player-set emms-player-mpd 'seek-to 'emms-player-mpd-seek-to) ;;; Dealing with the MusicPD network process (defvar emms-player-mpd-process nil) (defvar emms-player-mpd-queue nil) (defvar emms-player-mpd-playlist-id nil) (make-variable-buffer-local 'emms-player-mpd-playlist-id) (defvar emms-player-mpd-current-song nil) (defvar emms-player-mpd-last-state nil) (defvar emms-player-mpd-status-timer nil) (defvar emms-player-mpd-status-regexp "^\\(OK\\( MPD \\)?\\|ACK \\[\\([0-9]+\\)@[0-9]+\\] \\(.+\\)\\)\n+\\'" "Regexp that matches the valid status strings that MusicPD can return at the end of a request.") (defun emms-player-mpd-sentinel (proc event) "The process sentinel for MusicPD." (let ((status (process-status proc))) (cond ((string-match "^deleted" event) (when emms-player-mpd-verbose (message "MusicPD process was deleted"))) ((memq status '(exit signal closed)) (emms-player-mpd-close-process t) (when emms-player-mpd-verbose (message "Closed MusicPD process"))) ((memq status '(run open)) (when emms-player-mpd-verbose (message "MusicPD process started successfully"))) (t (when emms-player-mpd-verbose (message "Other MusicPD status change: %s, %s" status event)))))) ;; Ignore a useless byte-compile warning (eval-when-compile (put 'process-kill-without-query 'byte-compile nil)) (defun emms-player-mpd-ensure-process () "Make sure that a MusicPD process is currently active." (unless (and emms-player-mpd-process (processp emms-player-mpd-process) (memq (process-status emms-player-mpd-process) '(run open))) (setq emms-player-mpd-process (funcall emms-player-mpd-connect-function "mpd" nil emms-player-mpd-server-name emms-player-mpd-server-port)) (set-process-sentinel emms-player-mpd-process 'emms-player-mpd-sentinel) (setq emms-player-mpd-queue (tq-create emms-player-mpd-process)) (if (fboundp 'set-process-query-on-exit-flag) (set-process-query-on-exit-flag emms-player-mpd-process nil) (process-kill-without-query emms-player-mpd-process)) ;; send password (when (stringp emms-player-mpd-server-password) (tq-enqueue emms-player-mpd-queue (concat "password " emms-player-mpd-server-password "\n") emms-player-mpd-status-regexp nil #'ignore t)))) (defun emms-player-mpd-close-process (&optional from-sentinel) "Terminate the current MusicPD client process. FROM-SENTINEL indicates whether this was called by the process sentinel, in which case certain checks should not be made." (when (or from-sentinel (and (processp emms-player-mpd-process) (memq (process-status emms-player-mpd-process) '(run open)))) (tq-close emms-player-mpd-queue) (setq emms-player-mpd-queue nil) (setq emms-player-mpd-process nil))) (defun emms-player-mpd-send (question closure fn) "Send the given QUESTION to the MusicPD server. When a reply comes, call FN with CLOSURE and the result." (emms-player-mpd-ensure-process) (unless (string= (substring question -1) "\n") (setq question (concat question "\n"))) (tq-enqueue emms-player-mpd-queue question emms-player-mpd-status-regexp closure fn t)) ;;; Helper functions (defun emms-player-mpd-get-mpd-filename (file) "Turn FILE into something that MusicPD can understand. This usually means removing a prefix." (if (or (not emms-player-mpd-music-directory) (not (eq (aref file 0) ?/)) (string-match "\\`http://" file)) file (file-relative-name file emms-player-mpd-music-directory))) (defun emms-player-mpd-get-emms-filename (file) "Turn FILE into something that EMMS can understand. This usually means adding a prefix." (if (or (not emms-player-mpd-music-directory) (eq (aref file 0) ?/) (string-match "\\`http://" file)) file (expand-file-name file emms-player-mpd-music-directory))) (defun emms-player-mpd-parse-response (response) "Convert the given MusicPD response into a list. The car of the list is special: If an error has occurred, it will contain a cons cell whose car is an error number and whose cdr is the corresponding message. Otherwise, it will be nil." (when (stringp response) (save-match-data (let* ((data (split-string response "\n")) (cruft (last data 3)) (status (if (string= (cadr cruft) "") (car cruft) (cadr cruft)))) (setcdr cruft nil) (when (and (stringp (car data)) (string-match "^OK\\( MPD \\)?" (car data))) (setq data (cdr data))) (if (and (stringp status) (string-match "^ACK \\[\\([0-9]+\\)@[0-9]+\\] \\(.+\\)" status)) (cons (cons (match-string 1 status) (match-string 2 status)) data) (cons nil data)))))) (defun emms-player-mpd-parse-line (line) "Turn the given LINE from MusicPD into a cons cell. The format of the cell is (name . value)." (when (string-match "\\`\\([^:\n]+\\):\\s-*\\(.+\\)" line) (let ((name (match-string 1 line)) (value (match-string 2 line))) (if (and name value) (progn (setq name (downcase name)) (cons name value)) nil)))) (defun emms-player-mpd-get-alist (info) "Turn the given parsed INFO from MusicPD into an alist." (when (and info (null (car info)) ; no error has occurred (cdr info)) ; data exists (let ((alist nil) cell old-cell) (dolist (line (cdr info)) (when (setq cell (emms-player-mpd-parse-line line)) (if (setq old-cell (assoc (car cell) alist)) (setcdr old-cell (cdr cell)) (setq alist (cons cell alist))))) alist))) (defun emms-player-mpd-get-alists (info) "Turn the given parsed INFO from MusicPD into an list of alists. The list will be in reverse order." (when (and info (null (car info)) ; no error has occurred (cdr info)) ; data exists (let ((alists nil) (alist nil) cell) (dolist (line (cdr info)) (when (setq cell (emms-player-mpd-parse-line line)) (if (assoc (car cell) alist) (setq alists (cons alist alists) alist (list cell)) (setq alist (cons cell alist))))) (when alist (setq alists (cons alist alists))) alists))) (defun emms-player-mpd-get-tracks-1 (closure response) (let ((songs (emms-player-mpd-get-alists (emms-player-mpd-parse-response response))) (tracks nil)) (when songs (dolist (song-info songs) (let ((file (cdr (assoc "file" song-info)))) (when file (setq file (emms-player-mpd-get-emms-filename file)) (let* ((type (if (string-match "\\`http://" file) 'url 'file)) (track (emms-track type file))) (emms-info-mpd track song-info) (run-hook-with-args 'emms-track-info-filters track) (setq tracks (cons track tracks))))))) (funcall (car closure) (cdr closure) tracks))) (defun emms-player-mpd-get-tracks (closure callback) "Get the current playlist from MusicPD in the form of a list of EMMS tracks. Call CALLBACK with CLOSURE and result when the request is complete." (emms-player-mpd-send "playlistinfo" (cons callback closure) #'emms-player-mpd-get-tracks-1)) (defun emms-player-mpd-get-status-1 (closure response) (funcall (car closure) (cdr closure) (emms-player-mpd-get-alist (emms-player-mpd-parse-response response)))) (defun emms-player-mpd-get-status (closure callback) "Get status information from MusicPD. It will be returned in the form of an alist by calling CALLBACK with CLOSURE as its first argument, and the status as the second." (emms-player-mpd-send "status" (cons callback closure) #'emms-player-mpd-get-status-1)) (defun emms-player-mpd-get-status-part (closure callback item &optional info) "Get ITEM from the current MusicPD status. Call CALLBACK with CLOSURE and result when the request is complete. If INFO is specified, use that instead of acquiring the necessary info from MusicPD." (if info (funcall callback closure (cdr (assoc item info))) (emms-player-mpd-get-status (cons callback (cons closure item)) (lambda (closure info) (let ((fn (car closure)) (close (cadr closure)) (item (cddr closure))) (funcall fn close (cdr (assoc item info)))))))) (defun emms-player-mpd-get-playlist-id (closure callback &optional info) "Get the current playlist ID from MusicPD. Call CALLBACK with CLOSURE and result when the request is complete. If INFO is specified, use that instead of acquiring the necessary info from MusicPD." (when info (setq callback (lambda (closure id) id))) (emms-player-mpd-get-status-part closure callback "playlist" info)) (defun emms-player-mpd-get-volume (closure callback &optional info) "Get the current volume from MusicPD. Call CALLBACK with CLOSURE and result when the request is complete. If INFO is specified, use that instead of acquiring the necessary info from MusicPD." (when info (setq callback (lambda (closure volume) volume))) (emms-player-mpd-get-status-part closure callback "volume" info)) (defun emms-player-mpd-get-current-song (closure callback &optional info) "Get the current song from MusicPD. This is in the form of a number that indicates the position of the song on the current playlist. Call CALLBACK with CLOSURE and result when the request is complete. If INFO is specified, use that instead of acquiring the necessary info from MusicPD." (when info (setq callback (lambda (closure id) id))) (emms-player-mpd-get-status-part closure callback "song" info)) (defun emms-player-mpd-get-mpd-state (closure callback &optional info) "Get the current state of the MusicPD server. This is either \"play\", \"stop\", or \"pause\". Call CALLBACK with CLOSURE and result when the request is complete. If INFO is specified, use that instead of acquiring the necessary info from MusicPD." (when info (setq callback (lambda (closure id) id))) (emms-player-mpd-get-status-part closure callback "state" info)) (defun emms-player-mpd-get-playing-time (closure callback &optional info) "Get the number of seconds that the current song has been playing, or nil if we cannot obtain this information. Call CALLBACK with CLOSURE and result when the request is complete. If INFO is specified, use that instead of acquiring the necessary info from MusicPD." (if info (emms-player-mpd-get-status-part nil (lambda (closure time) (and time (string-match "\\`\\([0-9]+\\):" time) (string-to-number (match-string 1 time)))) "time" info) (emms-player-mpd-get-status-part (cons callback closure) (lambda (closure time) (funcall (car closure) (cdr closure) (and time (string-match "\\`\\([0-9]+\\):" time) (string-to-number (match-string 1 time))))) "time" info))) (defun emms-player-mpd-select-song (prev-song new-song) "Move to the given song position. The amount to move is the number difference between PREV-SONG and NEW-SONG. NEW-SONG should be a string containing a number. PREV-SONG may be either a string containing a number or nil, which indicates that we should start from the beginning of the buffer and move to NEW-SONG." (with-current-emms-playlist ;; move to current track (goto-char (if (and (stringp prev-song) emms-playlist-selected-marker (marker-position emms-playlist-selected-marker)) emms-playlist-selected-marker (point-min))) ;; seek forward or backward (let ((diff (if (stringp prev-song) (- (string-to-number new-song) (string-to-number prev-song)) (string-to-number new-song)))) (condition-case nil (progn ;; skip to first track if not on one (when (and (> diff 0) (not (emms-playlist-track-at (point)))) (emms-playlist-next)) ;; move to new track (while (> diff 0) (emms-playlist-next) (setq diff (- diff 1))) (while (< diff 0) (emms-playlist-previous) (setq diff (+ diff 1))) ;; select track at point (unless (emms-playlist-selected-track-at-p) (emms-playlist-select (point)))) (error (concat "Could not move to position " new-song)))))) (defun emms-player-mpd-sync-from-emms-1 (closure) (emms-player-mpd-get-playlist-id closure (lambda (closure id) (let ((buffer (car closure)) (fn (cdr closure))) (when (functionp fn) (funcall fn buffer id)))))) (defun emms-player-mpd-sync-from-emms (&optional callback) "Synchronize the MusicPD playlist with the contents of the current EMMS playlist. If CALLBACK is provided, call it with the current EMMS playlist buffer and MusicPD playlist ID when we are done, if there were no errors." (emms-player-mpd-clear) (with-current-emms-playlist (let (tracks) (save-excursion (setq tracks (nreverse (emms-playlist-tracks-in-region (point-min) (point-max))))) (emms-player-mpd-add-several-tracks tracks (cons (current-buffer) callback) #'emms-player-mpd-sync-from-emms-1)))) (defun emms-player-mpd-sync-from-mpd-2 (closure info) (let ((buffer (car closure)) (fn (cadr closure)) (close (cddr closure)) (id (emms-player-mpd-get-playlist-id nil #'ignore info)) (song (emms-player-mpd-get-current-song nil #'ignore info))) (when (buffer-live-p buffer) (let ((emms-playlist-buffer buffer)) (with-current-emms-playlist (setq emms-player-mpd-playlist-id id) (set-buffer-modified-p nil) (if song (emms-player-mpd-select-song nil song) (goto-char (point-min))))) (when (functionp fn) (funcall fn close info))))) (defun emms-player-mpd-sync-from-mpd-1 (closure tracks) (let ((buffer (car closure))) (when (and tracks (buffer-live-p buffer)) (let ((emms-playlist-buffer buffer)) (with-current-emms-playlist (emms-playlist-clear) (mapc #'emms-playlist-insert-track tracks))) (emms-player-mpd-get-status closure #'emms-player-mpd-sync-from-mpd-2)))) (defun emms-player-mpd-sync-from-mpd (&optional closure callback) "Synchronize the EMMS playlist with the contents of the current MusicPD playlist. Namely, clear the EMMS playlist buffer and add tracks to it that are present in the MusicPD playlist. If the current buffer is an EMMS playlist buffer, make it the main EMMS playlist buffer." (when (and emms-playlist-buffer-p (not (eq (current-buffer) emms-playlist-buffer))) (emms-playlist-set-playlist-buffer (current-buffer))) (with-current-emms-playlist (emms-player-mpd-get-tracks (cons emms-playlist-buffer (cons callback closure)) #'emms-player-mpd-sync-from-mpd-1))) (defun emms-player-mpd-detect-song-change-2 (state info) "Perform post-sync tasks after returning from a stop." (setq emms-player-mpd-current-song nil) (setq emms-player-playing-p 'emms-player-mpd) (when (string= state "pause") (setq emms-player-paused-p t)) (emms-player-mpd-detect-song-change info)) (defun emms-player-mpd-detect-song-change-1 (closure info) (let ((song (emms-player-mpd-get-current-song nil #'ignore info)) (state (emms-player-mpd-get-mpd-state nil #'ignore info)) (time (emms-player-mpd-get-playing-time nil #'ignore info)) (err-msg (cdr (assoc "error" info)))) (if (stringp err-msg) (progn (message "MusicPD error: %s" err-msg) (emms-player-mpd-send "clearerror" nil #'ignore)) (cond ((string= state "stop") (if song ;; a track remains: the user probably stopped MusicPD ;; manually, so we'll stop EMMS completely (let ((emms-player-stopped-p t)) (setq emms-player-mpd-last-state "stop") (emms-player-stopped)) ;; no more tracks are left: we probably ran out of things ;; to play, so let EMMS do something further if it wants (unless (string= emms-player-mpd-last-state "stop") (setq emms-player-mpd-last-state "stop") (emms-player-stopped)))) ((and emms-player-mpd-last-state (string= emms-player-mpd-last-state "stop")) ;; resume from a stop that occurred outside of EMMS (setq emms-player-mpd-last-state nil) (emms-player-mpd-sync-from-mpd state #'emms-player-mpd-detect-song-change-2)) ((string= state "pause") nil) ((string= state "play") (setq emms-player-mpd-last-state "play") (unless (or (null song) (and (stringp emms-player-mpd-current-song) (string= song emms-player-mpd-current-song))) (let ((emms-player-stopped-p t)) (emms-player-stopped)) (emms-player-mpd-select-song emms-player-mpd-current-song song) (setq emms-player-mpd-current-song song) (emms-player-started 'emms-player-mpd) (when time (run-hook-with-args 'emms-player-time-set-functions time)))))))) (defun emms-player-mpd-detect-song-change (&optional info) "Detect whether a song change has occurred. This is usually called by a timer. If INFO is specified, use that instead of acquiring the necessary info from MusicPD." (if info (emms-player-mpd-detect-song-change-1 nil info) (emms-player-mpd-get-status nil #'emms-player-mpd-detect-song-change-1))) (defun emms-player-mpd-quote-file (file) "Escape special characters in FILE and surround in double-quotes." (concat "\"" (emms-replace-regexp-in-string "\"" "\\\\\"" (emms-replace-regexp-in-string "\\\\" "\\\\\\\\" file)) "\"")) ;;;###autoload (defun emms-player-mpd-clear () "Clear the MusicPD playlist." (interactive) (when emms-player-mpd-status-timer (emms-cancel-timer emms-player-mpd-status-timer) (setq emms-player-mpd-status-timer nil)) (setq emms-player-mpd-last-state nil) (emms-player-mpd-send "clear" nil #'ignore) (let ((emms-player-stopped-p t)) (emms-player-stopped))) ;;; Adding to the MusicPD playlist (defun emms-player-mpd-add-file (file closure callback) "Add FILE to the current MusicPD playlist. Execute CALLBACK with CLOSURE as its first argument when done. If an error occurs, display a relevant message." (setq file (emms-player-mpd-get-mpd-filename file)) (emms-player-mpd-send (concat "add " (emms-player-mpd-quote-file file)) (cons file (cons callback closure)) (lambda (closure response) (let ((output (emms-player-mpd-parse-response response)) (file (car closure)) (callback (cadr closure)) (close (cddr closure))) (if (car output) (message "MusicPD error: %s: %s" file (cdar output)) (when (functionp callback) (funcall callback close))))))) (defun emms-player-mpd-add-buffer-contents (buffer closure callback) "Load contents of BUFFER into MusicPD by adding each line. Execute CALLBACK with CLOSURE as its first argument when done. This handles both m3u and pls type playlists." (with-current-buffer buffer (goto-char (point-min)) (let ((format (emms-source-playlist-determine-format))) (when format (emms-player-mpd-add-several-files (emms-source-playlist-files format) closure callback))))) (defun emms-player-mpd-add-playlist (playlist closure callback) "Load contents of PLAYLIST into MusicPD by adding each line. Execute CALLBACK with CLOSURE as its first argument when done. This handles both m3u and pls type playlists." ;; This is useful for playlists of playlists (with-temp-buffer (emms-insert-file-contents playlist) (emms-player-mpd-add-buffer-contents (current-buffer) closure callback))) (defun emms-player-mpd-add-streamlist (url closure callback) "Download contents of URL and then add its feeds into MusicPD. Execute CALLBACK with CLOSURE as its first argument when done." ;; This is useful with emms-streams.el (if (fboundp 'url-insert-file-contents) (progn (require 'emms-url) (with-temp-buffer (url-insert-file-contents (emms-url-quote-entire url)) (emms-http-decode-buffer (current-buffer)) (emms-player-mpd-add-buffer-contents (current-buffer) closure callback))) (error (message (concat "You need to install url.el so that" " Emms can retrieve this stream"))))) (defun emms-player-mpd-add (track closure callback) "Add TRACK to the MusicPD playlist. Execute CALLBACK with CLOSURE as its first argument when done." (let ((name (emms-track-get track 'name)) (type (emms-track-get track 'type))) (cond ((eq type 'url) (emms-player-mpd-add-file name closure callback)) ((eq type 'streamlist) (emms-player-mpd-add-streamlist name closure callback)) ((or (eq type 'playlist) (string-match "\\.\\(m3u\\|pls\\)\\'" name)) (emms-player-mpd-add-playlist name closure callback)) ((and (eq type 'file) (string-match emms-player-mpd-supported-regexp name)) (emms-player-mpd-add-file name closure callback))))) (defun emms-player-mpd-add-several-tracks (tracks closure callback) "Add TRACKS to the MusicPD playlist. Execute CALLBACK with CLOSURE as its first argument when done." (when (consp tracks) (while (cdr tracks) (emms-player-mpd-add (car tracks) nil #'ignore) (setq tracks (cdr tracks))) ;; only execute callback on last track (emms-player-mpd-add (car tracks) closure callback))) (defun emms-player-mpd-add-several-files (files closure callback) "Add FILES to the MusicPD playlist. Execute CALLBACK with CLOSURE as its first argument when done." (when (consp files) (while (cdr files) (emms-player-mpd-add-file (car files) nil #'ignore) (setq files (cdr files))) ;; only execute callback on last file (emms-player-mpd-add-file (car files) closure callback))) ;;; EMMS API (defun emms-player-mpd-playable-p (track) "Return non-nil when we can play this track." (and (memq (emms-track-type track) '(file url playlist streamlist)) (string-match (emms-player-get emms-player-mpd 'regex) (emms-track-name track)) (condition-case nil (progn (emms-player-mpd-ensure-process) t) (error nil)))) (defun emms-player-mpd-play (&optional id) "Play whatever is in the current MusicPD playlist. If ID is specified, play the song at that position in the MusicPD playlist." (if id (progn (unless (stringp id) (setq id (number-to-string id))) (emms-player-mpd-send (concat "play " id) nil (lambda (closure response) (setq emms-player-mpd-current-song nil) (if emms-player-mpd-check-interval (setq emms-player-mpd-status-timer (run-at-time t emms-player-mpd-check-interval 'emms-player-mpd-detect-song-change)) (emms-player-mpd-detect-song-change))))) ;; we only want to play one track, so don't start the timer (emms-player-mpd-send "play" nil (lambda (closure response) (emms-player-started 'emms-player-mpd))))) (defun emms-player-mpd-start-and-sync-2 (buffer id) (when (buffer-live-p buffer) (let ((emms-playlist-buffer buffer)) (with-current-emms-playlist (setq emms-player-mpd-playlist-id id) (set-buffer-modified-p nil) (let ((track-cnt 0)) (save-excursion (goto-char (if (and emms-playlist-selected-marker (marker-position emms-playlist-selected-marker)) emms-playlist-selected-marker (point-min))) (condition-case nil (while t (emms-playlist-previous) (setq track-cnt (1+ track-cnt))) (error nil))) (emms-player-mpd-play track-cnt)))))) (defun emms-player-mpd-start-and-sync-1 (closure id) (let ((buf-id (with-current-emms-playlist emms-player-mpd-playlist-id))) (if (and (not (buffer-modified-p emms-playlist-buffer)) (stringp buf-id) (string= buf-id id)) (emms-player-mpd-start-and-sync-2 emms-playlist-buffer id) (emms-player-mpd-sync-from-emms #'emms-player-mpd-start-and-sync-2)))) (defun emms-player-mpd-start-and-sync () "Ensure that MusicPD's playlist is up-to-date with EMMS's playlist, and then play the current track. This is called if `emms-player-mpd-sync-playlist' is non-nil." (when emms-player-mpd-status-timer (emms-cancel-timer emms-player-mpd-status-timer) (setq emms-player-mpd-status-timer nil)) (emms-player-mpd-send "clearerror" nil (lambda (closure response) (emms-player-mpd-get-playlist-id nil #'emms-player-mpd-start-and-sync-1)))) (defun emms-player-mpd-connect-1 (closure info) (setq emms-player-mpd-current-song nil) (let* ((state (emms-player-mpd-get-mpd-state nil #'ignore info))) (unless (string= state "stop") (setq emms-player-playing-p 'emms-player-mpd)) (when (string= state "pause") (setq emms-player-paused-p t)) (unless (string= state "stop") (emms-player-mpd-detect-song-change info) (when emms-player-mpd-check-interval (setq emms-player-mpd-status-timer (run-at-time t emms-player-mpd-check-interval 'emms-player-mpd-detect-song-change)))))) ;;;###autoload (defun emms-player-mpd-connect () "Connect to MusicPD and retrieve its current playlist. Afterward, the status of MusicPD will be tracked. This also has the effect of changing the current EMMS playlist to be the same as the current MusicPD playlist. Thus, this function is useful to call if the contents of the EMMS playlist buffer get out-of-sync for some reason." (interactive) (when emms-player-mpd-status-timer (emms-cancel-timer emms-player-mpd-status-timer) (setq emms-player-mpd-status-timer nil)) (emms-player-mpd-sync-from-mpd nil #'emms-player-mpd-connect-1)) (defun emms-player-mpd-start (track) "Starts a process playing TRACK." (interactive) (if (and emms-player-mpd-sync-playlist (not (memq (emms-track-get track 'type) '(streamlist playlist)))) (emms-player-mpd-start-and-sync) (emms-player-mpd-clear) ;; if we have loaded the item successfully, play it (emms-player-mpd-add track nil #'emms-player-mpd-play))) (defun emms-player-mpd-disconnect (&optional no-stop) "Terminate the MusicPD client process and disconnect from MusicPD. If NO-STOP is non-nil, do not indicate to EMMS that we are stopped. This argument is meant to be used when calling this from other functions." (interactive) (emms-cancel-timer emms-player-mpd-status-timer) (setq emms-player-mpd-status-timer nil) (setq emms-player-mpd-current-song nil) (setq emms-player-mpd-last-state nil) (emms-player-mpd-close-process) (unless no-stop (let ((emms-player-stopped-p t)) (emms-player-stopped)))) (defun emms-player-mpd-stop () "Stop the currently playing song." (interactive) (condition-case nil (emms-player-mpd-send "stop" nil #'ignore) (error nil)) (emms-player-mpd-disconnect t) (let ((emms-player-stopped-p t)) (emms-player-stopped))) (defun emms-player-mpd-pause () "Pause the currently playing song." (interactive) (emms-player-mpd-send "pause" nil #'ignore)) (defun emms-player-mpd-seek (amount) "Seek backward or forward by AMOUNT seconds, depending on sign of AMOUNT." (interactive) (emms-player-mpd-get-status amount (lambda (amount info) (let ((song (emms-player-mpd-get-current-song nil #'ignore info)) (secs (emms-player-mpd-get-playing-time nil #'ignore info))) (when (and song secs) (emms-player-mpd-send (concat "seek " song " " (number-to-string (round (+ secs amount)))) nil #'ignore)))))) (defun emms-player-mpd-seek-to (pos) "Seek to POS seconds from the start of the current track." (interactive) (emms-player-mpd-get-current-song pos (lambda (pos song) (when (and song pos) (emms-player-mpd-send (concat "seek " song " " (number-to-string (round pos))) nil #'ignore))))) (defun emms-player-mpd-next () "Move forward by one track in MusicPD's internal playlist." (interactive) (emms-player-mpd-send "next" nil #'ignore)) (defun emms-player-mpd-previous () "Move backward by one track in MusicPD's internal playlist." (interactive) (emms-player-mpd-send "previous" nil #'ignore)) ;;; Volume (defun emms-volume-mpd-change (amount) "Change volume up or down by AMOUNT, depending on whether it is positive or negative." (interactive "MVolume change amount (+ increase, - decrease): ") (emms-player-mpd-get-volume amount (lambda (change volume) (let ((new-volume (+ (string-to-number volume) change))) (emms-player-mpd-send (concat "setvol \"" (number-to-string new-volume) "\"") nil #'ignore))))) ;;; Now playing (defun emms-player-mpd-show-1 (closure response) (let* ((info (emms-player-mpd-get-alist (emms-player-mpd-parse-response response))) (insertp (car closure)) (callback (cadr closure)) (buffer (cddr closure)) (name (cdr (assoc "name" info))) ; radio feeds sometimes set this (file (cdr (assoc "file" info))) (desc nil)) ;; if we are playing lastfm radio, use its show function instead (if (and (boundp 'emms-lastfm-radio-stream-url) (stringp emms-lastfm-radio-stream-url) (string= emms-lastfm-radio-stream-url file)) (with-current-buffer buffer (and (fboundp 'emms-lastfm-np) (emms-lastfm-np insertp callback))) ;; otherwise build and show the description (when info (when name (setq desc name)) (when file (let ((track (emms-dictionary '*track*)) track-desc) (if (string-match "\\`http://" file) (emms-track-set track 'type 'url) (emms-track-set track 'type 'file)) (emms-track-set track 'name file) (emms-info-mpd track info) (run-hook-with-args 'emms-track-info-filters track) (setq track-desc (emms-track-description track)) (when (and (stringp track-desc) (not (string= track-desc ""))) (setq desc (if desc (concat desc ": " track-desc) track-desc)))))) (if (not desc) (unless (functionp callback) (message "Nothing playing right now")) (setq desc (format emms-show-format desc)) (cond ((functionp callback) (funcall callback buffer desc)) (insertp (when (buffer-live-p buffer) (with-current-buffer buffer (insert desc)))) (t (message "%s" desc))))))) ;;;###autoload (defun emms-player-mpd-show (&optional insertp callback) "Describe the current EMMS track in the minibuffer. If INSERTP is non-nil, insert the description into the current buffer instead. If CALLBACK is a function, call it with the current buffer and description as arguments instead of displaying the description or inserting it. This function uses `emms-show-format' to format the current track. It differs from `emms-show' in that it asks MusicPD for the current track, rather than EMMS." (interactive "P") (emms-player-mpd-send "currentsong" (cons insertp (cons callback (current-buffer))) #'emms-player-mpd-show-1)) ;;; Track info (defun emms-info-mpd-process (track info) (dolist (data info) (let ((name (car data)) (value (cdr data))) (setq name (cond ((string= name "artist") 'info-artist) ((string= name "composer") 'info-composer) ((string= name "performer") 'info-performer) ((string= name "title") 'info-title) ((string= name "album") 'info-album) ((string= name "track") 'info-tracknumber) ((string= name "date") 'info-year) ((string= name "genre") 'info-genre) ((string= name "time") (setq value (string-to-number value)) 'info-playing-time) (t nil))) (when name (emms-track-set track name value))))) (defun emms-info-mpd-1 (track response) (let ((info (emms-player-mpd-get-alist (emms-player-mpd-parse-response response)))) (when info (emms-info-mpd-process track info) (emms-track-updated track)))) (defun emms-info-mpd (track &optional info) "Add track information to TRACK. If INFO is specified, use that instead of acquiring the necessary info from MusicPD. This is a useful addition to `emms-info-functions'." (if info (emms-info-mpd-process track info) (when (and (eq 'file (emms-track-type track)) (not (string-match "\\`http://" (emms-track-name track)))) (let ((file (emms-player-mpd-get-mpd-filename (emms-track-name track)))) (when (or emms-player-mpd-music-directory (and file (string-match emms-player-mpd-supported-regexp file))) (condition-case nil (emms-player-mpd-send (concat "find filename " (emms-player-mpd-quote-file file)) track #'emms-info-mpd-1) (error nil))))))) ;;; Caching (defun emms-cache-set-from-mpd-track (track-info) "Dump TRACK-INFO into the EMMS cache. The track should be an alist as per `emms-player-mpd-get-alist'." (when emms-cache-set-function (let ((track (emms-dictionary '*track*)) (name (cdr (assoc "file" track-info)))) (when name (setq name (emms-player-mpd-get-emms-filename name)) (emms-track-set track 'type 'file) (emms-track-set track 'name name) (emms-info-mpd-process track track-info) (funcall emms-cache-set-function 'file name track))))) (defun emms-cache-set-from-mpd-directory (dir) "Dump all MusicPD data from DIR into the EMMS cache. This is useful to do when you have recently acquired new music." (interactive (list (if emms-player-mpd-music-directory (emms-read-directory-name "Directory: " emms-player-mpd-music-directory) (read-string "Directory: ")))) (unless (string= dir "") (setq dir (emms-player-mpd-get-mpd-filename dir))) (if emms-cache-set-function (progn (message "Dumping MusicPD data to cache...") (emms-player-mpd-send (concat "listallinfo " dir) nil (lambda (closure response) (message "Dumping MusicPD data to cache...processing") (let ((info (emms-player-mpd-get-alists (emms-player-mpd-parse-response response)))) (dolist (track-info info) (emms-cache-set-from-mpd-track track-info)) (message "Dumping MusicPD data to cache...done"))))) (error "Caching is not enabled"))) (defun emms-cache-set-from-mpd-all () "Dump all MusicPD data into the EMMS cache. This is useful to do once, just before using emms-browser.el, in order to prime the cache." (interactive) (emms-cache-set-from-mpd-directory "")) ;;; Updating tracks (defun emms-player-mpd-update-directory (dir) "Cause the tracks in DIR to be updated in the MusicPD database." (interactive (list (if emms-player-mpd-music-directory (emms-read-directory-name "Directory: " emms-player-mpd-music-directory) (read-string "Directory: ")))) (unless (string= dir "") (setq dir (emms-player-mpd-get-mpd-filename dir))) (emms-player-mpd-send (concat "update " (emms-player-mpd-quote-file dir)) nil (lambda (closure response) (let ((id (cdr (assoc "updating_db" (emms-player-mpd-get-alist (emms-player-mpd-parse-response response)))))) (if id (message "Updating DB with ID %s" id) (message "Could not update the DB")))))) (defun emms-player-mpd-update-all () "Cause all tracks in the MusicPD music directory to be updated in the MusicPD database." (interactive) (emms-player-mpd-update-directory "")) (defvar emms-player-mpd-waiting-for-update-timer nil "Timer object when waiting for MPD update to finish.") (defun emms-player-mpd-update-all-reset-cache () "Update all tracks in the MusicPD music directory. When update finishes, clear the EMMS cache and call `emms-cache-set-from-mpd-all' to dump the MusicPD data into the cache." (interactive) (if emms-player-mpd-waiting-for-update-timer (message "Already waiting for an update to finish.") (emms-player-mpd-send "update" nil 'emms-player-mpd-wait-for-update))) (defun emms-player-mpd-wait-for-update (&optional closure response) "Wait for a currently running mpd update to finish. Afterwards, clear the EMMS cache and call `emms-cache-set-from-mpd-all'." (if response ;; This is the first call after the update command (let ((id (cdr (assoc "updating_db" (emms-player-mpd-get-alist (emms-player-mpd-parse-response response)))))) (if id (progn (message "Updating DB with ID %s. Waiting for the update to finish..." id) (setq emms-player-mpd-waiting-for-update-timer (run-at-time 1 nil 'emms-player-mpd-wait-for-update))) (message "Could not update the DB"))) ;; Otherwise, check if update is still in progress (emms-player-mpd-get-status-part nil (lambda (closure updating) (if updating ;; MPD update still in progress, so wait another second (run-at-time 1 nil 'emms-player-mpd-wait-for-update) ;; MPD update finished (setq emms-player-mpd-waiting-for-update-timer nil) (message "MPD update finished.") (sit-for 1) (clrhash emms-cache-db) (emms-cache-set-from-mpd-all))) "updating_db"))) (provide 'emms-player-mpd) ;;; emms-player-mpd.el ends here �������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-player-mpg321-remote.el�������������������������������0000664�0000000�0000000�00000020327�12205151475�0024112�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-player-mpg321-remote.el --- play files with mpg321 -R ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Damien Elmes <emacs@repose.cx> ;; Keywords: emms, mp3, mpeg, multimedia ;; This file 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, or (at your option) ;; any later version. ;; This file 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 GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; This file provides an emms-player which uses mpg321's remote mode ;; to play files. This is a persistent process which isn't killed each ;; time a new file is played. ;; The remote process copes graciously with errors in music files, and ;; allows you to seek in files. ;; To enable this code, add the following to your emacs configuration: ;; (require 'emms-player-mpg321-remote) ;; (push 'emms-player-mpg321-remote emms-player-list) ;;; Code: (require 'emms) (require 'emms-player-simple) ;; -------------------------------------------------- ;; Variables and configuration ;; -------------------------------------------------- (defgroup emms-player-mpg321-remote nil "*EMMS player using mpg321's remote mode." :group 'emms-player :prefix "emms-player-mpg321-remote") (defcustom emms-player-mpg321-remote-command "mpg321" "*The command name of mpg321." :type 'string :group 'emms-player-mpg321-remote) (defcustom emms-player-mpg321-remote-parameters nil "*Extra arguments to pass to mpg321 when using remote mode For example: (list \"-o\" \"alsa\")" :type '(repeat string) :group 'emms-player-mpg321-remote) (defcustom emms-player-mpg321-remote (emms-player 'emms-player-mpg321-remote-start-playing 'emms-player-mpg321-remote-stop-playing 'emms-player-mpg321-remote-playable-p) "*A player for EMMS." :type '(cons symbol alist) :group 'emms-player-mpg321-remote) (defvar emms-player-mpg321-remote-initial-args (list "--skip-printing-frames=10" "-R" "-") "Initial args to pass to the mpg321 process.") (defvar emms-player-mpg321-remote-process-name "emms-player-mpg321-remote-proc" "The name of the mpg321 remote player process") (defvar emms-player-mpg321-remote-ignore-stop 0 "Number of stop messages to ignore, due to user action.") (defmacro emms-player-mpg321-remote-add (cmd func) `(emms-player-set 'emms-player-mpg321-remote ,cmd ,func)) (emms-player-mpg321-remote-add 'regex (emms-player-simple-regexp "mp3" "mp2")) (emms-player-mpg321-remote-add 'pause 'emms-player-mpg321-remote-pause) (emms-player-mpg321-remote-add 'resume 'emms-player-mpg321-remote-pause) (emms-player-mpg321-remote-add 'seek 'emms-player-mpg321-remote-seek) ;; -------------------------------------------------- ;; Process maintenence ;; -------------------------------------------------- (defun emms-player-mpg321-remote-start-process () "Start a new remote process, and return the process." (let ((process (apply 'start-process emms-player-mpg321-remote-process-name nil emms-player-mpg321-remote-command (append emms-player-mpg321-remote-initial-args emms-player-mpg321-remote-parameters)))) (set-process-sentinel process 'emms-player-mpg321-remote-sentinel) (set-process-filter process 'emms-player-mpg321-remote-filter) process)) (defun emms-player-mpg321-remote-stop () "Stop the currently playing process, if indeed there is one" (let ((process (emms-player-mpg321-remote-process))) (when process (kill-process process) (delete-process process)))) (defun emms-player-mpg321-remote-process () "Return the remote process, if it exists." (get-process emms-player-mpg321-remote-process-name)) (defun emms-player-mpg321-remote-running-p () "True if the remote process exists and is running." (let ((proc (emms-player-mpg321-remote-process))) (and proc (eq (process-status proc) 'run)))) (defun emms-player-mpg321-remote-sentinel (proc str) "Sentinel for determining the end of process" (when (or (eq (process-status proc) 'exit) (eq (process-status proc) 'signal)) ;; reset (setq emms-player-mpg321-remote-ignore-stop 0) (message "Remote process died!"))) (defun emms-player-mpg321-remote-send (text) "Send TEXT to the mpg321 remote process, and add a newline." (let (proc) ;; we shouldn't be trying to send to a dead process (unless (emms-player-mpg321-remote-running-p) (emms-player-mpg321-remote-start-process)) (setq proc (emms-player-mpg321-remote-process)) (process-send-string proc (concat text "\n")))) ;; -------------------------------------------------- ;; Interfacing with emms ;; -------------------------------------------------- (defun emms-player-mpg321-remote-filter (proc str) (let* ((data-lines (split-string str "\n" t)) data line cmd) (dolist (line data-lines) (setq data (split-string line)) (setq cmd (car data)) (cond ;; stop notice ((and (string= cmd "@P") (string= (cadr data) "0")) (emms-player-mpg321-remote-notify-emms)) ;; frame notice ((string= cmd "@F") ;; even though a timer is constantly updating this variable, ;; updating it here will cause it to stay pretty much in sync. (run-hook-with-args 'emms-player-time-set-functions (truncate (string-to-number (nth 3 data))))))))) (defun emms-player-mpg321-remote-start-playing (track) "Start playing a song by telling the remote process to play it. If the remote process is not running, launch it." (unless (emms-player-mpg321-remote-running-p) (emms-player-mpg321-remote-start-process)) (emms-player-mpg321-remote-play-track track)) (defun emms-player-mpg321-remote-notify-emms (&optional user-action) "Tell emms that the current song has finished. If USER-ACTION, set `emms-player-mpg321-remote-ignore-stop' so that we ignore the next message from mpg321." (if user-action (let ((emms-player-ignore-stop t)) ;; so we ignore the next stop message (setq emms-player-mpg321-remote-ignore-stop (1+ emms-player-mpg321-remote-ignore-stop)) (emms-player-stopped)) ;; not a user action (if (not (zerop emms-player-mpg321-remote-ignore-stop)) (setq emms-player-mpg321-remote-ignore-stop (1- emms-player-mpg321-remote-ignore-stop)) (emms-player-stopped)))) (defun emms-player-mpg321-remote-stop-playing () "Stop the current song playing." (emms-player-mpg321-remote-notify-emms t) (emms-player-mpg321-remote-send "stop")) (defun emms-player-mpg321-remote-play-track (track) "Send a play command to the remote, based on TRACK." (emms-player-mpg321-remote-send (concat "load " (emms-track-get track 'name))) (emms-player-started 'emms-player-mpg321-remote)) (defun emms-player-mpg321-remote-playable-p (track) ;; use the simple definition. (emms-player-mpg321-playable-p track)) (defun emms-player-mpg321-remote-pause () "Pause the player." (emms-player-mpg321-remote-send "pause")) (defun emms-player-mpg321-remote-resume () "Resume the player." (emms-player-mpg321-remote-send "pause")) (defun emms-player-mpg321-remote-seek (seconds) "Seek forward or backward in the file." ;; since mpg321 only supports seeking by frames, not seconds, we ;; make a very rough guess as to how much a second constitutes (let ((frame-string (number-to-string (* 35 seconds)))) ;; if we're not going backwards, we need to add a '+' (unless (eq ?- (string-to-char frame-string)) (setq frame-string (concat "+" frame-string))) (emms-player-mpg321-remote-send (concat "jump " frame-string)))) (provide 'emms-player-mpg321-remote) ;;; emms-player-mpg321-remote.el ends here ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-player-mplayer.el�������������������������������������0000664�0000000�0000000�00000005070�12205151475�0023257�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-player-mplayer.el --- mplayer support for EMMS ;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Authors: William Xu <william.xwl@gmail.com> ;; Jorgen Schaefer <forcer@forcix.cx> ;; This file is part of EMMS. ;; EMMS 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. ;; EMMS 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 EMMS; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;; This provides a player that uses mplayer. It supports pause and ;; seeking. For loading subtitles automatically, try adding ;; "sub-fuzziness=1" to your `~/.mplayer/config', see mplayer manual for ;; more. ;;; Code: (require 'emms-compat) (require 'emms-player-simple) (define-emms-simple-player mplayer '(file url) (concat "\\`\\(http\\|mms\\)://\\|" (emms-player-simple-regexp "ogg" "mp3" "wav" "mpg" "mpeg" "wmv" "wma" "mov" "avi" "divx" "ogm" "ogv" "asf" "mkv" "rm" "rmvb" "mp4" "flac" "vob" "m4a" "ape")) "mplayer" "-slave" "-quiet" "-really-quiet") (define-emms-simple-player mplayer-playlist '(streamlist) "\\`http://" "mplayer" "-slave" "-quiet" "-really-quiet" "-playlist") (emms-player-set emms-player-mplayer 'pause 'emms-player-mplayer-pause) ;;; Pause is also resume for mplayer (emms-player-set emms-player-mplayer 'resume nil) (emms-player-set emms-player-mplayer 'seek 'emms-player-mplayer-seek) (emms-player-set emms-player-mplayer 'seek-to 'emms-player-mplayer-seek-to) (defun emms-player-mplayer-pause () "Depends on mplayer's -slave mode." (process-send-string emms-player-simple-process-name "pause\n")) (defun emms-player-mplayer-seek (sec) "Depends on mplayer's -slave mode." (process-send-string emms-player-simple-process-name (format "seek %d\n" sec))) (defun emms-player-mplayer-seek-to (sec) "Depends on mplayer's -slave mode." (process-send-string emms-player-simple-process-name (format "seek %d 2\n" sec))) (provide 'emms-player-mplayer) ;;; emms-player-mplayer.el ends here ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-player-simple.el��������������������������������������0000664�0000000�0000000�00000020050�12205151475�0023072�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-player-simple.el --- A generic simple player. ;; Copyright (C) 2003, 2004, 2006, 2007, 2008, ;; 2009 Free Software Foundation, Inc. ;; Authors: Ulrik Jensen <terryp@daimi.au.dk> ;; Jorgen Schäfer <forcer@forcix.cx> ;; Keywords: emms, mpg321, ogg123, mplayer ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; This is a simple player interface - if you have an external player ;; that just expects the filename to play as an argument, this should ;; be able to use it. See the define-emms-simple-player lines at the ;; end of this file for examples. ;; Add the following to your `emms-player-list': ;; emms-player-mpg321 ;; emms-player-ogg123 ;; emms-player-mplayer ;;; Code: ;; Version control (defvar emms-player-simple-version "0.2 $Revision: 1.26 $" "Simple player for EMMS version string.") ;; $Id: emms-player-simple.el,v 1.26 2005/08/02 15:27:51 forcer Exp $ (require 'emms) ;; Customization (defmacro define-emms-simple-player (name types regex command &rest args) "Define a simple player with the use of `emms-define-player'. NAME is used to contruct the name of the function like emms-player-NAME. TYPES is a list of track types understood by this player. REGEX must be a regexp that matches the filenames the player can play. COMMAND specifies the command line arguement to call the player and ARGS are the command line arguements." (let ((group (intern (concat "emms-player-" (symbol-name name)))) (command-name (intern (concat "emms-player-" (symbol-name name) "-command-name"))) (parameters (intern (concat "emms-player-" (symbol-name name) "-parameters"))) (player-name (intern (concat "emms-player-" (symbol-name name)))) (start (intern (concat "emms-player-" (symbol-name name) "-start"))) (stop (intern (concat "emms-player-" (symbol-name name) "-stop"))) (playablep (intern (concat "emms-player-" (symbol-name name) "-playable-p")))) `(progn (defgroup ,group nil ,(concat "EMMS player for " command ".") :group 'emms-player :prefix ,(concat "emms-player-" (symbol-name name) "-")) (defcustom ,command-name ,command ,(concat "*The command name of " command ".") :type 'string :group ',group) (defcustom ,parameters ',args ,(concat "*The arguments to `" (symbol-name command-name) "'.") :type '(repeat string) :group ',group) (defcustom ,player-name (emms-player ',start ',stop ',playablep) ,(concat "*A player for EMMS.") :type '(cons symbol alist) :group ',group) (emms-player-set ,player-name 'regex ,regex) (emms-player-set ,player-name 'pause 'emms-player-simple-pause) (emms-player-set ,player-name 'resume 'emms-player-simple-resume) (defun ,start (track) "Start the player process." (emms-player-simple-start (emms-track-name track) ,player-name ,command-name ,parameters)) (defun ,stop () "Stop the player process." (emms-player-simple-stop)) (defun ,playablep (track) "Return non-nil when we can play this track." (and (executable-find ,command-name) (memq (emms-track-type track) ,types) (string-match (emms-player-get ,player-name 'regex) (emms-track-name track))))))) ;; Global variables (defvar emms-player-simple-process-name "emms-player-simple-process" "The name of the simple player process") (defun emms-player-simple-stop () "Stop the currently playing process, if indeed there is one" (let ((process (get-process emms-player-simple-process-name))) (when process (kill-process process) (delete-process process)))) ;; Utility-functions (defun emms-player-simple-start (filename player cmdname params) "Starts a process playing FILENAME using the specified CMDNAME with the specified PARAMS. PLAYER is the name of the current player." (let ((process (apply 'start-process emms-player-simple-process-name nil cmdname ;; splice in params here (append params (list filename))))) ;; add a sentinel for signaling termination (set-process-sentinel process 'emms-player-simple-sentinel)) (emms-player-started player)) (defun emms-player-simple-sentinel (proc str) "Sentinel for determining the end of process" (when (or (eq (process-status proc) 'exit) (eq (process-status proc) 'signal)) (emms-player-stopped))) (defun emms-player-simple-pause () "Pause the player by sending a SIGSTOP." (signal-process (get-process emms-player-simple-process-name) 'SIGSTOP)) (defun emms-player-simple-resume () "Resume the player by sending a SIGCONT." (signal-process (get-process emms-player-simple-process-name) 'SIGCONT)) (defun emms-player-simple-regexp (&rest extensions) "Return a regexp matching all EXTENSIONS, case-insensitively." (concat "\\.\\(" (mapconcat (lambda (extension) (mapconcat (lambda (char) (let ((u (upcase char)) (d (downcase char))) (if (= u d) (format "%c" char) (format "[%c%c]" u d)))) extension "")) extensions "\\|") "\\)\\'")) (define-emms-simple-player mpg321 '(file url) (emms-player-simple-regexp "mp3" "mp2") "mpg321") (define-emms-simple-player ogg123 '(file) (emms-player-simple-regexp "ogg" "flac") "ogg123") (define-emms-simple-player speexdec '(file) (emms-player-simple-regexp "spx") "speexdec") (define-emms-simple-player playsound '(file) (emms-player-simple-regexp "wav") "playsound") (define-emms-simple-player mikmod '(file) (emms-player-simple-regexp "669" "amf" "dsm" "far" "gdm" "it" "imf" "mod" "med" "mtm" "okt" "s3m" "stm" "stx" "ult" "apun" "xm" "mod") "mikmod" "-q" "-p" "1" "-X") (define-emms-simple-player timidity '(file) (emms-player-simple-regexp "mid" "rmi" "rcp" "r36" "g18" "g36" "mfi") "timidity") (define-emms-simple-player fluidsynth '(file) (emms-player-simple-regexp "mid") "fluidsynth" "-aalsa" "-in" "/media/music/sf/FluidR3-GM.SF2") (define-emms-simple-player alsaplayer '(file url) (concat "\\`http://\\|" (emms-player-simple-regexp "ogg" "mp3" "wav" "flac" "pls" "m3u")) "alsaplayer" "--quiet" "--nosave" "\"--interface text\"") (emms-player-set emms-player-alsaplayer 'pause 'emms-player-alsaplayer-pause) ;;; Pause is also resume for alsaplayer (emms-player-set emms-player-alsaplayer 'resume nil) (emms-player-set emms-player-alsaplayer 'seek 'emms-player-alsaplayer-seek) (defun emms-player-alsaplayer-pause () (call-process "alsaplayer" nil nil nil "--pause")) (defun emms-player-alsaplayer-seek (sec) (call-process "alsaplayer" nil nil nil "--relative" (format "%d" sec))) (provide 'emms-player-simple) ;;; emms-player-simple.el ends here ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-player-vlc.el�����������������������������������������0000664�0000000�0000000�00000006306�12205151475�0022375�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-player-vlc.el --- vlc support for EMMS ;; Copyright (C) 2008, 2009 Free Software Foundation, Inc. ;; Authors: Yoni Rabkin <yonirabkin@member.fsf.org> ;; This file is part of EMMS. ;; EMMS 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. ;; EMMS 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 EMMS; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;;; Code: (require 'emms-compat) (require 'emms-player-simple) ;; I use this macro, and later override some of the stuff it defines ;; in order to accomodate VLC's particular idioms. (define-emms-simple-player vlc '(file url) (concat "\\`\\(http\\|mms\\)://\\|" (emms-player-simple-regexp "ogg" "mp3" "wav" "mpg" "mpeg" "wmv" "wma" "mov" "avi" "divx" "ogm" "ogv" "asf" "mkv" "rm" "rmvb" "mp4" "flac" "vob" "m4a" "ape")) "vlc" "--intf=rc") (define-emms-simple-player vlc-playlist '(streamlist) "\\`http://" "vlc" "--intf=rc") ;; (kludge) By default, VLC does not quit after finishing to play a ;; track, so the player sentinel has no way of telling that the next ;; track should be played. Therefore I redefine this low-level ;; function and add a "quit" track which is invisible to Emms. (defadvice emms-player-vlc-start (around quit-vlc-after-finish activate) (let ((process (apply 'start-process emms-player-simple-process-name nil emms-player-vlc-command-name ;; splice in params here (append emms-player-vlc-parameters (list (emms-track-name (ad-get-arg 0))) '("vlc://quit"))))) ;; add a sentinel for signaling termination (set-process-sentinel process 'emms-player-simple-sentinel)) (emms-player-started emms-player-vlc)) (defun emms-player-vlc-pause () "Depends on vlc's rc mode." (process-send-string emms-player-simple-process-name "pause\n")) (defun emms-player-vlc-seek (sec) "Seek relative within a stream." (when (not (= 0 sec)) (process-send-string emms-player-simple-process-name (if (< 0 sec) "fastforward\n" "rewind\n")))) (defun emms-player-vlc-seek-to (sec) "Seek to time SEC within the stream." (process-send-string emms-player-simple-process-name (format "seek %d\n" sec))) (emms-player-set emms-player-vlc 'pause 'emms-player-vlc-pause) (emms-player-set emms-player-vlc 'resume nil) ; pause is also resume (emms-player-set emms-player-vlc 'start 'emms-player-vlc-start) (emms-player-set emms-player-vlc 'seek 'emms-player-vlc-seek) (emms-player-set emms-player-vlc 'seek-to 'emms-player-vlc-seek-to) (provide 'emms-player-vlc) ;;; emms-player-vlc.el ends here ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-player-xine.el����������������������������������������0000664�0000000�0000000�00000006001�12205151475�0022544�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-player-xine.el --- xine support for EMMS ;; Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Tassilo Horn <tassilo@member.fsf.org> ;; This file is part of EMMS. ;; EMMS 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. ;; EMMS 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 EMMS; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;; This provides a player that uses xine. It supports pause and ;; seeking. ;;; Code: ;; TODO: The video window cannot be disabled. I asked on ;; gmane.comp.video.xine.user (<87y7ohqcbq.fsf@baldur.tsdh.de>)... ;; TODO: Implement seek-to with "SetPositionX%\n" where X is in {0,10,..,90} (require 'emms-player-simple) (define-emms-simple-player xine '(file url) (concat "\\`\\(http\\|mms\\)://\\|" (emms-player-simple-regexp "ogg" "mp3" "wav" "mpg" "mpeg" "wmv" "wma" "mov" "avi" "divx" "ogm" "ogv" "asf" "mkv" "rm" "rmvb" "mp4" "flac" "vob")) "xine" "--no-gui" "--no-logo" "--no-splash" "--no-reload" "--stdctl") (emms-player-set emms-player-xine 'pause 'emms-player-xine-pause) ;;; Pause is also resume for xine (emms-player-set emms-player-xine 'resume nil) (emms-player-set emms-player-xine 'seek 'emms-player-xine-seek) (defun emms-player-xine-pause () "Depends on xine's --stdctl mode." (process-send-string emms-player-simple-process-name "pause\n")) (defun emms-player-xine-seek (secs) "Depends on xine's --stdctl mode." ;; xine-ui's stdctl supports only seeking forward/backward in 7/15/30 and 60 ;; second steps, so we take the value that is nearest to SECS. (let ((s (emms-nearest-value secs '(-60 -30 -15 -7 7 15 30 60)))) (when (/= s secs) (message (concat "EMMS: Xine only supports seeking for [+/-] 7/15/30/60 " "seconds, so we seeked %d seconds") s)) (process-send-string emms-player-simple-process-name (if (< s 0) (format "SeekRelative%d\n" s) (format "SeekRelative+%d\n" s))))) (defun emms-nearest-value (val list) "Returns the value of LIST which is nearest to VAL. LIST should be a list of integers." (let* ((nearest (car list)) (dist (abs (- val nearest)))) (dolist (lval (cdr list)) (let ((ndist (abs (- val lval)))) (when (< ndist dist) (setq nearest lval dist ndist)))) nearest)) (provide 'emms-player-xine) ;;; emms-player-xine.el ends here �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-playing-time.el���������������������������������������0000664�0000000�0000000�00000017726�12205151475�0022726�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-playing-time.el --- Display emms playing time on mode line ;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: William Xu <william.xwl@gmail.com> ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;; Display playing time on mode line, it looks like: 01:32/04:09. ;; Put this file into your load-path and the following into your ;; ~/.emacs: ;; (require 'emms-playing-time) ;; (emms-playing-time 1) ;; Note: `(emms-playing-time -1)' will disable emms-playing-time module ;; completely, and is not recommended. (since some other emms modules ;; may rely on it, such as `emms-lastfm.el') ;; Instead, to toggle displaying playing time on mode line, one could ;; call `emms-playing-time-enable-display' and ;; `emms-playing-time-disable-display'." ;;; Code: (eval-when-compile (require 'cl)) (require 'emms-info) (require 'emms-player-simple) ;;; Customizations (defgroup emms-playing-time nil "Playing-time module for EMMS." :group 'emms) (defcustom emms-playing-time-display-short-p nil "Non-nil will only display elapsed time. e.g., display 02:37 instead of 02:37/05:49." :type 'boolean :group 'emms-playing-time) (defcustom emms-playing-time-display-format " %s " "Format used for displaying playing time." :type 'string :group 'emms-playing-time) (defcustom emms-playing-time-style 'time "Style used for displaying playing time. Valid styles are `time' (e.g., 01:30/4:20) and `bar' (e.g., [===> ])." :type 'symbol :group 'emms-playing-time) ;;; Emms Playing Time (defvar emms-playing-time-display-p nil "Whether to display playing time on mode line or not") (defvar emms-playing-time 0 "Time elapsed in current track.") (defvar emms-playing-time-string "") (defvar emms-playing-time-display-timer nil) (defvar emms-playing-time-p nil "Whether emms-playing-time module is enabled or not") (defun emms-playing-time-start () "Get ready for display playing time." (setq emms-playing-time 0) (unless emms-playing-time-display-timer (setq emms-playing-time-display-timer (run-at-time t 1 'emms-playing-time-display)))) (defun emms-playing-time-stop () "Remove playing time on the mode line." (if (or (not emms-player-paused-p) emms-player-stopped-p) (progn (setq emms-playing-time-string "") (force-mode-line-update))) (emms-cancel-timer emms-playing-time-display-timer) (setq emms-playing-time-display-timer nil)) (defun emms-playing-time-pause () "Pause playing time." (if emms-player-paused-p (emms-playing-time-stop) (unless emms-playing-time-display-timer (setq emms-playing-time-display-timer (run-at-time t 1 'emms-playing-time-display))))) (defun emms-playing-time-seek (sec) "Seek forward or backward SEC playing time." (setq emms-playing-time (+ emms-playing-time sec)) (when (< emms-playing-time 0) ; back to start point (setq emms-playing-time 0))) (defun emms-playing-time-set (sec) "Set the playing time to SEC." (setq emms-playing-time sec) (when (< emms-playing-time 0) ; back to start point (setq emms-playing-time 0))) (defun emms-playing-time (arg) "Turn on emms playing time if ARG is positive, off otherwise. Note: `(emms-playing-time -1)' will disable emms-playing-time module completely, and is not recommended. (since some other emms modules may rely on it, such as `emms-lastfm.el') Instead, to toggle displaying playing time on mode line, one could call `emms-playing-time-enable-display' and `emms-playing-time-disable-display'." (if (and arg (> arg 0)) (progn (setq emms-playing-time-p t emms-playing-time-display-p t) (emms-playing-time-mode-line) (add-hook 'emms-player-started-hook 'emms-playing-time-start) (add-hook 'emms-player-stopped-hook 'emms-playing-time-stop) (add-hook 'emms-player-finished-hook 'emms-playing-time-stop) (add-hook 'emms-player-paused-hook 'emms-playing-time-pause) (add-hook 'emms-player-seeked-functions 'emms-playing-time-seek) (add-hook 'emms-player-time-set-functions 'emms-playing-time-set)) (setq emms-playing-time-p nil emms-playing-time-display-p nil) (emms-playing-time-stop) (emms-playing-time-restore-mode-line) (remove-hook 'emms-player-started-hook 'emms-playing-time-start) (remove-hook 'emms-player-stopped-hook 'emms-playing-time-stop) (remove-hook 'emms-player-finished-hook 'emms-playing-time-stop) (remove-hook 'emms-player-paused-hook 'emms-playing-time-pause) (remove-hook 'emms-player-seeked-functions 'emms-playing-time-seek) (remove-hook 'emms-player-time-set-functions 'emms-playing-time-set))) ;;;###autoload (defun emms-playing-time-enable-display () "Display playing time on mode line." (interactive) (setq emms-playing-time-display-p t)) ;;;###autoload (defun emms-playing-time-disable-display () "Remove playing time from mode line." (interactive) (setq emms-playing-time-display-p nil)) (defun emms-playing-time-display () "Display playing time on the mode line." (setq emms-playing-time (round (1+ emms-playing-time))) (setq emms-playing-time-string "") (when emms-playing-time-display-p (let* ((min (/ emms-playing-time 60)) (sec (% emms-playing-time 60)) (total-playing-time (or (emms-track-get (emms-playlist-current-selected-track) 'info-playing-time) 0)) (total-min-only (/ total-playing-time 60)) (total-sec-only (% total-playing-time 60))) (case emms-playing-time-style ((bar) ; `bar' style (if (zerop total-playing-time) (setq emms-playing-time-string "[==>........]") (let ((progress "[") ;; percent based on 10 (percent (/ (* emms-playing-time 10) total-playing-time))) (dotimes (i percent) (setq progress (concat progress "="))) (setq progress (concat progress ">")) (dotimes (i (- 10 percent)) (setq progress (concat progress " "))) (setq progress (concat progress "]")) (setq emms-playing-time-string progress)))) (t ; `time' style (setq emms-playing-time-string (emms-replace-regexp-in-string " " "0" (if (or emms-playing-time-display-short-p ;; unable to get total playing-time (eq total-playing-time 0)) (format "%2d:%2d" min sec) (format "%2d:%2d/%2s:%2s" min sec total-min-only total-sec-only)))))) (setq emms-playing-time-string (format emms-playing-time-display-format emms-playing-time-string)))) (force-mode-line-update)) (defun emms-playing-time-mode-line () "Add playing time to the mode line." (or global-mode-string (setq global-mode-string '(""))) (unless (member 'emms-playing-time-string global-mode-string) (setq global-mode-string (append global-mode-string '(emms-playing-time-string))))) (defun emms-playing-time-restore-mode-line () "Restore the mode line." (setq global-mode-string (remove 'emms-playing-time-string global-mode-string)) (force-mode-line-update)) (provide 'emms-playing-time) ;;; emms-playing-time.el ends here ������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-playlist-limit.el�������������������������������������0000664�0000000�0000000�00000014566�12205151475�0023303�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-playlist-limit.el --- Limit playlist by various info ;; Copyright (C) 2007, 2008, 2009 William Xu ;; Author: William Xu <william.xwl@gmail.com> ;; Keywords: emms, limit ;; EMMS 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, or (at your option) ;; any later version. ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Code: (require 'emms-playlist-mode) ;;; User Interfaces (defgroup emms-playlist-limit nil "Playlist limit module for EMMS." :group 'emms) (defcustom emms-playlist-limit-hook nil "Hooks to run after each limit operations." :type 'symbol :group 'emms-playing-limit) (defvar emms-playlist-limit-enabled-p nil "If non-nil, emms playlist limit is enabled.") (defun emms-playlist-limit (arg) "Turn on emms playlist limit if ARG is positive, off otherwise." (interactive "p") (if (and arg (> arg 0)) (progn (setq emms-playlist-limit-enabled-p t) (add-hook 'emms-playlist-source-inserted-hook 'emms-playlist-limit-insert)) (setq emms-playlist-limit-enabled-p nil) (remove-hook 'emms-playlist-source-inserted-hook 'emms-playlist-limit-insert))) ;;;###autoload (defun emms-playlist-limit-enable () "Turn on emms playlist limit." (interactive) (emms-playlist-limit 1) (message "emms playlist limit enabled")) ;;;###autoload (defun emms-playlist-limit-disable () "Turn off emms playlist limit." (interactive) (emms-playlist-limit -1) (message "emms playlist limit disabled")) ;;;###autoload (defun emms-playlist-limit-toggle () "Toggle emms playlist limit." (interactive) (if emms-playlist-limit-enabled-p (emms-playlist-limit-disable) (emms-playlist-limit-enable))) (defmacro define-emms-playlist-limit (attribute) "Macro for defining emms playlist limit functions." `(defun ,(intern (format "emms-playlist-limit-to-%s" attribute)) (regexp) ,(format "Limit to playlists that have %s that matches REGEXP." attribute) (interactive (list (let* ((curr (or (emms-track-get (emms-playlist-track-at) (quote ,attribute)) (emms-track-get (emms-playlist-selected-track) (quote ,attribute)))) (attr-name ,(emms-replace-regexp-in-string "info-" "" (symbol-name attribute))) (fmt (if curr (format "Limit to %s (regexp = %s): " attr-name curr) (format "Limit to %s (regexp): " attr-name)))) (read-string fmt)))) (when (string= regexp "") (setq regexp (emms-track-get (emms-playlist-track-at) (quote ,attribute)))) (emms-playlist-limit-do (quote ,attribute) regexp))) (define-emms-playlist-limit info-artist) (define-emms-playlist-limit info-composer) (define-emms-playlist-limit info-performer) (define-emms-playlist-limit info-title) (define-emms-playlist-limit info-album) (define-emms-playlist-limit info-year) (define-emms-playlist-limit info-genre) (define-emms-playlist-limit name) (defun emms-playlist-limit-to-all () "Show all tracks again." (interactive) (emms-playlist-limit-do nil nil)) (define-key emms-playlist-mode-map (kbd "/ n") 'emms-playlist-limit-to-name) (define-key emms-playlist-mode-map (kbd "/ a") 'emms-playlist-limit-to-info-artist) (define-key emms-playlist-mode-map (kbd "/ c") 'emms-playlist-limit-to-info-composer) (define-key emms-playlist-mode-map (kbd "/ p") 'emms-playlist-limit-to-info-performer) (define-key emms-playlist-mode-map (kbd "/ t") 'emms-playlist-limit-to-info-title) (define-key emms-playlist-mode-map (kbd "/ b") 'emms-playlist-limit-to-info-album) (define-key emms-playlist-mode-map (kbd "/ y") 'emms-playlist-limit-to-info-year) (define-key emms-playlist-mode-map (kbd "/ g") 'emms-playlist-limit-to-info-genre) (define-key emms-playlist-mode-map (kbd "/ /") 'emms-playlist-limit-to-all) ;;; Low Level Functions (defvar emms-playlist-limit-tracks nil "All tracks in playlist buffer(unlimited).") (defun emms-playlist-limit-insert () "Run in `emms-playlist-source-inserted-hook'." (with-current-emms-playlist (emms-playlist-ensure-playlist-buffer) (setq emms-playlist-limit-tracks (emms-with-widened-buffer (emms-playlist-tracks-in-region (point-min) (point-max)))))) ;; FIXME: When user deletes some tracks, `emms-playlist-limit-tracks' ;; should be updated. ;; (defun emms-playlist-limit-clear () ;; "Run in `emms-playlist-cleared-hook'." ;; (setq emms-playlist-limit-tracks ;; (append emms-playlist-limit-tracks ;; (emms-playlist-tracks-in-region ;; (point-min) (point-max))))) (defun emms-playlist-limit-do (name value) "Limit by NAME with VALUE. e.g., (emms-playlist-limit-do 'info-artist \"Jane Zhang\") When NAME is nil, show all tracks again. See `emms-info-mp3find-arguments' for possible options for NAME." (with-current-emms-playlist (emms-playlist-ensure-playlist-buffer) (let ((curr (emms-playlist-current-selected-track)) (tracks (emms-playlist-tracks-in-region (point-min) (point-max)))) (erase-buffer) (run-hooks 'emms-playlist-cleared-hook) (if name (mapc (lambda (track) (let ((track-value (emms-track-get track name))) (when (and track-value (string-match value track-value)) (emms-playlist-insert-track track)))) tracks) (mapc (lambda (track) (emms-playlist-insert-track track)) emms-playlist-limit-tracks)) (let ((pos (text-property-any (point-min) (point-max) 'emms-track curr))) (if pos (emms-playlist-select pos) (emms-playlist-first))) (run-hooks 'emms-playlist-limit-hook) (emms-playlist-mode-center-current)))) (provide 'emms-playlist-limit) ;;; emms-playlist-limit.el ends here ������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-playlist-mode.el��������������������������������������0000664�0000000�0000000�00000053134�12205151475�0023103�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-playlist-mode.el --- Playlist mode for Emms. ;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Yoni Rabkin <yonirabkin@member.fsf.org> ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;;; ;;; This is a method of displaying and manipulating the different Emms ;;; playlist buffers. ;;; ;;; Emms developer's motto: "When forcer says (require 'jump) we say ;;; (funcall #'jump height)" ;;; Code: ;;; -------------------------------------------------------- ;;; Variables ;;; -------------------------------------------------------- (require 'emms) (condition-case nil (require 'overlay) (error nil)) (require 'emms-source-playlist) (defvar emms-playlist-mode-hook nil "Emms playlist mode hook.") (defvar emms-playlist-mode-selected-overlay nil "Last selected track. Use for updating the display.") (defvar emms-playlist-mode-switched-buffer nil "Last buffer visited before calling `emms-playlist-mode-switch-buffer'.") (defvar emms-playlist-mode-popup-enabled nil "True when the playlist was called as a popup window.") (make-variable-buffer-local 'emms-playlist-mode-selected-overlay) (defgroup emms-playlist-mode nil "*The Emacs Multimedia System playlist mode." :prefix "emms-playlist-mode-" :group 'emms) (defcustom emms-playlist-mode-open-playlists nil "*Determine whether to open playlists in a new EMMS buffer on RET. This is useful if you have a master playlist buffer that is composed of other playlists." :type 'boolean :group 'emms-playlist-mode) (defcustom emms-playlist-mode-window-width 25 "*Determine the width of the Emms popup window. The value should a positive integer." :type 'integer :group 'emms-playlist-mode) (defcustom emms-playlist-mode-center-when-go nil "*Determine whether to center on the currently selected track. This is true for every invocation of `emms-playlist-mode-go'." :type 'boolean :group 'emms-playlist-mode) ;;; -------------------------------------------------------- ;;; Faces ;;; -------------------------------------------------------- (defface emms-playlist-track-face '((((class color) (background dark)) (:foreground "DarkSeaGreen")) (((class color) (background light)) (:foreground "Blue")) (((type tty) (class mono)) (:inverse-video t)) (t (:background "Blue"))) "Face for the tracks in a playlist buffer." :group 'emms-playlist-mode) (defface emms-playlist-selected-face '((((class color) (background dark)) (:foreground "SteelBlue3")) (((class color) (background light)) (:background "blue3" :foreground "white")) (((type tty) (class mono)) (:inverse-video t)) (t (:background "blue3"))) "Face for highlighting the selected track." :group 'emms-playlist-mode) ;;; -------------------------------------------------------- ;;; Keys ;;; -------------------------------------------------------- (defconst emms-playlist-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map text-mode-map) (define-key map (kbd "C-x C-s") 'emms-playlist-save) (define-key map (kbd "C-y") 'emms-playlist-mode-yank) (define-key map (kbd "C-k") 'emms-playlist-mode-kill-track) (define-key map (kbd "C-w") 'emms-playlist-mode-kill) (define-key map (kbd "C-_") 'emms-playlist-mode-undo) (define-key map (kbd "C-/") 'emms-playlist-mode-undo) (define-key map (kbd "C-x u") 'emms-playlist-mode-undo) (define-key map (kbd "C-n") 'next-line) (define-key map (kbd "C-p") 'previous-line) (define-key map (kbd "C-j") 'emms-playlist-mode-insert-newline) (define-key map (kbd "M-y") 'emms-playlist-mode-yank-pop) (define-key map (kbd "M-<") 'emms-playlist-mode-first) (define-key map (kbd "M->") 'emms-playlist-mode-last) (define-key map (kbd "M-n") 'emms-playlist-mode-next) (define-key map (kbd "M-p") 'emms-playlist-mode-previous) (define-key map (kbd "a") 'emms-playlist-mode-add-contents) (define-key map (kbd "b") 'emms-playlist-set-playlist-buffer) (define-key map (kbd "D") 'emms-playlist-mode-kill-entire-track) (define-key map (kbd "n") 'emms-next) (define-key map (kbd "p") 'emms-previous) (define-key map (kbd "SPC") 'scroll-up) (define-key map (kbd ">") 'emms-seek-forward) (define-key map (kbd "<") 'emms-seek-backward) (define-key map (kbd "P") 'emms-pause) (define-key map (kbd "s") 'emms-stop) (define-key map (kbd "f") 'emms-show) (define-key map (kbd "c") 'emms-playlist-mode-center-current) (define-key map (kbd "q") 'emms-playlist-mode-bury-buffer) (define-key map (kbd "k") 'emms-playlist-mode-current-kill) (define-key map (kbd "?") 'describe-mode) (define-key map (kbd "r") 'emms-random) (define-key map (kbd "C") 'emms-playlist-mode-clear) (define-key map (kbd "d") 'emms-playlist-mode-goto-dired-at-point) (define-key map (kbd "<mouse-2>") 'emms-playlist-mode-play-current-track) (define-key map (kbd "RET") 'emms-playlist-mode-play-smart) map) "Keymap for `emms-playlist-mode'.") (defmacro emms-playlist-mode-move-wrapper (name fun) "Create a function NAME which is an `interactive' version of FUN. NAME should be a symbol. FUN should be a function." `(defun ,name () ,(format "Interactive wrapper around `%s' for playlist-mode." fun) (interactive) (,fun))) (emms-playlist-mode-move-wrapper emms-playlist-mode-first emms-playlist-first) (emms-playlist-mode-move-wrapper emms-playlist-mode-select-next emms-playlist-next) (emms-playlist-mode-move-wrapper emms-playlist-mode-select-previous emms-playlist-previous) (defun emms-playlist-mode-bury-buffer () "Wrapper around `bury-buffer' for popup windows." (interactive) (if emms-playlist-mode-popup-enabled (unwind-protect (delete-window) (setq emms-playlist-mode-popup-enabled nil)) (bury-buffer))) (defun emms-playlist-mode-current-kill () "If the current buffer is an EMMS playlist buffer, kill it. Otherwise, kill the current EMMS playlist buffer." (interactive) (if (and emms-playlist-buffer-p (not (eq (current-buffer) emms-playlist-buffer))) (kill-buffer (current-buffer)) (emms-playlist-current-kill))) (defun emms-playlist-mode-clear () "If the current buffer is an EMMS playlist buffer, clear it. Otherwise, clear the current EMMS playlist buffer." (interactive) (if (and emms-playlist-buffer-p (not (eq (current-buffer) emms-playlist-buffer))) (let ((inhibit-read-only t)) (widen) (delete-region (point-min) (point-max))) (emms-playlist-clear))) (defun emms-playlist-mode-last () "Move to directly after the last track in the current buffer." (interactive) (emms-playlist-ensure-playlist-buffer) (let ((last (condition-case nil (save-excursion (goto-char (point-max)) (point)) (error nil)))) (if last (goto-char last) (error "No last track")))) (defun emms-playlist-mode-center-current () "Move point to the currently selected track." (interactive) (goto-char (if emms-playlist-mode-selected-overlay (overlay-start emms-playlist-mode-selected-overlay) (point-min)))) (defun emms-playlist-mode-play-current-track () "Start playing track at point." (interactive) (emms-playlist-set-playlist-buffer (current-buffer)) (unless (emms-playlist-track-at (point)) (emms-playlist-next)) (emms-playlist-select (point)) (when emms-player-playing-p (emms-stop)) (emms-start)) (defun emms-playlist-mode-play-smart () "Determine the best operation to take on the current track. If on a playlist, and `emms-playlist-mode-open-playlists' is non-nil, load the playlist at point into a new buffer. Otherwise play the track immediately." (interactive) (save-excursion ;; move to the start of the line, in case the point is on the \n, ;; which isn't propertized (emms-move-beginning-of-line nil) (if (not emms-playlist-mode-open-playlists) (emms-playlist-mode-play-current-track) (unless (emms-playlist-track-at) (emms-playlist-next)) (let* ((track (emms-playlist-track-at)) (name (emms-track-get track 'name)) (type (emms-track-get track 'type))) (if (or (eq type 'playlist) (and (eq type 'file) (string-match "\\.\\(m3u\\|pls\\)\\'" name))) (emms-playlist-mode-load-playlist) (emms-playlist-mode-play-current-track)))))) (defun emms-playlist-mode-switch-buffer () "Switch to the playlist buffer and then switch back if called again. This function switches to the current Emms playlist buffer and remembers the buffer switched from. When called again the function switches back to the remembered buffer." (interactive) (if (eq (current-buffer) emms-playlist-buffer) (switch-to-buffer emms-playlist-mode-switched-buffer) (setq emms-playlist-mode-switched-buffer (current-buffer)) (switch-to-buffer emms-playlist-buffer))) (defun emms-playlist-mode-insert-newline () "Insert a newline at point." (interactive) (emms-with-inhibit-read-only-t (newline))) (defun emms-playlist-mode-undo () "Wrapper around `undo'." (interactive) (emms-with-inhibit-read-only-t (undo))) (defun emms-playlist-mode-add-contents () "Add files in the playlist at point to the current playlist buffer. If we are in the current playlist, make a new playlist buffer and set it as current." (interactive) (save-excursion (emms-move-beginning-of-line nil) (unless (emms-playlist-track-at) (emms-playlist-next)) (let* ((track (emms-playlist-track-at)) (name (emms-track-get track 'name)) (type (emms-track-get track 'type)) (playlist-p (or (eq type 'playlist) (and (eq type 'file) (save-match-data (string-match "\\.\\(m3u\\|pls\\)\\'" name)))))) (emms-playlist-select (point)) (unless (and (buffer-live-p emms-playlist-buffer) (not (eq (current-buffer) emms-playlist-buffer))) (setq emms-playlist-buffer (emms-playlist-set-playlist-buffer (emms-playlist-new)))) (with-current-emms-playlist (goto-char (point-max)) (when playlist-p (insert (emms-track-force-description track) "\n")) (let ((beg (point))) (if playlist-p (emms-add-playlist name) (let ((func (intern (concat "emms-add-" (symbol-name type))))) (if (functionp func) (funcall func name) ;; fallback (emms-add-file name)))) (when playlist-p (goto-char (point-max)) (while (progn (forward-line -1) (>= (point) beg)) (insert " "))) (goto-char (point-min)) (message "Added %s" (symbol-name type))))))) (defun emms-playlist-mode-goto-dired-at-point () "Visit the track at point in a `dired' buffer." (interactive) (let ((track (emms-playlist-track-at))) (if track (let ((name (emms-track-get track 'name)) (type (emms-track-get track 'type))) (if (eq type 'file) (dired (file-name-directory name)) (error "Can't visit this track type in Dired"))) (error "No track at point")))) ;;; -------------------------------------------------------- ;;; Killing and yanking ;;; -------------------------------------------------------- (defun emms-playlist-mode-between-p (p a b) "Return t if P is a point between points A and B." (and (<= a p) (<= p b))) ;; d (defun emms-playlist-mode-kill-entire-track () "Kill track at point, including newline." (interactive) (let ((kill-whole-line t)) (emms-playlist-mode-kill-track))) ;; C-k ;; ;; Currently this kills as regular GNU/Emacs would and not like a ;; typical music player would. (defun emms-playlist-mode-kill-track () "Kill track at point." (interactive) (emms-with-inhibit-read-only-t (let ((track (emms-playlist-track-at))) (if track (let ((track-region (emms-property-region (point) 'emms-track))) (when (and emms-player-playing-p (emms-playlist-selected-track-at-p)) (emms-stop) (delete-overlay emms-playlist-mode-selected-overlay) (setq emms-playlist-mode-selected-overlay nil)) (kill-line)) (kill-line))))) ;; C-w (defun emms-playlist-mode-kill () "Kill from mark to point." (interactive) (emms-with-inhibit-read-only-t ;; Are we killing the playing/selected track? (when (and (markerp emms-playlist-selected-marker) (emms-playlist-mode-between-p (marker-position emms-playlist-selected-marker) (region-beginning) (region-end))) (emms-stop) (delete-overlay emms-playlist-mode-selected-overlay) (setq emms-playlist-mode-selected-overlay nil)) (kill-region (region-beginning) (region-end)))) (defun emms-playlist-mode-correct-previous-yank () "Fix the previous yank if needed." (when (and (< (point-at-bol) (point)) (< (point) (point-at-eol))) (newline))) ;; C-y (defun emms-playlist-mode-yank () "Yank into the playlist buffer." (interactive) (emms-with-inhibit-read-only-t (goto-char (point-at-bol)) (yank) (emms-playlist-mode-correct-previous-yank))) ;; M-y (defun emms-playlist-mode-yank-pop () "Cycle through the kill-ring." (interactive) (emms-with-inhibit-read-only-t (yank-pop nil) (emms-playlist-mode-correct-previous-yank))) ;;; -------------------------------------------------------- ;;; Overlay ;;; -------------------------------------------------------- (defun emms-playlist-mode-overlay-selected () "Place an overlay over the currently selected track." (when emms-playlist-selected-marker (save-excursion (goto-char emms-playlist-selected-marker) (let ((reg (emms-property-region (point) 'emms-track))) (if emms-playlist-mode-selected-overlay (move-overlay emms-playlist-mode-selected-overlay (car reg) (cdr reg)) (setq emms-playlist-mode-selected-overlay (make-overlay (car reg) (cdr reg) nil t nil)) (overlay-put emms-playlist-mode-selected-overlay 'face 'emms-playlist-selected-face) (overlay-put emms-playlist-mode-selected-overlay 'evaporate t)))))) ;;; -------------------------------------------------------- ;;; Saving/Restoring ;;; -------------------------------------------------------- (defun emms-playlist-mode-open-buffer (filename) "Opens a previously saved playlist buffer. It creates a buffer called \"filename\", and restores the contents of the saved playlist inside." (interactive "fFile: ") (let* ((s) (buffer (get-buffer-create filename)) (name (buffer-name buffer))) (with-current-buffer buffer (emms-insert-file-contents filename) (setq s (read (buffer-string)))) (kill-buffer buffer) (with-current-buffer (emms-playlist-new name) (emms-with-inhibit-read-only-t (insert s) (goto-char (point-min)) (emms-walk-tracks (emms-playlist-update-track))) (emms-playlist-first) (emms-playlist-select (point)) (switch-to-buffer (current-buffer))))) (defun emms-playlist-mode-load-playlist () "Load the playlist into a new EMMS buffer. This preserves the current EMMS buffer." (interactive) (let* ((track (emms-playlist-track-at)) (name (emms-track-get track 'name)) (type (emms-track-get track 'type))) (emms-playlist-select (point)) (run-hooks 'emms-player-stopped-hook) (switch-to-buffer (emms-playlist-set-playlist-buffer (emms-playlist-new))) (emms-add-playlist name))) ;;; -------------------------------------------------------- ;;; Local functions ;;; -------------------------------------------------------- (defun emms-playlist-mode-insert-track (track &optional no-newline) "Insert the description of TRACK at point. When NO-NEWLINE is non-nil, do not insert a newline after the track." (emms-playlist-ensure-playlist-buffer) (emms-with-inhibit-read-only-t (insert (emms-propertize (emms-track-force-description track) 'emms-track track 'face 'emms-playlist-track-face)) (when (emms-playlist-selected-track-at-p) (emms-playlist-mode-overlay-selected)) (unless no-newline (insert "\n")))) (defun emms-playlist-mode-update-track-function () "Update the track display at point." (emms-playlist-ensure-playlist-buffer) (emms-with-inhibit-read-only-t (let ((track-region (emms-property-region (point) 'emms-track)) (track (get-text-property (point) 'emms-track)) (selectedp (emms-playlist-selected-track-at-p))) (save-excursion (delete-region (car track-region) (cdr track-region)) (when selectedp (delete-overlay emms-playlist-mode-selected-overlay) (setq emms-playlist-mode-selected-overlay nil)) (emms-playlist-mode-insert-track track t)) (when selectedp (emms-playlist-select (point)))))) ;;; -------------------------------------------------------- ;;; Entry ;;; -------------------------------------------------------- (defun emms-playlist-mode-go () "Switch to the current emms-playlist buffer and use emms-playlist-mode." (interactive) (if (or (null emms-playlist-buffer) (not (buffer-live-p emms-playlist-buffer))) (error "No current Emms buffer") (switch-to-buffer emms-playlist-buffer) (when (and (not (eq major-mode 'emms-playlist-mode)) emms-playlist-buffer-p) (emms-playlist-mode)) (when emms-playlist-mode-center-when-go (emms-playlist-mode-center-current)))) (defun emms () "Switch to the current emms-playlist buffer, use emms-playlist-mode and query for a directory tree to add to the playlist." (interactive) (if (or (null emms-playlist-buffer) (not (buffer-live-p emms-playlist-buffer))) (call-interactively 'emms-add-file)) (emms-playlist-mode-go)) (defun emms-playlist-mode-go-popup (&optional window-width) "Popup emms-playlist buffer as a side window. Default value for WINDOW-WIDTH is `emms-playlist-mode-window-width'. WINDOW-WIDTH should be a positive integer." (interactive) (setq emms-playlist-mode-window-width (round (or window-width emms-playlist-mode-window-width))) (split-window-horizontally (- emms-playlist-mode-window-width)) (other-window 1) (emms-playlist-mode-go) (setq emms-playlist-mode-popup-enabled t)) (defun emms-playlist-mode-next (arg) "Navigate between playlists." (interactive "p") (let ((playlists (emms-playlist-buffer-list)) bufs idx) (if playlists ;; if not in playlist mode, switch to emms-playlist-buffer (if (not (member (current-buffer) playlists)) (switch-to-buffer (if (and emms-playlist-buffer (buffer-live-p emms-playlist-buffer)) emms-playlist-buffer (car playlists))) (setq bufs (member (current-buffer) playlists)) (setq idx (+ (- (length playlists) (length bufs)) (if (> arg 0) 1 -1))) (switch-to-buffer (nth (mod idx (length playlists)) playlists))) (message "No playlist found!")))) (defun emms-playlist-mode-previous (arg) (interactive "p") (emms-playlist-mode-next (- arg))) (defun emms-playlist-mode-startup () "Instigate emms-playlist-mode on the current buffer." ;; when there is neither a current emms track or a playing one... (when (not (or emms-playlist-selected-marker emms-player-playing-p)) ;; ...then stop the player. (emms-stop) ;; why select the first track? (when emms-playlist-buffer-p (emms-playlist-select-first))) ;; when there is a selected track. (when emms-playlist-selected-marker (emms-playlist-mode-overlay-selected)) (emms-with-inhibit-read-only-t (add-text-properties (point-min) (point-max) '(face emms-playlist-track-face))) (setq buffer-read-only t) (setq truncate-lines t) (setq buffer-undo-list nil)) ;;;###autoload (defun emms-playlist-mode () "A major mode for Emms playlists. \\{emms-playlist-mode-map}" (interactive) (let ((val emms-playlist-buffer-p)) (kill-all-local-variables) (setq emms-playlist-buffer-p val)) (use-local-map emms-playlist-mode-map) (setq major-mode 'emms-playlist-mode mode-name "EMMS") (setq emms-playlist-insert-track-function 'emms-playlist-mode-insert-track) (setq emms-playlist-update-track-function 'emms-playlist-mode-update-track-function) (add-hook 'emms-playlist-selection-changed-hook 'emms-playlist-mode-overlay-selected) (emms-playlist-mode-startup) (run-hooks 'emms-playlist-mode-hook)) (provide 'emms-playlist-mode) ;;; emms-playlist-mode.el ends here ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-playlist-sort.el��������������������������������������0000664�0000000�0000000�00000017552�12205151475�0023152�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-playlist-sort.el --- sort emms playlist ;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: William Xu <william.xwl@gmail.com> ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ;;; Code: (eval-when-compile (require 'cl)) (require 'emms-last-played) (require 'emms-playlist-mode) ;;; User Customizations (defgroup emms-playlist-sort nil "Sorting Emacs Multimedia System playlists." :prefix "emms-playlist-sort-" :group 'emms) (defcustom emms-playlist-sort-list '(info-artist info-album) "Sorting list used by `emms-playlist-sort-by-list'. Currently it understands the following fields: name info-artist imfo-composer info-performer info-title info-album info-genre info-playing-time info-tracknumber info-discnumber." :type 'symbol :group 'emms-playlist-sort) (defcustom emms-playlist-sort-prefix "S" "Prefix key sequence for `emms-playlist-sort-map'. Remember to call `emms-playlist-sort-map-setup' if you modify it." :type 'string :group 'emms-playlist-sort) ;;; User Interfaces (defmacro define-emms-playlist-sort (attribute) "Macro for defining emms playlist sort functions on strings ." `(defun ,(intern (format "emms-playlist-sort-by-%s" attribute)) () ,(format "Sort emms playlist by %s, increasingly. With a prefix argument, decreasingly." attribute) (interactive) (emms-playlist-sort '(lambda (a b) (funcall (if current-prefix-arg 'emms-string> 'emms-string<) (emms-track-get a (quote ,attribute)) (emms-track-get b (quote ,attribute))))))) (define-emms-playlist-sort name) (define-emms-playlist-sort info-artist) (define-emms-playlist-sort info-composer) (define-emms-playlist-sort info-performer) (define-emms-playlist-sort info-title) (define-emms-playlist-sort info-album) (define-emms-playlist-sort info-year) (define-emms-playlist-sort info-note) (defun emms-playlist-sort-by-natural-order () "Sort emms playlist by natural order. See `emms-sort-natural-order-less-p'." (interactive) (emms-playlist-sort 'emms-sort-natural-order-less-p)) (defun emms-playlist-sort-by-list () "Sort emms playlist by `emms-playlist-sort-list'. The sort will be carried out until comparsion succeeds, increasingly." (interactive) (emms-playlist-sort 'emms-playlist-sort-by-list-p)) (defun emms-playlist-sort-by-last-played () "Sort emms playlist by last played time, increasingly. With a prefix argument, decreasingly." (interactive) (emms-playlist-sort '(lambda (a b) (funcall (if current-prefix-arg 'not 'identity) (time-less-p (or (emms-track-get a 'last-played) '(0 0 0)) (or (emms-track-get b 'last-played) '(0 0 0))))))) (defun emms-playlist-sort-by-play-count () "Sort emms playlist by play-count, increasingly. With a prefix argument, decreasingly." (interactive) (emms-playlist-sort '(lambda (a b) (funcall (if current-prefix-arg 'not 'identity) (< (or (emms-track-get a 'play-count) 0) (or (emms-track-get b 'play-count) 0)))))) (defun emms-playlist-sort-by-file-extension () "Sort emms playlist by file extension, increasingly. With a prefix argument, decreasingly." (interactive) (emms-playlist-sort '(lambda (a b) (funcall (if current-prefix-arg 'emms-string> 'emms-string<) (file-name-extension (emms-track-get a 'name)) (file-name-extension (emms-track-get b 'name)))))) (defvar emms-playlist-sort-map nil) (defun emms-playlist-sort-map-setup () "Setup sort map with latest `emms-playlist-sort-prefix'." (setq emms-playlist-sort-map (let ((map (make-sparse-keymap))) (define-key map (kbd "n") 'emms-playlist-sort-by-natural-order) (define-key map (kbd "a") 'emms-playlist-sort-by-info-artist) (define-key map (kbd "c") 'emms-playlist-sort-by-play-count) (define-key map (kbd "b") 'emms-playlist-sort-by-info-album) (define-key map (kbd "l") 'emms-playlist-sort-by-last-played) (define-key map (kbd "t") 'emms-playlist-sort-by-info-title) (define-key map (kbd "e") 'emms-playlist-sort-by-file-extension) (define-key map (kbd "p") 'emms-playlist-sort-by-info-performer) (define-key map (kbd "y") 'emms-playlist-sort-by-info-year) (define-key map (kbd "o") 'emms-playlist-sort-by-info-note) (define-key map (kbd "C") 'emms-playlist-sort-by-info-composer) (define-key map (kbd "L") 'emms-playlist-sort-by-list) (define-key map (kbd "N") 'emms-playlist-sort-by-name) map)) (define-key emms-playlist-mode-map emms-playlist-sort-prefix emms-playlist-sort-map)) (setq emms-playlist-sort-map (emms-playlist-sort-map-setup)) ;;; Low Level Functions (defun emms-playlist-sort (predicate) "Sort the playlist buffer by PREDICATE." (with-current-emms-playlist (emms-playlist-ensure-playlist-buffer) (let ((current (emms-playlist-selected-track)) (tracks (nreverse (emms-playlist-tracks-in-region (point-min) (point-max))))) (delete-region (point-min) (point-max)) (run-hooks 'emms-playlist-cleared-hook) (mapc 'emms-playlist-insert-track (sort tracks predicate)) (let ((pos (text-property-any (point-min) (point-max) 'emms-track current))) (if pos (emms-playlist-select pos) (emms-playlist-first)) ;; (emms-playlist-mode-center-current) (goto-char (point-min)) )))) (defun emms-sort-natural-order-less-p (a b) "Sort two tracks by natural order. This is the order in which albums where intended to be played. ie. by album name and then by track number." (let ((album-a (emms-track-get a 'info-album)) (album-b (emms-track-get b 'info-album)) (discnum-a (string-to-number (or (emms-track-get a 'info-discnumber) "0"))) (discnum-b (string-to-number (or (emms-track-get b 'info-discnumber) "0"))) (tracknum-a (string-to-number (or (emms-track-get a 'info-tracknumber) "0"))) (tracknum-b (string-to-number (or (emms-track-get b 'info-tracknumber) "0")))) (or (emms-string< album-a album-b) (and album-a album-b (string= album-a album-b) (or (< discnum-a discnum-b) (and (= discnum-a discnum-b) (< tracknum-a tracknum-b))))))) (defun emms-playlist-sort-by-list-p (a b) (catch 'return (dolist (info emms-playlist-sort-list) (case info ((name info-artist info-composer info-performer info-title info-album info-genre) (when (emms-string< (emms-track-get a info) (emms-track-get b info)) (throw 'return t))) ((info-playing-time) (when (< (emms-track-get a info) (emms-track-get b info)) (throw 'return t))) ((info-tracknumber info-discnumber) (when (< (string-to-number (or (emms-track-get a info) "0")) (string-to-number (or (emms-track-get b info) "0"))) (throw 'return t))))))) (defun emms-string< (s1 s2) (string< (downcase (or s1 "")) (downcase (or s2 "")))) (defun emms-string> (s1 s2) (let ((a (downcase (or s1 ""))) (b (downcase (or s2 "")))) (not (or (string= a b) (string< a b))))) (provide 'emms-playlist-sort) ;;; emms-playlist-sort.el ends here ������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-score.el����������������������������������������������0000664�0000000�0000000�00000022210�12205151475�0021422�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-score.el --- Scoring system for mp3player ;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, ;; 2009 Free Software Foundation, Inc. ;; Authors: Jean-Philippe Theberge <jphiltheberge@videotron.ca>, ;; Yoni Rabkin <yonirabkin@member.fsf.org> ;; ;; Keywords: emms, mp3, mpeg, multimedia ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; NOTE: This is experimental stuff - comments welcome! There ;; shouldn't worky anything in that file... scores aren't saved, they ;; even don't have any consequence on playing order and there's just ;; one mood in the moment. But it's a beginning and you can score down ;; or up tracks... :) ;; ;; * How to use scoring in emms ;; ;; When you load emms, you are set to a default mood ;; 'emms-default-mood' A mood is a one word string describing how ;; you feel (like "funny", "tired", "aggresive"...) Each mood have is ;; own set of scoring rules. ;; ;; You can change your mood with M-x emms-score-change-mood. ;; ;; Every music file start with a default score of 0 the command ;; emms-score-up-playing and emms-score-down-playing modify the ;; score of the file you are curently listening by 1 In addition, ;; skipping a file (with emms-skip) automaticaly score the file ;; down. ;; ;; With scoring on (this mean the variable emms-use-scoring is t), ;; emms will compare the score of the file with your tolerance to ;; decide if it is played or not. ;; ;; The default tolerance level is 0 (or the variable ;; emms-score-min-score). This mean files with a score of 0 or more will ;; be played and files with a score of -1 or less will be skipped. ;; ;; You can change the tolerance (by 1) with M-x ;; emms-score-lower-tolerance and M-x ;; emms-score-be-more-tolerant ;;; Code: (require 'emms) (defvar emms-scores-list nil) (defvar emms-score-current-mood 'default) (defvar emms-score-min-score 0) (defvar emms-score-default-score 0) (defvar emms-score-hash (make-hash-table :test 'equal)) (defvar emms-score-enabled-p nil "If non-nil, emms score is active.") (defcustom emms-score-file (concat (file-name-as-directory emms-directory) "scores") "*Directory to store the score file." :type 'directory :group 'emms) ;;; User Interfaces (defun emms-score (arg) "Turn on emms-score if prefix argument ARG is a positive integer, off otherwise." (interactive "p") (if (and arg (> arg 0)) (progn (setq emms-score-enabled-p t) (setq emms-player-next-function 'emms-score-next-noerror) (emms-score-load-hash) (unless noninteractive (add-hook 'kill-emacs-hook 'emms-score-save-hash))) (setq emms-score-enabled-p nil) (setq emms-player-next-function 'emms-next-noerror) (emms-score-save-hash) (remove-hook 'kill-emacs-hook 'emms-score-save-hash))) ;;;###autoload (defun emms-score-enable () "Turn on emms-score." (interactive) (emms-score 1) (message "emms score enabled")) ;;;###autoload (defun emms-score-disable () "Turn off emms-score." (interactive) (emms-score -1) (message "emms score disabled")) ;;;###autoload (defun emms-score-toggle () "Toggle emms-score." (interactive) (if emms-score-enabled-p (emms-score-disable) (emms-score-enable))) (defun emms-score-change-mood (mood) "Change the current MOOD. The score hash is automatically saved." (interactive "sMood: ") (emms-score-save-hash) (setq emms-score-current-mood (intern (downcase mood)))) (defun emms-score-up-playing () (interactive) (if emms-player-playing-p (emms-score-change-score 1 (emms-score-current-selected-track-filename)) (error "No track currently playing"))) (defun emms-score-down-playing () (interactive) (if emms-player-playing-p (emms-score-change-score -1 (emms-score-current-selected-track-filename)) (error "No track currently playing"))) (defun emms-score-up-file-on-line () (interactive) (emms-score-change-score 1 (emms-score-track-at-filename))) (defun emms-score-down-file-on-line () (interactive) (emms-score-change-score -1 (emms-score-track-at-filename))) (defun emms-score-less-tolerant () "Only play mp3 with a higher score" (interactive) (setq emms-score-min-score (+ emms-score-min-score 1)) (message "Will play songs with a score >= %d" emms-score-min-score)) (defun emms-score-more-tolerant () "Allow playing of mp3 with a lower score." (interactive) (setq emms-score-min-score (- emms-score-min-score 1)) (message "Will play songs with a score >= %d" emms-score-min-score)) (defun emms-score-set-playing (score) "Set score for current playing track." (interactive "nSet score for playing track: ") (let ((filename (emms-score-current-selected-track-filename))) (if emms-player-playing-p (emms-score-change-score (- score (emms-score-get-score filename)) filename) (error "No track currently playing")))) (defun emms-score-set-file-on-line (score) "Set score for track at point in emms-playlist buffer." (interactive "nSet score for track at point: ") (let ((filename (emms-score-track-at-filename))) (if emms-player-playing-p (emms-score-change-score (- score (emms-score-get-score filename)) filename)))) (defun emms-score-set-tolerance (tolerance) "Allow playing tracks with a score >= tolerance." (interactive "nSet tolerance: ") (setq emms-score-min-score tolerance) (message "Will play songs with a score >= %d" emms-score-min-score)) (defun emms-score-show-playing () "Show score for current playing track in minibuf." (interactive) (message "track/tolerance score: %d/%d" (emms-score-get-score (emms-score-current-selected-track-filename)) emms-score-min-score)) (defun emms-score-show-file-on-line () "Show score for track at point in emms-playlist buffer." (interactive) (message "track/tolerance score: %d/%d" (emms-score-get-score (emms-score-track-at-filename)) emms-score-min-score)) ;;; Internal Functions (defun emms-score-current-selected-track-filename () "Return filename of current selected track." (emms-track-get (emms-playlist-current-selected-track) 'name)) (defun emms-score-track-at-filename () "Return file of track at point in emms-playlist buffer." (emms-track-get (emms-playlist-track-at) 'name)) (defun emms-score-next-noerror () "Run `emms-next-noerror' with score check. See also `emms-next-noerror'." (interactive) (when emms-player-playing-p (error "A track is already being played")) (cond (emms-repeat-track (emms-start)) ((condition-case nil (progn (emms-playlist-current-select-next) t) (error nil)) (if (emms-score-check-score (emms-score-current-selected-track-filename)) (emms-start) (emms-score-next-noerror))) (t (message "No next track in playlist")))) (defun emms-score-save-hash () "Save score hash in `emms-score-file'." (interactive) (unless (file-directory-p (file-name-directory emms-score-file)) (make-directory (file-name-directory emms-score-file))) (with-temp-file emms-score-file (let ((standard-output (current-buffer))) (insert "(") (maphash (lambda (key value) (prin1 (cons key value))) emms-score-hash) (insert ")")))) (defun emms-score-load-hash () "Load score hash from `emms-score-file'." (interactive) (if (file-exists-p emms-score-file) (let ((score-string (with-temp-buffer (emms-insert-file-contents emms-score-file) (buffer-string)))) (if (> (length score-string) 0) (mapc (lambda (elt) (puthash (car elt) (cdr elt) emms-score-hash)) (read score-string))) ;; when file not exists, make empty but valid score file (emms-score-save-hash)))) (defun emms-score-get-plist (filename) (gethash filename emms-score-hash)) (defun emms-score-change-score (score filename) (let ((sp (emms-score-get-plist filename) ) (sc (emms-score-get-score filename))) (puthash filename (plist-put sp emms-score-current-mood (+ sc score)) emms-score-hash) (message "New score is %s" (+ score sc)))) (defun emms-score-create-entry (filename) (puthash filename `(,emms-score-current-mood ,emms-score-default-score) emms-score-hash)) (defun emms-score-get-score (filename) "Return score of TRACK." (let ((plist (emms-score-get-plist filename))) (if (member emms-score-current-mood plist) (plist-get plist emms-score-current-mood) (emms-score-create-entry filename) (emms-score-get-score filename)))) (defun emms-score-check-score (filename) (>= (emms-score-get-score filename) emms-score-min-score)) (provide 'emms-score) ;;; emms-scores.el ends here ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-setup.el����������������������������������������������0000664�0000000�0000000�00000011271�12205151475�0021454�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-setup.el --- Setup script for EMMS ;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Yoni Rabkin <yonirabkin@member.fsf.org> ;; Keywords: emms setup multimedia ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; This file provides the `emms-setup' feature. With `emms-setup' we ;; can setup Emms with different features enabled. The use of this ;; feature is documented in the Emms manual which is distributed with ;; Emms. ;; ;; The use this feature we can invoke (for example): ;; ;; (require 'emms-setup) ;; (emms-all) ;; ;; The first command loads the feature into Emacs and the second ;; chooses the `emms-all' level. ;;; Code: (require 'emms) (defgroup emms-setup nil "*The Emacs Multimedia System setup utility." :prefix "emms-setup" :group 'multimedia) (defcustom emms-setup-default-player-list '(emms-player-mpg321 emms-player-ogg123 emms-player-mplayer-playlist emms-player-mplayer emms-player-vlc) "*Default list of players for emms-setup." :group 'emms-setup :type 'list) ;;;###autoload (defun emms-minimalistic () "An Emms setup script. Invisible playlists and all the basics for playing media." (require 'emms-source-file) (require 'emms-source-playlist) (require 'emms-player-simple) (require 'emms-player-mplayer) (require 'emms-player-vlc)) ;;;###autoload (defun emms-standard () "An Emms setup script. Everything included in the `emms-minimalistic' setup, the Emms interactive playlist mode, reading information from tagged audio files, and a metadata cache." ;; include (emms-minimalistic) ;; define (eval-and-compile (require 'emms-playlist-mode) (require 'emms-info) (require 'emms-info-mp3info) (require 'emms-info-ogginfo) (require 'emms-cache)) ;; setup (setq emms-playlist-default-major-mode 'emms-playlist-mode) (add-to-list 'emms-track-initialize-functions 'emms-info-initialize-track) (when (executable-find emms-info-mp3info-program-name) (add-to-list 'emms-info-functions 'emms-info-mp3info)) (when (executable-find emms-info-ogginfo-program-name) (add-to-list 'emms-info-functions 'emms-info-ogginfo)) (setq emms-track-description-function 'emms-info-track-description) (when (fboundp 'emms-cache) ; work around compiler warning (emms-cache 1))) ;;;###autoload (defun emms-all () "An Emms setup script. Everything included in the `emms-standard' setup and adds all the stable features which come with the Emms distribution." ;; include (emms-standard) ;; define (eval-and-compile (require 'emms-mode-line) (require 'emms-mark) (require 'emms-tag-editor) (require 'emms-streams) (require 'emms-lyrics) (require 'emms-playing-time) (require 'emms-player-mpd) (require 'emms-player-xine) (require 'emms-playlist-sort) (require 'emms-browser) (require 'emms-mode-line-icon) (require 'emms-cue) (require 'emms-lastfm-client) (require 'emms-bookmarks) (require 'emms-last-played)) ;; setup (emms-mode-line 1) (emms-mode-line-blank) (emms-lyrics 1) (emms-playing-time 1) (add-to-list 'emms-info-functions 'emms-info-cueinfo) (add-hook 'emms-player-started-hook 'emms-last-played-update-current)) ;;;###autoload (defun emms-devel () "An Emms setup script. Everything included in the `emms-all' setup and adds all the features which come with the Emms distribution regardless of if they are considered stable or not. Use this if you like living on the edge." ;; include (emms-all) ;; define (eval-and-compile (require 'emms-metaplaylist-mode) (require 'emms-stream-info) (require 'emms-score) (require 'emms-history) (require 'emms-i18n) (require 'emms-volume) (require 'emms-playlist-limit)) ;; setup (emms-score 1) (emms-playlist-limit 1)) ;;;###autoload (defun emms-default-players () "Set `emms-player-list' to `emms-setup-default-player-list'." (setq emms-player-list emms-setup-default-player-list)) (provide 'emms-setup) ;;; emms-setup.el ends here ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-source-file.el����������������������������������������0000664�0000000�0000000�00000026630�12205151475�0022536�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-source-file.el --- EMMS sources from the filesystem. ;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, ;; 2009 Free Software Foundation, Inc. ;; Author: Jorgen Schäfer <forcer@forcix.cx> ;; Keywords: emms, mp3, mpeg, multimedia ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; This file contains a track source for EMMS that is based on the ;; file system. You can retrieve single files or whole directories. ;; Also, this file offers the commands to play from these sources. ;;; Code: ;; Version control (defvar emms-source-file-version "0.2 $Revision: 1.30 $" "emms-source-file.el version string") ;; $Id: emms-source-file.el,v 1.30 2005/08/11 06:16:15 yonirabkin Exp $ ;;; User Customization (require 'emms) (eval-when-compile (condition-case nil (require 'locate) (error nil))) (require 'dired) (defgroup emms-source-file nil "*Sources for EMMS that use the file system." :prefix "emms-source-file-" :group 'emms-source) (defcustom emms-source-file-default-directory nil "*The default directory to look for media files." :type 'string :group 'emms-source-file) (defcustom emms-source-file-directory-tree-function 'emms-source-file-directory-tree-internal "*A function to call that searches in a given directory all files that match a given regex. DIR and REGEX are the only arguments passed to this function. You have two build-in options: `emms-source-file-directory-tree-internal' will work always, but might be slow. `emms-source-file-directory-tree-find' will work only if you have GNU find, but it's faster." :type 'function :options '(emms-source-file-directory-tree-internal emms-source-file-directory-tree-find) :group 'emms-source-file) (defcustom emms-source-file-exclude-regexp (concat "\\`\\(#.*#\\|.*,v\\|.*~\\|\\.\\.?\\|\\.#.*\\|,.*\\)\\'\\|" "/\\(CVS\\|RCS\\|\\.arch-ids\\|{arch}\\|,.*\\|\\.svn\\|" "_darcs\\)\\(/\\|\\'\\)") "A regexp matching files to be ignored when adding directories. You should set case-fold-search to nil before using this regexp in code." :type 'regexp :group 'emms-source-file) (defcustom emms-source-file-gnu-find "find" "*The program name for GNU find." :type 'string :group 'emms-source-file) ;; The `read-directory-name' function is not available in Emacs 21. (defalias 'emms-read-directory-name (if (fboundp 'read-directory-name) #'read-directory-name #'read-file-name)) ;;; Sources ;;;###autoload (autoload 'emms-play-file "emms-source-file" nil t) ;;;###autoload (autoload 'emms-add-file "emms-source-file" nil t) (define-emms-source file (file) "An EMMS source for a single file - either FILE, or queried from the user." (interactive (list (read-file-name "Play file: " emms-source-file-default-directory emms-source-file-default-directory t))) (if (file-directory-p file) (emms-source-directory file) (emms-playlist-insert-track (emms-track 'file (expand-file-name file))))) ;;;###autoload (autoload 'emms-play-directory "emms-source-file" nil t) ;;;###autoload (autoload 'emms-add-directory "emms-source-file" nil t) (define-emms-source directory (dir) "An EMMS source for a whole directory tree - either DIR, or queried from the user." (interactive (list (emms-read-directory-name "Play directory: " emms-source-file-default-directory emms-source-file-default-directory t))) (mapc (lambda (file) (unless (or (let ((case-fold-search nil)) (string-match emms-source-file-exclude-regexp file)) (file-directory-p file)) (emms-playlist-insert-track (emms-track 'file (expand-file-name file))))) (directory-files dir t (emms-source-file-regex)))) ;;;###autoload (autoload 'emms-play-directory-tree "emms-source-file" nil t) ;;;###autoload (autoload 'emms-add-directory-tree "emms-source-file" nil t) (define-emms-source directory-tree (dir) "An EMMS source for multiple directory trees - either DIR, or the value of `emms-source-file-default-directory'." (interactive (list (emms-read-directory-name "Play directory tree: " emms-source-file-default-directory emms-source-file-default-directory t))) (let ((files (emms-source-file-directory-tree (expand-file-name dir) (emms-source-file-regex))) (case-fold-search nil)) (emms-playlist-ensure-playlist-buffer) (mapc (lambda (file) (unless (string-match emms-source-file-exclude-regexp file) (funcall emms-playlist-insert-track-function (emms-track 'file file)))) files))) ;;;###autoload (autoload 'emms-play-find "emms-source-file" nil t) ;;;###autoload (autoload 'emms-add-find "emms-source-file" nil t) (define-emms-source find (dir regex) "An EMMS source that will find files in DIR or `emms-source-file-default-directory' that match REGEX." (interactive (list (emms-read-directory-name "Find in directory: " emms-source-file-default-directory emms-source-file-default-directory t) (read-from-minibuffer "Find files matching: "))) (mapc (lambda (file) (unless (let ((case-fold-search nil)) (string-match emms-source-file-exclude-regexp file)) (emms-playlist-insert-track (emms-track 'file file)))) (emms-source-file-directory-tree (expand-file-name dir) regex))) ;;;###autoload (autoload 'emms-play-dired "emms-source-file" nil t) ;;;###autoload (autoload 'emms-add-dired "emms-source-file" nil t) (define-emms-source dired () "Return all marked files of a dired buffer" (interactive) (mapc (lambda (file) (if (file-directory-p file) (emms-source-directory-tree file) (emms-source-file file))) (with-current-buffer emms-source-old-buffer (dired-get-marked-files)))) ;;; Helper functions ;;;###autoload (defun emms-source-file-directory-tree (dir regex) "Return a list of all files under DIR that match REGEX. This function uses `emms-source-file-directory-tree-function'." (message "Building playlist...") (let ((pl (sort (funcall emms-source-file-directory-tree-function dir regex) 'string<))) (message "Building playlist...done") pl)) (defun emms-source-file-directory-tree-internal (dir regex) "Return a list of all files under DIR that match REGEX. This function uses only emacs functions, so it might be a bit slow." (let ((files '()) (dirs (list dir))) (while dirs (cond ((file-directory-p (car dirs)) (if (or (string-match "/\\.\\.?$" (car dirs)) (let ((symlink (file-symlink-p (car dirs)))) (and symlink (string-equal dir (substring symlink 0 (string-width dir)))))) (setq dirs (cdr dirs)) (setq dirs (condition-case nil (append (cdr dirs) (directory-files (car dirs) t nil t)) (error (cdr dirs)))))) ((string-match regex (car dirs)) (setq files (cons (car dirs) files) dirs (cdr dirs))) (t (setq dirs (cdr dirs))))) files)) (defun emms-source-file-directory-tree-find (dir regex) "Return a list of all files under DIR that match REGEX. This function uses the external find utility. The name for GNU find may be supplied using `emms-source-file-gnu-find'." (with-temp-buffer (call-process emms-source-file-gnu-find nil t nil (expand-file-name dir) "-type" "f" "-iregex" (concat ".*\\(" regex "\\).*")) (delete "" (split-string (buffer-substring (point-min) (point-max)) "\n")))) (defmacro emms-with-excluded-directories (directory-list &rest body) "Run BODY while excluding DIRECTORY-LIST." `(let ((emms-source-file-exclude-regexp (concat (or ,emms-source-file-exclude-regexp "") "\\|\\(" (or (regexp-opt ,directory-list) "") "\\)"))) ,@body)) ;;;###autoload (defun emms-source-file-regex () "Return a regexp that matches everything any player (that supports files) can play." (mapconcat (lambda (player) (or (emms-player-get player 'regex) "")) emms-player-list "\\|")) ;; emms-locate should be part of a once to be emms-dired, with maybe ;; file rename after tag functions and so on, but till then i park it ;; here... :) ;;;###autoload (defun emms-locate (regexp) "Search for REGEXP and display the results in a locate buffer" (interactive "sRegexp to search for: ") (require 'locate) (save-window-excursion (set-buffer (get-buffer-create "*EMMS Find*")) (locate-mode) (erase-buffer) (mapc (lambda (elt) (insert (cdr (assoc 'name elt)) "\n")) (emms-source-find emms-source-file-default-directory regexp)) (locate-do-setup regexp)) (and (not (string-equal (buffer-name) "*EMMS Find*")) (switch-to-buffer-other-window "*EMMS Find*")) (run-hooks 'dired-mode-hook) (dired-next-line 2)) ;; Strictly speaking, this does not belong in this file (URLs are not ;; real files), but it's close enough :-) ;;;###autoload (autoload 'emms-play-url "emms-source-file" nil t) ;;;###autoload (autoload 'emms-add-url "emms-source-file" nil t) (define-emms-source url (url) "An EMMS source for an URL - for example, for streaming." (interactive "sPlay URL: ") (emms-playlist-insert-track (emms-track 'url url))) ;;;###autoload (autoload 'emms-play-streamlist "emms-source-file" nil t) ;;;###autoload (autoload 'emms-add-streamlist "emms-source-file" nil t) (define-emms-source streamlist (streamlist) "An EMMS source for streaming playlists (usually URLs ending in .pls)." (interactive "sPlay streamlist URL: ") (emms-playlist-insert-track (emms-track 'streamlist streamlist))) ;;;###autoload (autoload 'emms-play-lastfm "emms-lastfm" nil t) ;;;###autoload (autoload 'emms-add-lastfm "emms-lastfm" nil t) (define-emms-source lastfm (lastfm-url) "An EMMS source for Last.fm URLs, which begin with lastfm://." (interactive "sPlay Last.fm URL: ") (emms-playlist-insert-track (emms-track 'lastfm lastfm-url))) (provide 'emms-source-file) ;;; emms-source-file.el ends here ��������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-source-playlist.el������������������������������������0000664�0000000�0000000�00000045352�12205151475�0023462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-source-playlist.el --- EMMS sources from playlist files ;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, ;; 2009 Free Software Foundation, Inc. ;; Author: Jorgen Schäfer <forcer@forcix.cx> ;; Keywords: emms, mp3, mpeg, multimedia ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; This file contains track sources for EMMS which read playlist ;; files. EMMS' own playlist files are supported as well as .m3u and ;; .pls files. ;;; Code: ;; Version control (defvar emms-source-playlist-version "0.5 $Revision: 1.30 $" "emms-source-playlist.el version string") ;; $Id: emms-source-file.el,v 1.30 2005/08/11 06:16:15 yonirabkin Exp $ (require 'emms) (require 'emms-source-file) (defcustom emms-source-playlist-formats '(native pls m3u) "*A list of playlist formats. Each entry must have at least three corresponding functions. First, a function named `emms-source-playlist-FORMAT-p' which returns non-nil if the current buffer is of the type FORMAT. It is called with no arguments. Second, a function named `emms-source-playlist-parse-FORMAT' which parses the current buffer into tracks. It is called with no arguments. Third, a function named `emms-source-playlist-unparse-FORMAT' which creates an output file in the type FORMAT that contains the tracks of a playlist buffer. It is called with two arguments: The playlist buffer and the file buffer. It is also recommended to have a function named `emms-source-playlist-FORMAT-files' which returns a list of the files contained in the playlist." :type '(repeat (symbol :tag "Format")) :group 'emms) (defcustom emms-source-playlist-default-format nil "*The default format to use for saving playlists. If this is nil, you will be prompted for a format to use." :type '(choice (const :tag "Prompt each time" nil) (const :tag "Native" native) (const :tag "m3u" m3u) (const :tag "pls" pls) (symbol :tag "Other")) :group 'emms) (defcustom emms-source-playlist-ask-before-overwrite t "*Ask before saving over an existing playlist. If this is nil, existing playlists will be quietly overwritten." :type 'boolean :group 'emms) ;;; General playlist (defsubst emms-source-playlist-p-sym (format) (intern (concat "emms-source-playlist-" (symbol-name format) "-p"))) (defsubst emms-source-playlist-parse-sym (format) (intern (concat "emms-source-playlist-parse-" (symbol-name format)))) (defsubst emms-source-playlist-unparse-sym (format) (intern (concat "emms-source-playlist-unparse-" (symbol-name format)))) (defsubst emms-source-playlist-files-sym (format) (intern (concat "emms-source-playlist-" (symbol-name format) "-files"))) (defun emms-source-playlist-p (format &optional parse-files) (let ((sym (emms-source-playlist-p-sym format))) (when (and (functionp sym) (or (not parse-files) (functionp (emms-source-playlist-files-sym format)))) (funcall sym)))) (defun emms-source-playlist-parse (format file) (funcall (emms-source-playlist-parse-sym format) file)) (defun emms-source-playlist-unparse (format playlist-buf file-buf) (funcall (emms-source-playlist-unparse-sym format) playlist-buf file-buf)) (defun emms-source-playlist-files (format) (let ((sym (emms-source-playlist-files-sym format))) (if (functionp sym) (funcall sym) (error "The `%s' format cannot parse files from a playlist" format)))) (defvar emms-source-playlist-format-history nil "List of recently-entered formats; used by `emms-playlist-save'.") (defun emms-source-playlist-read-format () "Read a playlist format from the user. If `emms-source-playlist-default-format' is non-nil, use it instead of prompting the user." (or emms-source-playlist-default-format (let ((format (emms-completing-read (concat "Playlist format: (default: " (if emms-source-playlist-format-history (car emms-source-playlist-format-history) "native") ") ") (mapcar #'symbol-name emms-source-playlist-formats) nil nil nil 'emms-source-playlist-format-history (if emms-source-playlist-format-history (car emms-source-playlist-format-history) "native")))) ;; Sometimes the completion function can put partial results ;; onto the history, so pop the last one off and include the ;; completed version instead. (setq emms-source-playlist-format-history (cons format (cdr emms-source-playlist-format-history))) (intern format)))) (defun emms-playlist-save (format file) "Store the current playlist to FILE as the type FORMAT. The default format is specified by `emms-source-playlist-default-format'." (interactive (list (emms-source-playlist-read-format) (read-file-name "Store as: " emms-source-file-default-directory emms-source-file-default-directory nil))) (with-temp-buffer (emms-source-playlist-unparse format (with-current-emms-playlist (current-buffer)) (current-buffer)) (let ((backup-inhibited t)) (write-file file emms-source-playlist-ask-before-overwrite)))) (defun emms-source-playlist-determine-format (&optional parse-files) "Determine the playlist format of the current buffer. If PARSE-FILES is specified, the given format must be able to return a list of the files contained in the playlist." (catch 'return (let ((formats emms-source-playlist-formats)) (while formats (when (emms-source-playlist-p (car formats) parse-files) (throw 'return (car formats))) (setq formats (cdr formats)))))) ;;;###autoload (autoload 'emms-play-playlist "emms-source-playlist" nil t) ;;;###autoload (autoload 'emms-add-playlist "emms-source-playlist" nil t) (define-emms-source playlist (file) "An EMMS source for playlists. See `emms-source-playlist-formats' for a list of supported formats." (interactive (list (read-file-name "Playlist file: " emms-source-file-default-directory emms-source-file-default-directory t))) (mapc #'emms-playlist-insert-track (with-temp-buffer (emms-insert-file-contents file) (goto-char (point-min)) (let ((format (emms-source-playlist-determine-format))) (if format (emms-source-playlist-parse format file) (error "Not a recognized playlist format")))))) ;;; EMMS native playlists ;; Format: ;; ;;; This is an EMMS playlist file. Play it with M-x emms-play-playlist ;; <sexpr> (defun emms-source-playlist-native-p () "Return non-nil if the current buffer contains a native EMMS playlist." (save-excursion (goto-char (point-min)) (looking-at "^;;; This is an EMMS playlist file"))) (defun emms-source-playlist-parse-native (file) "Parse the native EMMS playlist in the current buffer." (save-excursion (goto-char (point-min)) (read (current-buffer)))) (defun emms-source-playlist-unparse-native (in out) "Unparse a native playlist from IN to OUT. IN should be a buffer with a EMMS playlist in it. OUT should be the buffer where tracks are stored in the native EMMS format." (with-current-buffer in ;; Don't modify the position (save-excursion ;; in the IN buffer (with-current-buffer out (insert ";;; This is an EMMS playlist file." " Play it with M-x emms-play-playlist\n") (insert "(")) (let ((firstp t)) (goto-char (point-min)) (emms-walk-tracks (let ((track (emms-playlist-track-at (point)))) (with-current-buffer out (if (not firstp) (insert "\n ") (setq firstp nil)) (prin1 track (current-buffer)))))) (with-current-buffer out (insert ")\n"))))) ;;;###autoload (autoload 'emms-play-native-playlist "emms-source-playlist" nil t) ;;;###autoload (autoload 'emms-add-native-playlist "emms-source-playlist" nil t) (define-emms-source native-playlist (file) "An EMMS source for a native EMMS playlist file." (interactive (list (read-file-name "Playlist file: " emms-source-file-default-directory emms-source-file-default-directory t))) (mapc #'emms-playlist-insert-track (with-temp-buffer (emms-insert-file-contents file) (goto-char (point-min)) (when (not (emms-source-playlist-native-p)) (error "Not a native EMMS playlist file.")) (emms-source-playlist-parse-native file)))) ;;; m3u files ;; Format: ;; Either a list of filename-per-line, ignore lines beginning with # ;; or: ;; #EXTM3U ;; #EXTINF:<length in seconds>,<name> ;; <filename> ; emms-source-playlist-m3u-p ; emms-source-playlist-parse-m3u ; emms-source-playlist-m3u-files ; emms-source-playlist-unparse-m3u (defun emms-source-playlist-m3u-p () "Return non-nil if the current buffer contains an m3u playlist. We currently have no metric for determining whether a buffer is an .m3u playlist based on its contents alone, so we assume that the more restrictive playlist formats have already been detected and simply return non-nil always." t) (defun emms-source-playlist-parse-m3u (playlist-file) "Parse the m3u playlist in the current buffer. Files will be relative to the directory of PLAYLIST-FILE, unless they have absolute paths." (let ((dir (file-name-directory playlist-file))) (mapcar (lambda (file) (if (string-match "\\`\\(http\\|mms\\)://" file) (emms-track 'url file) (emms-track 'file (expand-file-name file dir)))) (emms-source-playlist-m3u-files)))) (defun emms-source-playlist-m3u-files () "Extract a list of filenames from the given m3u playlist. Empty lines and lines starting with '#' are ignored." (let ((files nil)) (save-excursion (goto-char (point-min)) (while (re-search-forward "^[^# \n].*$" nil t) (setq files (cons (match-string 0) files)))) (nreverse files))) (defun emms-source-playlist-unparse-m3u (in out) "Unparse an m3u playlist from IN to OUT. IN should be a buffer containing an m3u playlist. OUT should be the buffer where tracks are stored in m3u format." (with-current-buffer in ;; Don't modify the position (save-excursion ;; in the IN buffer (goto-char (point-min)) (emms-walk-tracks (let ((track (emms-playlist-track-at (point)))) (with-current-buffer out (insert (emms-track-name track) ?\n))))))) ;;;###autoload (autoload 'emms-play-m3u-playlist "emms-source-playlist" nil t) ;;;###autoload (autoload 'emms-add-m3u-playlist "emms-source-playlist" nil t) (define-emms-source m3u-playlist (file) "An EMMS source for an m3u playlist file." (interactive (list (read-file-name "Playlist file: " emms-source-file-default-directory emms-source-file-default-directory t))) (mapc #'emms-playlist-insert-track (with-temp-buffer (emms-insert-file-contents file) (goto-char (point-min)) (when (not (emms-source-playlist-m3u-p)) (error "Not an m3u playlist file.")) (emms-source-playlist-parse-m3u file)))) ;;; pls files ;; Format: ;; A list of one filename per line. ;; [playlist] ;; NumberOfEntries=<num_entries> ;; File<position>=<filename> ; emms-source-playlist-pls-p ; emms-source-playlist-parse-pls ; emms-source-playlist-pls-files ; emms-source-playlist-unparse-pls (defun emms-source-playlist-pls-p () "Return non-nil if the current buffer contains a pls playlist." (save-excursion (goto-char (point-min)) (if (re-search-forward "^File[0-9]*=.+$" nil t) t nil))) (defun emms-source-playlist-parse-pls (playlist-file) "Parse the pls playlist in the current buffer. Files will be relative to the directory of PLAYLIST-FILE, unless they have absolute paths." (let ((dir (file-name-directory playlist-file))) (mapcar (lambda (file) (if (string-match "\\`\\(http\\|mms\\)://" file) (emms-track 'url file) (if (string-match "\\`file://" file) ;; handle file:// uris (let ((file (url-unhex-string (substring file 7)))) (emms-track 'file file)) (emms-track 'file (expand-file-name file dir))))) (emms-source-playlist-pls-files)))) (defun emms-source-playlist-pls-files () "Extract a list of filenames from the given pls playlist. Empty lines and lines starting with '#' are ignored." (let ((files nil)) (save-excursion (goto-char (point-min)) (while (re-search-forward "^File[0-9]*=\\(.+\\)$" nil t) (setq files (cons (match-string 1) files)))) (nreverse files))) (defun emms-source-playlist-unparse-pls (in out) "Unparse a pls playlist from IN to OUT. IN should be a buffer conatining a pls playlist. OUT should be the buffer where tracks are stored in pls format." (with-current-buffer in ;; Don't modify the position (save-excursion ;; in the IN buffer (let ((pos 0) beg) (with-current-buffer out (insert "[playlist]\n") (setq beg (point))) (goto-char (point-min)) (emms-walk-tracks (let ((track (emms-playlist-track-at (point)))) (setq pos (1+ pos)) (with-current-buffer out (insert "File" (number-to-string pos) "=" (emms-track-name track) ?\n)))) (with-current-buffer out (goto-char beg) (insert "NumberOfEntries=" (number-to-string pos) ?\n)))))) ;;;###autoload (autoload 'emms-play-pls-playlist "emms-source-playlist" nil t) ;;;###autoload (autoload 'emms-add-pls-playlist "emms-source-playlist" nil t) (define-emms-source pls-playlist (file) "An EMMS source for a pls playlist file." (interactive (list (read-file-name "Playlist file: " emms-source-file-default-directory emms-source-file-default-directory t))) (mapc #'emms-playlist-insert-track (with-temp-buffer (emms-insert-file-contents file) (goto-char (point-min)) (when (not (emms-source-playlist-pls-p)) (error "Not a pls playlist file.")) (emms-source-playlist-parse-pls file)))) ;;; extm3u files ;; Format: ;; #EXTM3U ;; #EXTINF:<length in seconds>,<name> ;; <filename> ; emms-source-playlist-extm3u-p ; emms-source-playlist-parse-extm3u ; emms-source-playlist-unparse-extm3u ;; (erase-buffer) ;; (insert "#EXTM3U\n") ;; (mapc (lambda (track) ;; (let ((time (or (emms-track-get track 'info-mtime) "")) ;; (artist (emms-track-get track 'info-artist)) ;; (title (emms-track-get track 'info-title)) ;; (name (emms-track-get track 'name))) ;; (insert (format "#EXTINF: %s,%s - %s\n%s\n" ;; time artist title name)))) ;; tracklist) ;; (save-buffer) ;; (kill-buffer (current-buffer))))) ;; Not implemented yet ;;; Adding playlists as files ;;;###autoload (autoload 'emms-play-playlist-file "emms-source-playlist" nil t) ;;;###autoload (autoload 'emms-add-playlist-file "emms-source-playlist" nil t) (define-emms-source playlist-file (file) "An EMMS source for playlist files. This adds the given file to the current EMMS playlist buffer, without adding its contents. See `emms-source-playlist-formats' for a list of supported formats." (interactive (list (read-file-name "Playlist file: " emms-source-file-default-directory emms-source-file-default-directory t))) (emms-playlist-insert-track (emms-track 'playlist (expand-file-name file)))) ;;;###autoload (autoload 'emms-play-playlist-directory ;;;###autoload "emms-source-playlist" nil t) ;;;###autoload (autoload 'emms-add-playlist-directory ;;;###autoload "emms-source-playlist" nil t) (define-emms-source playlist-directory (dir) "An EMMS source for a whole directory tree of playlist files. If DIR is not specified, it is queried from the user." (interactive (list (emms-read-directory-name "Play directory: " emms-source-file-default-directory emms-source-file-default-directory t))) (mapc (lambda (file) (unless (or (let ((case-fold-search nil)) (string-match emms-source-file-exclude-regexp file)) (file-directory-p file)) (emms-playlist-insert-track (emms-track 'playlist (expand-file-name file))))) (directory-files dir t "^[^.]"))) ;;;###autoload (autoload 'emms-play-playlist-directory-tree ;;;###autoload "emms-source-playlist" nil t) ;;;###autoload (autoload 'emms-add-playlist-directory-tree ;;;###autoload "emms-source-file" nil t) (define-emms-source playlist-directory-tree (dir) "An EMMS source for multiple directory trees of playlist files. If DIR is not specified, it is queried from the user." (interactive (list (emms-read-directory-name "Play directory tree: " emms-source-file-default-directory emms-source-file-default-directory t))) (mapc (lambda (file) (unless (let ((case-fold-search nil)) (string-match emms-source-file-exclude-regexp file)) (emms-playlist-insert-track (emms-track 'playlist file)))) (emms-source-file-directory-tree (expand-file-name dir) "^[^.]"))) (provide 'emms-source-playlist) ;;; emms-source-playlist.el ends here ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-stream-info.el����������������������������������������0000664�0000000�0000000�00000010376�12205151475�0022545�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-stream-info.el --- Info from streaming audio ;; Copyright (C) 2004, 2005, 2006, 2007, 2008, ;; 2009 Free Software Foundation, Inc. ;; Author: Yoni Rabkin <yonirabkin@member.fsf.org> ;; This file is part of EMMS. ;; EMMS 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. ;; EMMS 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 EMMS; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;; ;; Set `*emms-stream-info-backend*' to either 'vlc or 'mplayer, which ;; are the two currently supported backends for retrieving stream ;; information. You can then either call `emms-stream-info-message' ;; directly or hit "i" in the `emms-streams' buffer over stream you ;; want to investigate. ;; ;; Note that you do not have to be playing the stream in question in ;; order to find out what is playing on it since this library will ;; open its own connection to the streaming server. ;; ;; Please send bug reports and stations which do not work to the ;; maintainer (email at the top of this file). ;;; History: ;; ;; This library was re-implemented from scratch around January of ;; 2009. If you are looking for the old code then grab source code ;; older than that. ;;; Code: (defvar *emms-stream-info-backend* 'mplayer "Symbol designating the backend program to use.") ;; using unhygienic macros for good... or is it evil? (defmacro emms-stream-info-defreg (symname regexp) "Set SYMNAME to be the match for REGEXP." `(save-excursion (goto-char (point-min)) (re-search-forward ,regexp (point-max) t) (when (and (match-string-no-properties 1) (> (length (match-string-no-properties 1)) 0)) (setq ,symname (match-string-no-properties 1))))) (defun emms-stream-info-mplayer-backend (url) "Backend command for running mplayer on URL." (condition-case excep (call-process "mplayer" nil t nil "-nocache" "-endpos" "0" "-vo" "null" "-ao" "null" url) (file-error (error "Could not find the mplayer backend binary")))) (defun emms-stream-info-vlc-backend (url) "Backend command for running VLC on URL." (condition-case excep (call-process "vlc" nil t nil "-vvv" "--intf" "dummy" "--stop-time" "1" "--noaudio" url "vlc:quit") (file-error (error "Could not find the VLC backend binary")))) (defun emms-stream-info-call-backend (url) "Call backend and return a list of stream information for URL." (let ((name "N/A") (genre "N/A") (bitrate "N/A") (nowplaying "N/A")) (with-temp-buffer (message "querying stream...") (cond ((eq *emms-stream-info-backend* 'mplayer) (emms-stream-info-mplayer-backend url) (emms-stream-info-defreg name "^Name[ ]+:[ ]+\\(.*\\)$") (emms-stream-info-defreg genre "^Genre[ ]+:[ ]+\\(.*\\)$") (emms-stream-info-defreg bitrate "^Bitrate[ ]+:[ ]+\\(.*\\)$") (emms-stream-info-defreg nowplaying "ICY Info: StreamTitle='\\(.+?\\)'")) ((eq *emms-stream-info-backend* 'vlc) (emms-stream-info-vlc-backend url) (emms-stream-info-defreg name "'Title' = '\\(.*\\)'") (emms-stream-info-defreg genre "Genre: \\(.*\\)") (emms-stream-info-defreg bitrate "bitrate:\\([0-9].+\\)") (emms-stream-info-defreg nowplaying "'Now Playing' = '\\(.+?\\)'")) (t (error "Unknown backend")))) (message "querying stream...done") (list name genre bitrate nowplaying))) ;; point of entry (defun emms-stream-info-message (url) "Display a message with information about the stream at URL." (interactive "Murl: ") (let* ((stream-info (emms-stream-info-call-backend url)) (name (nth 0 stream-info)) (genre (nth 1 stream-info)) (bitrate (nth 2 stream-info)) (nowplaying (nth 3 stream-info))) (message "now playing: %s on %s, genre: %s, bitrate: %s" nowplaying name genre bitrate))) (provide 'emms-stream-info) ;;; emms-stream-info.el ends here ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-streams.el��������������������������������������������0000664�0000000�0000000�00000060002�12205151475�0021766�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;; emms-streams.el -- interface to add and play streams ;; Copyright (C) 2004, 2005, 2006, 2007, 2008, ;; 2009 Free Software Foundation, Inc. ;; Authors: Lucas Bonnet <lucas@rincevent.net> ;; Jose A Ortega Ruiz <jao@gnu.org> ;; Yoni Rabkin <yonirabkin@member.fsf.org> ;; Michael Olson <mwolson@gnu.org> ;; This file is part of EMMS. ;; EMMS 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. ;; EMMS 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 EMMS; if not, write to the Free Software Foundation, ;; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;; It is part of the EMMS package ;; Heavily based on bmk-mgr.el by Jose A Ortega Ruiz <jao@gnu.org> ;; thanks to you ! ;;; Code: (require 'emms) (require 'later-do) (defgroup emms-stream nil "*Add and play streams with EMMS." :group 'emms) (defcustom emms-stream-bookmarks-file (concat (file-name-as-directory emms-directory) "streams") "*The file where you store your favorite emms streams." :type 'file :group 'emms-stream) (defcustom emms-stream-default-action "add" "*The default action when you press RET in the EMMS Stream interface. Can be either \"add\" or \"play\". The default is \"add\"." :type 'string :group 'emms-stream) (defface emms-stream-name-face '((t (:bold t :weight bold))) "Face for stream names." :group 'emms-stream) (defface emms-stream-url-face '((((class color) (background dark)) (:foreground "LightSteelBlue")) (((class color) (background light)) (:foreground "Blue"))) "Face for stream URLs." :group 'emms-stream) (defvar emms-stream-list nil "The list that contains your current stream bookmarks.") (defvar emms-stream-buffer-name "*EMMS Streams*" "The name of the buffer used by emms-stream interface.") (defvar emms-stream-play-hook nil "*A hook run when you add or play an EMMS stream via the popup.") (defvar emms-stream-hook nil "*A hook run when you call emms-streams or emms-stream-popup.") (defvar emms-stream-current-stream nil "The stream currently being played. Needed by the info method, as the track doesn't contain all the needed info.") (defvar emms-stream-popup-old-conf nil "Old window configuration.") (defvar emms-stream-last-stream nil "The last stream added/played by EMMS.") (defvar emms-stream-playlist-buffer nil "The EMMS playlist buffer associated with emms-streams.") (defcustom emms-stream-repeat-p nil "*If non-nil, try to repeat a streamlist if it gets disconnected." :set (function (lambda (sym val) (when (buffer-live-p emms-stream-playlist-buffer) (with-current-buffer emms-stream-playlist-buffer (setq emms-repeat-playlist val))) (set sym val))) :type 'boolean :group 'emms-stream) ;; Format: (("descriptive name" url feed-number type)) ;; ;; type could be either url, playlist, or lastfm. If url, then it ;; represents a direct IP, if streamlist it's a stream playlist, if ;; lastfm it's a lastfm station (defvar emms-stream-default-list '(("SomaFM: Beatblender" "http://www.somafm.com/beatblender.pls" 1 streamlist) ("SomaFM: Secret Agent" "http://www.somafm.com/secretagent.pls" 1 streamlist) ("SomaFM: Groove Salad" "http://www.somafm.com/groovesalad.pls" 1 streamlist) ("SomaFM: Drone Zone" "http://www.somafm.com/dronezone.pls" 1 streamlist) ("SomaFM: Tag's Trance" "http://www.somafm.com/tagstrance.pls" 1 streamlist) ("SomaFM: Indie Pop Rocks" "http://www.somafm.com/indiepop.pls" 1 streamlist) ("SomaFM: Doomed" "http://www.somafm.com/doomed.pls" 1 streamlist) ("Digitally Imported, Trance" "http://www.di.fm/mp3/trance.pls" 1 streamlist) ("Digitally Imported, Deephouse" "http://www.di.fm/mp3/deephouse.pls" 1 streamlist) ("Digitally Imported, Chillout" "http://www.di.fm/mp3/chillout.pls" 1 streamlist) ("Digitally Imported, Drum and Bass" "http://www.di.fm/mp3/drumandbass.pls" 1 streamlist) ("SKY.fm, Mostly Classical" "http://www.sky.fm/mp3/classical.pls" 1 streamlist) ("SKY.fm, Jazz" "http://www.sky.fm/mp3/jazz.pls" 1 streamlist) ("Philosomatika, Goa-Trance" "http://www.shoutcast.com/sbin/shoutcast-playlist.pls?rn=1712&file=filename.pls" 1 streamlist) ("Drum and Bass Radio, BassDrive" "http://www.bassdrive.com/BassDrive.m3u" 1 streamlist) ("Flaresound, Jazzmusique" "http://64.236.34.196:80/stream/1016" 1 url) ("Flaresound, Jazzmusique" "http://205.188.234.4:8004" 2 url) ("Flaresound, L'Electric" "http://www.bp6.com:8002" 1 url) ("Stangs Garage, Eclectic" "http://www.stangsgarage.com/listen.pls" 1 streamlist) ("DNA Lounge, Live" "http://www.dnalounge.com/webcast/128.m3u" 1 streamlist) ("DNA Lounge Radio" "http://www.dnalounge.com/webcast/dnaradio.m3u" 1 streamlist) ("Virgin Radio, The Groove" "http://www.smgradio.com/core/audio/ogg/live.pls?service=grbb" 1 streamlist) ("Virgin Radio, Virgin Classic" "http://www.smgradio.com/core/audio/ogg/live.pls?service=vcbb" 1 streamlist) ("Virgin Radio, Virgin 1215AM" "http://www.smgradio.com/core/audio/ogg/live.pls?service=vrbb" 1 streamlist) ("Voices From Within - Words From Beyond" "http://207.200.96.225:8024/listen.pls" 1 streamlist) ("WCPE, Classical Music" "http://www.ibiblio.org/wcpe/wcpe.pls" 1 streamlist) ("PLUG: Voices of the Free Software movement" "http://purduelug.org:8000/voices-free_software.ogg" 1 url) ("VGamp Radio, Video Game music" "http://vgamp.com/listen128.pls" 1 streamlist) ("Kohina - Old school game and demo music" "http://stream.nute.net/kohina/stream.ogg.m3u" 1 streamlist) ("Nectarine, Demoscene Radio" "http://www.scenemusic.eu:8002/high.ogg.m3u" 1 streamlist) ("idobi Radio" "http://www.idobi.com/radio/iradio.pls" 1 streamlist) ("radio.wazee - Modern Alternative Rock" "http://www.wazee.org/128.pls" 1 streamlist) ("ChroniX Aggression - Loud & Clear" "http://www.chronixradio.com/chronixaggression/listen/listen.pls" 1 streamlist) ("WFMU, Freeform radio" "http://www.wfmu.org/wfmu.pls" 1 streamlist) ("KEXP - Seattle Community Radio" "http://kexp-mp3-128k.cac.washington.edu:8000/listen.pls" 1 streamlist) ("KRUU-LP - Fairfield, Iowa Community Radio" "http://kruufm.com/live.pls" 1 streamlist) ("WBCR-LP - Berkshire Community Radio" "http://nyc01.egihosting.com:6232/listen.pls" 1 streamlist))) (defvar emms-stream-mode-map (let ((map (make-keymap))) (suppress-keymap map) (define-key map (kbd "C-a") 'beginning-of-line) (define-key map (kbd "C-e") 'end-of-line) (define-key map (kbd "C-k") 'emms-stream-kill-bookmark) (define-key map (kbd "C-y") 'emms-stream-yank-bookmark) (define-key map (kbd "C-n") 'emms-stream-next-line) (define-key map (kbd "C-p") 'emms-stream-previous-line) (define-key map (kbd "Q") 'emms-stream-quit) (define-key map (kbd "a") 'emms-stream-add-bookmark) (define-key map (kbd "d") 'emms-stream-delete-bookmark) (define-key map (kbd "e") 'emms-stream-edit-bookmark) (define-key map (kbd "h") 'describe-mode) (define-key map (kbd "n") 'emms-stream-next-line) (define-key map (kbd "p") 'emms-stream-previous-line) (define-key map (kbd "q") 'emms-stream-quit) (define-key map (kbd "s") 'emms-stream-save-bookmarks-file) (define-key map (kbd "t") 'emms-stream-toggle-default-action) ;; (define-key map (kbd "u") 'emms-stream-move-bookmark-up) (define-key map (kbd "i") 'emms-stream-info-bookmark) (define-key map (kbd "<up>") 'emms-stream-previous-line) (define-key map (kbd "<down>") 'emms-stream-next-line) (define-key map (kbd "<left>") 'beginning-of-line) (define-key map (kbd "<right>") 'end-of-line) (define-key map (kbd "RET") 'emms-stream-play) map) "Keymap for `emms-stream-menu'.") ;;;###autoload (defun emms-streams () "Opens the EMMS Streams interface." (interactive) (kill-buffer (get-buffer-create emms-stream-buffer-name)) (set-buffer (get-buffer-create emms-stream-buffer-name)) (erase-buffer) (when (string= emms-stream-default-action "play") (emms-stream-create-playlist)) (emms-stream-mode) (switch-to-buffer emms-stream-buffer-name)) (defun emms-stream-mode () (kill-all-local-variables) (buffer-disable-undo) (setq major-mode 'emms-stream-mode) (setq mode-name "EMMS Streams") (use-local-map emms-stream-mode-map) (emms-stream-init) (set (make-local-variable 'truncate-lines) t) (set (make-local-variable 'auto-hscroll-mode) t) (set (make-local-variable 'kill-whole-line) t) (set (make-local-variable 'next-line-add-newlines) nil) (goto-char 1) (emms-stream-display) (toggle-read-only 1) (run-hooks 'emms-stream-hook) (set-buffer-modified-p nil) (message "EMMS Stream Menu")) (defun emms-stream-create-playlist () "Create a new EMMS playlist and associate it with emms-streams. This is used when `emms-stream-default-action' is \"play\"." (save-excursion (setq emms-stream-playlist-buffer (emms-playlist-set-playlist-buffer (emms-playlist-new))) (with-current-buffer emms-stream-playlist-buffer ;; if emms-stream-repeat-p is non-nil, make sure that we ;; continue to play the station, even if briefly disconnected (set (make-local-variable 'emms-repeat-playlist) emms-stream-repeat-p)))) (defun emms-stream-kill-playlist () "Delete the EMMS playlist associated with emms-streams, if one exists." (when (buffer-live-p emms-stream-playlist-buffer) (save-excursion (if (eq emms-stream-playlist-buffer emms-playlist-buffer) (emms-playlist-current-kill) (kill-buffer emms-stream-playlist-buffer))) (setq emms-stream-playlist-buffer nil))) (defun emms-stream-popup-revert () "Revert to the window-configuration from before if there is one, otherwise just remove the special bindings from the stream menu." (interactive) (remove-hook 'emms-pbi-manually-change-song-hook 'emms-pbi-popup-revert) (let ((streambuffer (get-buffer emms-stream-buffer-name))) (when streambuffer (save-excursion (set-buffer streambuffer) ;; (local-unset-key (kbd "q")) (local-unset-key (kbd "TAB"))))) ;; (local-unset-key (kbd "RET"))))) (when emms-stream-popup-old-conf (set-window-configuration emms-stream-popup-old-conf)) (remove-hook 'emms-stream-play-hook 'emms-stream-popup-revert) (remove-hook 'emms-stream-quit-hook 'emms-stream-popup-revert)) (defun emms-stream-popup (&optional popup-height) "Pops up the stream Menu, for the new stream selection. POPUP-HEIGHT is the height of the new frame, defaulting to `emms-popup-default-height'." (interactive) (setq popup-height (or popup-height (/ (window-height) 2))) ;; Split the current screen, and make the stream menu popup (let ((new-window-height (- (window-height) popup-height))) (if (not (> new-window-height 0)) (error "Current window too small to popup menu!")) ;; Save the current window-configuration (setq emms-stream-popup-old-conf (current-window-configuration)) ;; Split and select the menu (let ((buffer-down (split-window-vertically new-window-height))) (select-window buffer-down)) (kill-buffer (get-buffer-create emms-stream-buffer-name)) (switch-to-buffer (get-buffer-create emms-stream-buffer-name)) (erase-buffer) (emms-stream-mode) (add-hook 'emms-stream-play-hook 'emms-stream-popup-revert) (add-hook 'emms-stream-quit-hook 'emms-stream-popup-revert) (local-set-key (kbd "TAB") 'emms-stream-popup-revert) (local-set-key (kbd "RET") 'emms-stream-play) ;; (local-set-key (kbd "q") 'delete-window) ;; Also, forget about the whole thing if the user does something ;; to the window-configuration ;; (add-hook 'window-configuration-change-hook 'emms-stream-popup-forget-conf))) )) (defun emms-stream-init () (setq emms-stream-list (emms-stream-read-file emms-stream-bookmarks-file))) (defun emms-stream-read-file (file) "Returns a sexp." (let ((file (expand-file-name file))) (if (file-readable-p file) (with-temp-buffer (emms-insert-file-contents file) (goto-char (point-min)) (read (current-buffer))) emms-stream-default-list))) (defun emms-stream-save-bookmarks-file () (interactive) (save-excursion (let ((buffer (find-file-noselect emms-stream-bookmarks-file))) (set-buffer buffer) (erase-buffer) (insert "(") (let ((firstp t)) (dolist (stream emms-stream-list) (if (not firstp) (insert "\n ") (setq firstp nil)) ;; make sure type identifier is a symbol, not a string (when (stringp (nth 3 stream)) (setq stream (copy-alist stream)) (setcar (nthcdr 3 stream) (intern (nth 3 stream)))) (prin1 stream buffer))) (insert ")\n") (save-buffer) (kill-buffer buffer))) (set-buffer-modified-p nil)) (defun emms-stream-display-line (line) (insert (emms-stream-name line)) (add-text-properties (point-at-bol) (point-at-eol) '(face emms-stream-name-face)) (add-text-properties (point-at-bol) (point-at-eol) `(emms-stream ,line)) (insert "\n ") (insert (emms-stream-url line)) (add-text-properties (point-at-bol) (point-at-eol) '(face emms-stream-url-face)) (insert "\n")) (defun emms-stream-display () "Displays the bookmark list in the current buffer, in a human readable way." (mapc 'emms-stream-display-line emms-stream-list) (goto-char (point-min))) ;; Helper functions (defun emms-stream-take (n list) "Takes N elements from LIST." (let ((idx 0) (res '())) (while (< idx n) (setq res (append res (list (nth idx list)))) (setq idx (+ idx 1))) res)) (defun emms-stream-insert-at (n elt list) "Inserts the element ELT in LIST, *before* position N. Positions are counted starting with 0." (let* ((n-1 (- n 1)) (before (emms-stream-take n-1 list)) (after (last list (- (length list) n-1)))) (append before (list elt) after))) (defun emms-stream-insert-several-at (n new-list list) "Inserts the list NEW-LIST in LIST, *before* position N. Positions are counted starting with 0." (let* ((n-1 (- n 1)) (before (emms-stream-take n-1 list)) (after (last list (- (length list) n-1)))) (append before new-list after))) (defun emms-stream-look-behind () "Return non-nil if the position behind the point is an emms-stream." (and (not (bobp)) (get-text-property (1- (point)) 'emms-stream))) (defun emms-stream-back-to-stream () "If we are not on a stream, move backwards to the nearest one." (unless (get-text-property (point) 'emms-stream) (unless (emms-stream-look-behind) (goto-char (or (previous-single-property-change (point) 'emms-stream) (point-min)))) (goto-char (or (previous-single-property-change (point) 'emms-stream) (point-min))))) (defun emms-stream-get-bookmark-at-point () "Returns the bookmark under point." (emms-stream-back-to-stream) (get-text-property (point) 'emms-stream)) (defun emms-stream-redisplay () (let ((inhibit-read-only t)) (erase-buffer) (goto-char (point-min)) (emms-stream-display))) (defun emms-stream-determine-fd (name) "Return a feed descriptor, given NAME. This is the count of the times NAME appears in the bookmark list, plus one." (let ((count 1)) (dolist (feed emms-stream-list) (when (string= (emms-stream-name feed) name) (setq count (1+ count)))) count)) (defun emms-stream-add-bookmark (name url fd type) "Creates a new bookmark, and inserts it at point position. Don't forget to run `emms-stream-save-bookmarks-file' after !" (interactive (list (read-string "Name of the bookmark: ") (read-string "URL: ") nil (emms-completing-read "Type (url, streamlist, or lastfm): " (mapcar #'list '("url" "streamlist" "lastfm"))))) (unless fd (setq fd (emms-stream-determine-fd name))) (when (stringp type) (setq type (intern type))) (let* ((line (emms-line-number-at-pos (point))) (index (+ (/ line 2) 1))) (setq emms-stream-list (emms-stream-insert-at index (list name url fd type) emms-stream-list)) (emms-stream-redisplay) (goto-char (point-min)) (forward-line (1- line)))) (defun emms-stream-delete-bookmark () "Deletes the bookmark under the point. Don't forget to save your modifications !" (interactive) (let ((line (emms-line-number-at-pos (point)))) (setq emms-stream-list (delete (emms-stream-get-bookmark-at-point) emms-stream-list)) (emms-stream-redisplay) (goto-char (point-min)) (forward-line (1- line)))) (defun emms-stream-edit-bookmark () "Change the information of current bookmark." (interactive) (let* ((bookmark (emms-stream-get-bookmark-at-point)) (name (read-from-minibuffer "Description: " (emms-stream-name bookmark))) (url (read-from-minibuffer "URL: " (emms-stream-url bookmark))) (fd (read-from-minibuffer "Feed Descriptor: " (int-to-string (emms-stream-fd bookmark)))) (type (read-from-minibuffer "Type (url, streamlist, or lastfm): " (format "%s" (emms-stream-type bookmark))))) (emms-stream-delete-bookmark) (emms-stream-add-bookmark name url (string-to-number fd) type))) (defun emms-stream-name (el) (car el)) (defun emms-stream-url (el) (cadr el)) (defun emms-stream-fd (el) (car (cddr el))) (defun emms-stream-type (el) (cadr (cddr el))) (defun emms-stream-play () (interactive) (let* ((line (or (get-text-property (point) 'emms-stream) (progn (goto-char (or (previous-single-property-change (point) 'emms-stream) (point-min))) (goto-char (or (previous-single-property-change (point) 'emms-stream) (point-min))) (get-text-property (point) 'emms-stream)) (error "No stream found at point"))) (name (emms-stream-name line)) (url (emms-stream-url line)) (fd (emms-stream-fd line)) (type (emms-stream-type line)) (player (read (concat "emms-" emms-stream-default-action "-" (format "%s" type))))) (setq emms-stream-last-stream line) ;; (funcall emms-stream-default-action url) (funcall player url) (if (string= emms-stream-default-action "add") (message "URL added to playlist"))) (later-do 'emms-mode-line-alter) (run-hooks 'emms-stream-play-hook)) (defun emms-stream-info-bookmark () "Return the station and track information for the streaming audio station under point." (interactive) (if (fboundp 'emms-stream-info-message) (let* ((line (get-text-property (point) 'emms-stream)) (url (emms-stream-url line))) (emms-stream-info-message url)) (message "Streaming media info not available."))) ;; Killing and yanking (defvar emms-stream-killed-streams () "Bookmarks that have been killed.") (defun emms-stream-kill-bookmark () "Kill the current bookmark." (interactive) (let ((stream (emms-stream-get-bookmark-at-point))) (setq emms-stream-list (delete stream emms-stream-list) emms-stream-killed-streams (cons stream emms-stream-killed-streams))) (let ((inhibit-read-only t)) (kill-line 2))) (defun emms-stream-yank-bookmark () "Yank bookmark into the streams buffer." (interactive) (emms-stream-back-to-stream) (let ((inhibit-read-only t) (streams nil)) ;; get all valid streams (save-restriction (narrow-to-region (point) (point)) (yank) (goto-char (point-min)) (while (and (< (point) (point-max)) (car emms-stream-killed-streams) (looking-at "^\\(.+\\)\n \\(.+\\)\n")) (setq streams (cons (car emms-stream-killed-streams) streams) emms-stream-killed-streams (cdr emms-stream-killed-streams)) (goto-char (match-end 0))) (delete-region (point-min) (point-max))) ;; insert streams into list (if streams (let* ((line (emms-line-number-at-pos (point))) (index (+ (/ line 2) 1))) (setq emms-stream-list (emms-stream-insert-several-at index streams emms-stream-list)) (setq line (+ line (* (length streams) 2))) (emms-stream-redisplay) (goto-char (point-min)) (forward-line (1- line))) (message "Not yanking anything")))) ;; Navigation (defun emms-stream-next-line () (interactive) (when (get-text-property (point) 'emms-stream) (goto-char (or (next-single-property-change (point) 'emms-stream) (point-max)))) (goto-char (or (next-single-property-change (point) 'emms-stream) (point-max))) (forward-line 0)) (defun emms-stream-previous-line () (interactive) (emms-stream-back-to-stream) (goto-char (or (previous-single-property-change (point) 'emms-stream) (point-min))) (goto-char (or (previous-single-property-change (point) 'emms-stream) (point-min))) (forward-line 0)) (defun emms-stream-quit () (interactive) (emms-stream-kill-playlist) (kill-this-buffer) (run-hooks 'emms-stream-quit-hook)) (defun emms-stream-toggle-default-action () "Toggle between adding to the current active playlist or play right now (and thus erase the current active playlist)." (interactive) (if (string= emms-stream-default-action "play") (progn (emms-stream-kill-playlist) (setq emms-stream-default-action "add") (message "Default action is now add")) (emms-stream-create-playlist) (setq emms-stream-default-action "play") (message "Default action is now play"))) ;; info part ; (define-emms-info-method emms-info-url ; :providep 'emms-info-url-providep ; :get 'emms-info-url-get) ;; :set 'emms-info-url-set) ;; A way to get the last element. it is either the only one, or the ;; last one added by emms-add-url. so in both cases, that's what we ;; want. ;; FIXME : not working with the new design. Yrk ? ; (defun emms-stream-last-element () ; (elt emms-playlist (- (length emms-playlist) 1))) (defun emms-info-url-providep (track) (if (eq (emms-track-type track) 'url) t nil)) ; (defun emms-info-url-get (track) ; (make-emms-info ; :title (emms-stream-url (emms-track-get track 'metadata)) ; :artist (emms-stream-name (emms-track-get track 'metadata)) ; :album " " ; :note " " ; :year " " ; :genre " " ; :file (emms-stream-url (emms-track-get track 'metadata)))) ;; Then you register it with emms-info, by adding it to ;; `emms-info-methods-list'. ; (add-to-list 'emms-info-methods-list 'emms-info-url) (defun emms-stream-add-data-to-track (track) (emms-track-set track 'metadata emms-stream-last-stream)) (add-to-list 'emms-track-initialize-functions 'emms-stream-add-data-to-track) ; (when (featurep 'emms-info) ; (eval-when-compile (require 'emms-info)) ; appease byte-compiler ; (add-to-list 'emms-info-methods-list 'emms-info-streamlist) ; (defun emms-info-streamlist-providep (track) ; (if (eq (emms-track-type track) 'streamlist) ; t ; nil)) ; (define-emms-info-method emms-info-streamlist ;; FIXME-PLS ? ; :providep 'emms-info-streamlist-providep ;; FIXME-PLS ? ; :get 'emms-info-url-get)) (provide 'emms-streams) ;;; emms-streams.el ends here ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-tag-editor.el�����������������������������������������0000664�0000000�0000000�00000074212�12205151475�0022357�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-tag-editor.el --- Edit track tags. ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; ;; Author: Ye Wenbin <wenbinye@163.com> ;; This file is part of EMMS. ;; 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, or (at your option) ;; any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ;;; Commentary: ;; Put this file into your load-path and the following into your ~/.emacs: ;; (require 'emms-tag-editor) ;;; Code: (eval-when-compile (require 'cl)) (condition-case nil (require 'overlay) (error nil)) (require 'emms) (eval-when-compile (require 'emms-info-metaflac)) (require 'emms-info-mp3info) (require 'emms-playlist-mode) (require 'emms-mark) (require 'format-spec) (defvar emms-tag-editor-tags '((info-artist . "a") (info-composer . "C") (info-performer . "p") (info-title . "t") (info-album . "l") (info-tracknumber . "n") (info-year . "y") (info-genre . "g") (info-date . "d") (info-note . "c")) "An alist to determine the format of various info tags.") (defvar emms-tag-editor-edit-buffer "*EMMS-TAGS*" "Name of the buffer used for editing tags.") (defvar emms-tag-editor-log-buffer "*EMMS-LOG*" "Name of emms-tag-editor's log buffer.") (defun emms-tag-editor-make-format (tags) "Make a format string based on TAGS." (concat "%m\n" (emms-propertize (format "%-16s = " "name") 'read-only t 'rear-nonsticky t 'face 'bold) "%f\n" (mapconcat (lambda (tag) (concat (emms-propertize (format "%-16s = " (symbol-name tag)) 'read-only t 'rear-nonsticky t 'face 'bold) "%" (cdr (assoc tag emms-tag-editor-tags)))) tags "\n") "\n\n")) (defvar emms-tag-editor-formats (let* ((tags (mapcar 'car emms-tag-editor-tags)) (default (emms-tag-editor-make-format (remove 'info-date tags)))) `(("mp3" . ,default) ("ogg" . ,(emms-tag-editor-make-format (remove 'info-year tags))) ("flac" . ,(emms-tag-editor-make-format (remove 'info-year tags))) ("default" . ,default))) "Format to use when inserting the track. The CAR part is the extension of the track name, and the CDR part is the format template. The format specification is like: m -- Track description f -- Track name a -- Track info-artist c -- Track info-composer p -- Track info-performer t -- Track info-title l -- Track info-album n -- Track info-tracknumber D -- Track info-discnumber y -- Track info-year g -- Track info-genre ; -- Track info-note You can add new specifications in `emms-tag-editor-tags', and use `emms-tag-editor-make-format' to create a new format string. The CDR part also can be a function, which accepts one parameter, the track, and returns a string to insert in `emms-tag-editor-edit-buffer'.") (defvar emms-tag-editor-get-format-function 'emms-tag-editor-get-format "Determines which function to call to get the format string, which is used for inserting the track.") (defvar emms-tag-editor-parse-function 'emms-tag-editor-default-parser "Function to parse tags in `emms-tag-editor-edit-buffer'. It should find all modified tags, and return all the tracks. The tracks for which a tag has been modified should set a property 'tag-modified to t. If the track name has been changed, the function should set a new property 'newname instead of setting the 'name directly. See also `emms-tag-editor-default-parser'.") (defvar emms-tag-editor-tagfile-functions '(("mp3" "mp3info" ((info-artist . "a") (info-title . "t") (info-album . "l") (info-tracknumber . "n") (info-year . "y") (info-genre . "g") (info-note . "c"))) ("ogg" . emms-tag-editor-tag-ogg) ("flac" . emms-tag-editor-tag-flac)) "An alist used when committing changes to tags in files. If the external program sets tags by command line options one-by-one such as mp3info, then the list should like: (EXTENSION PROGRAM COMMAND_LINE_OPTIONS) Otherwise, a function that accepts a single parameter, the track, should be given. See also `emms-tag-editor-tag-file' and `emms-tag-editor-tag-ogg'.") (defun emms-tag-editor-tag-flac (track) "Commit changes to an FLAC file according to TRACK." (require 'emms-info-metaflac) (with-temp-buffer (let (need val) (mapc (lambda (tag) (let ((info-tag (intern (concat "info-" tag)))) (when (> (length (setq val (emms-track-get track info-tag))) 0) (insert (upcase tag) "=" val "\n")))) '("artist" "composer" "performer" "title" "album" "tracknumber" "discnumber" "date" "genre" "note")) (when (buffer-string) (funcall #'call-process-region (point-min) (point-max) emms-info-metaflac-program-name nil (get-buffer-create emms-tag-editor-log-buffer) nil "--import-tags-from=-" (emms-track-name track)))))) (defun emms-tag-editor-tag-ogg (track) "Commit changes to an OGG file according to TRACK." (let (args val) (mapc (lambda (tag) (let ((info-tag (intern (concat "info-" tag)))) (when (> (length (setq val (emms-track-get track info-tag))) 0) (setq args (append (list "-t" (concat (upcase tag) "=" val)) args))))) '("artist" "composer" "performer" "title" "album" "tracknumber" "date" "genre" "note")) (when args (apply #'call-process "vorbiscomment" nil (get-buffer-create emms-tag-editor-log-buffer) nil "-w" (append args (list (emms-track-name track))))))) (defun emms-tag-editor-tag-file (track program tags) "Change TAGS in FILE, using PROGRAM. Valid tags are given by `emms-tag-editor-tagfile-functions'." (let (args val) (mapc (lambda (tag) (setq val (emms-track-get track (car tag))) (if (and val (stringp val)) (setq args (append (list (concat "-" (cdr tag)) val) args)))) tags) (apply 'call-process program nil (get-buffer-create emms-tag-editor-log-buffer) nil (nconc args (list filename))))) (defun emms-tag-editor-get-format (track) "Get the format string to use for committing changes to TRACK." (let ((format (assoc (file-name-extension (emms-track-name track)) emms-tag-editor-formats))) (if format (cdr format) (cdr (assoc "default" emms-tag-editor-formats))))) (defun emms-tag-editor-format-track (track) "Return a string representing the info tags contained in TRACK. This string is suitable for inserting into the tags buffer." (let ((format (funcall emms-tag-editor-get-format-function track))) (if (functionp format) (funcall format track) (format-spec format (apply 'format-spec-make ?m (emms-propertize (emms-track-force-description track) 'face 'emms-playlist-track-face 'emms-track (copy-sequence track)) ?f (emms-track-name track) (apply 'append (mapcar (lambda (tag) (list (string-to-char (cdr tag)) (or (emms-track-get track (car tag)) ""))) emms-tag-editor-tags))))))) (defun emms-tag-editor-track-at (&optional pos) "Return a copy of the track at POS. Defaults to point if POS is nil." (let ((track (emms-playlist-track-at pos)) newtrack) (when track (setq newtrack (copy-sequence track)) (emms-track-set newtrack 'position (point-marker)) (emms-track-set newtrack 'orig-track track) newtrack))) (defsubst emms-tag-editor-erase-buffer (&optional buf) "Erase the buffer BUF, and ensure that it exists." (let ((inhibit-read-only t)) (save-excursion (set-buffer (get-buffer-create buf)) (erase-buffer)))) (defsubst emms-tag-editor-insert-track (track) "Insert TRACK, if it is specified." (and track (insert (emms-tag-editor-format-track track)))) (defsubst emms-tag-editor-display-log-buffer-maybe () "Display the log buffer if it has any contents." (if (> (buffer-size (get-buffer emms-tag-editor-log-buffer)) 0) (display-buffer emms-tag-editor-log-buffer))) (defun emms-tag-editor-insert-tracks (tracks) "Insert TRACKS into the tag editor buffer." (save-excursion (emms-tag-editor-erase-buffer emms-tag-editor-log-buffer) (emms-tag-editor-erase-buffer emms-tag-editor-edit-buffer) (set-buffer (get-buffer emms-tag-editor-edit-buffer)) (mapc 'emms-tag-editor-insert-track tracks) (emms-tag-editor-mode) (pop-to-buffer (current-buffer)) (goto-char (point-min)) (emms-tag-editor-display-log-buffer-maybe))) (defun emms-tag-editor-edit-track (track) "Edit the track at point, or TRACK." (interactive (list (emms-tag-editor-track-at))) (if (null track) (message "No track at point!") (emms-tag-editor-insert-tracks (list track)))) (defun emms-tag-editor-edit-marked-tracks () "Edit all tracks marked in the current buffer." (interactive) (let ((tracks (emms-mark-mapcar-marked-track 'emms-tag-editor-track-at t))) (if (null tracks) (message "No track marked!") (emms-tag-editor-insert-tracks tracks)))) (defun emms-tag-editor-edit () "Edit tags of either the track at point or all marked tracks." (interactive) (if (emms-mark-has-markedp) (emms-tag-editor-edit-marked-tracks) (emms-tag-editor-edit-track (emms-tag-editor-track-at)))) (defvar emms-tag-editor-mode-map (let ((map (make-sparse-keymap))) (define-key map [tab] 'emms-tag-editor-next-field) (define-key map [backtab] 'emms-tag-editor-prev-field) (define-key map "\C-c\C-n" 'emms-tag-editor-next-track) (define-key map "\C-c\C-p" 'emms-tag-editor-prev-track) (define-key map "\C-c\C-c" 'emms-tag-editor-submit-and-exit) (define-key map "\C-c\C-s" 'emms-tag-editor-submit) (define-key map "\C-x\C-s" 'emms-tag-editor-submit) (define-key map "\C-c\C-r" 'emms-tag-editor-set-all) (define-key map "\C-c\C-a" 'emms-tag-editor-replace-in-tag) (define-key map "\C-c\C-t" 'emms-tag-editor-transpose-tag) map) "Keymap for `emms-tag-editor-mode'.") (define-key emms-playlist-mode-map "E" 'emms-tag-editor-edit) (define-derived-mode emms-tag-editor-mode text-mode "Tag-Edit" "Major mode to edit track tags. \\{emms-tag-editor-mode-map}") (defun emms-tag-editor-set-all (tag value) "Set TAG to VALUE in all tracks. If transient-mark-mode is turned on, you can apply the command to a selected region. If `transient-mark-mode' is on and the mark is active, the changes will only take effect on the tracks in the region." (interactive (list (emms-completing-read "Set tag: " (mapcar (lambda (arg) (list (symbol-name (car arg)))) emms-tag-editor-tags) nil t) (read-from-minibuffer "To: "))) (save-excursion (save-restriction (if (and mark-active transient-mark-mode) (narrow-to-region (region-beginning) (region-end))) (goto-char (point-min)) (while (re-search-forward (concat "^" (regexp-quote tag)) nil t) (skip-chars-forward " \t=") (delete-region (point) (line-end-position)) (insert value))))) (defun emms-tag-editor-replace-in-tag (tag from to) "Query and replace text in selected TAG. For example, if the info-title tag is selected, then only perform replacement in title tags. If `transient-mark-mode' is on and the mark is active, the changes will only take effect on the tracks in the region." (interactive (cons (emms-completing-read "Replace in tag: " (mapcar (lambda (arg) (list (symbol-name (car arg)))) emms-tag-editor-tags) nil t) (let ((common (query-replace-read-args (if (and transient-mark-mode mark-active) "Query replace regexp in region" "Query replace regexp") t))) (butlast common)))) (let ((overlay (make-overlay (point-min) (1+ (point-min))))) (overlay-put overlay 'face 'match) (unwind-protect (save-excursion (save-restriction (when (and mark-active transient-mark-mode) (narrow-to-region (region-beginning) (region-end)) (deactivate-mark)) (setq tag (concat (regexp-quote tag) "[ \t]+=[ \t]+")) (goto-char (point-min)) (map-y-or-n-p (lambda (match) (move-overlay overlay (match-beginning 0) (match-end 0)) (format "Replace %s to %s" match to)) (lambda (match) (delete-region (- (point) (length match)) (point)) (insert to)) (lambda () (if (and (save-excursion (re-search-backward tag (line-beginning-position) t)) (re-search-forward from (line-end-position) t)) (match-string 0) (let (found) (while (and (not found) (re-search-forward tag nil t)) (if (re-search-forward from (line-end-position) t) (setq found t))) (and found (match-string 0)))))))) (delete-overlay overlay)))) (defun emms-tag-editor-transpose-tag (tag1 tag2) "Transpose value of TAG1 and TAG2. If `transient-mark-mode' is on and the mark is active, the changes will only take effect on the tracks in the region." (interactive (let* ((tag1 (intern (emms-completing-read "Tag 1: " (mapcar (lambda (arg) (list (symbol-name (car arg)))) emms-tag-editor-tags) nil t))) (tag2 (intern (emms-completing-read "Tag 2: " (mapcar (lambda (arg) (list (symbol-name (car arg)))) (assq-delete-all tag1 (copy-sequence emms-tag-editor-tags))) nil t)))) (list tag1 tag2))) (save-excursion (save-restriction (if (and mark-active transient-mark-mode) (narrow-to-region (region-beginning) (region-end))) (let* ((emms-playlist-buffer-p t) (tracks (emms-playlist-tracks-in-region (point-min) (point-max))) (inhibit-read-only t) temp) (erase-buffer) (dolist (track (nreverse tracks)) (setq temp (emms-track-get track tag1)) (emms-track-set track tag1 (emms-track-get track tag2)) (emms-track-set track tag2 temp) (emms-track-set track 'tag-modified t) (emms-tag-editor-insert-track track)))))) (defun emms-tag-editor-guess-tag-filename (pattern fullname) "A pattern is a string like \"%a-%t-%y\" which stand for the file name is constructed by artist, title, year with seperator '-'. see `emms-tag-editor-compile-pattern' for detail about pattern syntax. Available tags are list in `emms-tag-editor-tags'. if with prefix argument, the information will extract from full name, otherwise just match in file name. An example to guess tag from file name, which the file directory is the aritist and file name is the title. It can be done like: C-u M-x emms-tag-editor-guess-tag-filename RET %{a:[^/]+}/%{t:[^/]+}\.mp3 RET " (interactive (list (read-from-minibuffer (format "Match in %sfile name(C-h for help): " (if current-prefix-arg "FULL " "")) nil (let ((map (make-sparse-keymap))) (set-keymap-parent map minibuffer-local-map) (define-key map "\C-h" (lambda () (interactive) (with-output-to-temp-buffer "*Help*" (princ "A pattern is a string like \"%a-%t-%y\" which stand for the file name is constructed by artist, title, year with seperator '-'. see `emms-tag-editor-compile-pattern' for detail about pattern syntax. Available tags are: ") (mapc (lambda (tag) (princ (format "\t%s - %S\n" (cdr tag) (car tag)))) emms-tag-editor-tags) (save-excursion (set-buffer standard-output) (help-mode))))) map)) current-prefix-arg)) (setq pattern (emms-tag-editor-compile-pattern pattern)) (save-excursion (save-restriction (if (and mark-active transient-mark-mode) (narrow-to-region (region-beginning) (region-end))) (let* ((emms-playlist-buffer-p t) (tracks (emms-playlist-tracks-in-region (point-min) (point-max))) (inhibit-read-only t) filename) (erase-buffer) (dolist (track (nreverse tracks)) (emms-track-set track 'tag-modified t) (setq filename (emms-track-name track)) (or fullname (setq filename (file-name-nondirectory filename))) (when (string-match (car pattern) filename) (mapc (lambda (pair) (emms-track-set track (car (rassoc (char-to-string (car pair)) emms-tag-editor-tags)) (match-string (cdr pair) filename))) (cdr pattern))) (emms-tag-editor-insert-track track)))))) (defun emms-tag-editor-compile-pattern (pattern) "A pattern to regexp convertor. \"%a-%{b:[a-z]+}\" will compile to \"\\([^-]+\\)-\\([a-z]+\\)\"." (let ((index 0) (paren 0) (i 0) (len (length pattern)) (compiled "") registers register match escape c) (while (< i len) (setq c (aref pattern i) i (1+ i)) (cond ((= c ?\\) (setq c (aref pattern i) i (1+ i)) (cond ((= c ?\() (setq paren (1+ paren) index (1+ index))) ((= c ?\)) (setq paren (1- paren)))) (setq compiled (concat compiled "\\" (char-to-string c)))) ((= c ?%) (setq c (aref pattern i) i (1+ i)) ;; How to repressent } in the pattern? (if (= c ?{) (if (/= (aref pattern (1+ i)) ?:) (error "Compile error") (setq register (aref pattern i) match "" i (+ i 2)) (while (and (< i len) (or escape (/= (aref pattern i) ?}))) (if escape (setq escape nil) (if (= (aref pattern i) ?\\) (setq escape t))) (setq match (concat match (char-to-string (aref pattern i))) i (1+ i))) (setq i (1+ i))) (setq register c match "[^-]+")) (setq compiled (concat compiled "\\(" match "\\)") index (1+ index)) (add-to-list 'registers (cons register index))) (t (setq compiled (concat compiled (char-to-string c)))))) (if (/= paren 0) (error "Paren not match!")) (cons compiled registers))) (defun emms-tag-editor-next-field (arg) "Move to the next tag field." (interactive "p") (if (> arg 0) (re-search-forward "\\s-*=[ \t]*" nil nil arg) (emms-tag-editor-prev-field (- arg)))) (defun emms-tag-editor-prev-field (arg) "Move to the previous tag field." (interactive "p") (if (< arg 0) (emms-tag-editor-next-field (- arg)) (skip-chars-backward " \t=") (re-search-backward "\\s-*=[ \t]*" nil nil arg) (skip-chars-forward " \t="))) (defun emms-tag-editor-prev-track () "Move to the previous track." (interactive) (let ((prev (previous-single-property-change (point) 'emms-track))) (when (not prev) (error "No previous track")) (when (not (get-text-property prev 'emms-track)) (setq prev (or (previous-single-property-change prev 'emms-track) (point-min)))) (when (or (not prev) (not (get-text-property prev 'emms-track))) (error "No previous track")) (goto-char prev))) (defun emms-tag-editor-next-track () "Move to the next track." (interactive) (let ((next (next-single-property-change (point) 'emms-track))) (when (not next) (error "No next track")) (when (not (get-text-property next 'emms-track)) (setq next (next-single-property-change next 'emms-track))) (when (or (not next) (= next (point-max))) (error "No next track")) (goto-char next))) (defun emms-tag-editor-submit (arg) "Make modified tags take affect. With prefix argument, bury the tag edit buffer." (interactive "P") (let ((tracks (funcall emms-tag-editor-parse-function))) (if (not (and tracks (y-or-n-p "Submit changes? "))) (message "No tags were modified") (emms-tag-editor-erase-buffer emms-tag-editor-log-buffer) (emms-tag-editor-apply tracks))) (if arg (bury-buffer))) (defun emms-tag-editor-apply (tracks) "Apply all changes made to TRACKS." (message "Setting tags...") (let (filename func exit old pos val need-sync) (save-excursion (dolist (track tracks) (when (emms-track-get track 'tag-modified) (setq filename (emms-track-name track) old (emms-track-get track 'orig-track)) ;; rename local file (when (and (emms-track-get track 'newname) (eq (emms-track-get track 'type) 'file) (file-writable-p (emms-track-name track)) (y-or-n-p (format "Rename %s to %s? " (emms-track-name track) (emms-track-get track 'newname)))) (setq filename (emms-track-get track 'newname)) (ignore-errors ;; Ignore errors so that renaming multiple files doesn't stop ;; because of one that fails. In that case it's probably ;; old-file = newfile which causes the problem. (rename-file (emms-track-name track) filename 1)) (emms-track-set old 'name filename) ;; for re-enter this function (emms-track-set track 'name filename) (setq need-sync t) ;; register to emms-cache-db (when (boundp 'emms-cache-modified-function) (funcall emms-cache-modified-function) (funcall emms-cache-set-function 'file filename old))) (emms-track-set track 'newname nil) ;; set tags to original track (dolist (tag emms-tag-editor-tags) (when (setq val (emms-track-get track (car tag))) (emms-track-set old (car tag) val))) ;; use mp3info to change tag in mp3 file (when (and (eq (emms-track-get track 'type) 'file) (file-writable-p (emms-track-name track)) (setq func (assoc (file-name-extension filename) emms-tag-editor-tagfile-functions))) (setq exit (if (functionp (cdr func)) (funcall (cdr func) track) (emms-tag-editor-tag-file track (cadr func) (nth 2 func)))) (if (zerop exit) (emms-track-get track 'info-mtime (butlast (current-time))) (emms-tag-editor-log "Changing tags of %s failed with exit value %d" filename exit))) ;; update track in playlist (when (and (setq pos (emms-track-get track 'position)) (marker-position pos)) (set-buffer (marker-buffer pos)) (goto-char pos) (funcall emms-playlist-update-track-function)) ;; clear modified tag (emms-track-set track 'tag-modified nil)))) (if (and (featurep 'emms-cache) need-sync (y-or-n-p "You have changed some track names; sync the cache? ")) (and (fboundp 'emms-cache-sync) ; silence byte-compiler (emms-cache-sync))) (unless (emms-tag-editor-display-log-buffer-maybe) (message "Setting tags...done")))) (defun emms-tag-editor-submit-and-exit () "Submit changes to track information and exit the tag editor." (interactive) (emms-tag-editor-submit t)) (defun emms-tag-editor-default-parser () "Default function used to parse tags in `emms-tag-editor-edit-buffer'." (let (next tracks track key val) (goto-char (point-min)) (if (get-text-property (point) 'emms-track) (setq next (point)) (setq next (next-single-property-change (point) 'emms-track))) (when next (while (progn (goto-char next) (setq track (get-text-property (point) 'emms-track)) (forward-line 1) (mapc (lambda (pair) (when (string-match "\\s-*=\\s-*" pair) (setq key (intern-soft (substring pair 0 (match-beginning 0))) val (substring pair (match-end 0))) (when (and key (let ((old (emms-track-get track key))) (if old (not (string= val old)) (string< "" val)))) (if (eq key 'name) (emms-track-set track 'newname val) (emms-track-set track key val)) (emms-track-set track 'tag-modified t)))) (let ((end-point (next-single-property-change (point) 'emms-track))) (if (and end-point (save-excursion (goto-char end-point) (bolp))) (setq next end-point) (progn (setq next nil end-point (point-max)))) (split-string (buffer-substring (point) end-point) "\n"))) (if (emms-track-get track 'tag-modified) (push track tracks)) next)) tracks))) (defun emms-tag-editor-log (&rest args) (with-current-buffer (get-buffer-create emms-tag-editor-log-buffer) (goto-char (point-max)) (insert (apply 'format args) "\n"))) ;; ;; Renaming files according their tags ;; (defvar emms-tag-editor-rename-format "%a - %l - %n - %t" "When `emms-tag-editor-rename' is invoked the track's file will be renamed according this format specification. The file extension will be added automatically. It uses the format specs defined in `emms-tag-editor-tags'.") (defun emms-tag-editor-rename () "Rename the file corresponding to track at point or all marked tracks according to the value of `emms-tag-editor-rename-format'." (interactive) (if (emms-mark-has-markedp) (emms-tag-editor-rename-marked-tracks) (emms-tag-editor-rename-track (emms-tag-editor-track-at)))) (defun emms-tag-editor-rename-track (track &optional dont-apply) "Rename TRACK's file according `emms-tag-editor-rename-format's value. If DONT-APPLY is non-nil the changes won't be applied directly. Then it's the callers job to apply them afterwards with `emms-tag-editor-apply'." (if (eq (emms-track-get track 'type) 'file) (let* ((old-file (emms-track-name track)) (path (file-name-directory old-file)) (suffix (file-name-extension old-file)) (new-file (concat path (format-spec emms-tag-editor-rename-format (apply 'format-spec-make (apply 'append (mapcar (lambda (tag) (list (string-to-char (cdr tag)) (or (emms-track-get track (car tag)) ""))) emms-tag-editor-tags)))) "." suffix))) (emms-track-set track 'newname new-file) (emms-track-set track 'tag-modified t) (unless dont-apply (emms-tag-editor-apply (list track)))) (message "Only files can be renamed."))) (defun emms-tag-editor-rename-marked-tracks () "Rename the files corresponding to all marked tracks according `emms-tag-editor-rename-format's value." (let ((tracks (emms-mark-mapcar-marked-track 'emms-tag-editor-track-at t))) (if (null tracks) (message "No track marked!") (dolist (track tracks) (emms-tag-editor-rename-track track t)) (emms-tag-editor-apply tracks)))) (define-key emms-playlist-mode-map "R" 'emms-tag-editor-rename) (provide 'emms-tag-editor) ;;; Emms-tag-editor.el ends here ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-url.el������������������������������������������������0000664�0000000�0000000�00000007424�12205151475�0021123�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-url.el --- Make URL and EMMS work together well ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; These routines sanify input to URL and parse data returned by URL. ;;; Code: (require 'url) (require 'emms-compat) (defvar emms-url-specials-entire '((?\ . "%20") (?\n . "%0D%0A")) "*An alist of characters which must be represented specially in URLs. The transformation is the key of the pair. This is used by `emms-url-quote-entire'.") (defun emms-url-quote-entire (url) "Escape specials conservatively in an entire URL. The specials to escape are specified by the `emms-url-specials-entire' variable. If you want to escape parts of URLs thoroughly, then use `emms-url-quote' instead." (apply (function concat) (mapcar (lambda (ch) (let ((repl (assoc ch emms-url-specials-entire))) (if (null repl) (char-to-string ch) (cdr repl)))) (append url nil)))) (defun emms-url-quote (s &optional safe) "Replace special characters in S using the `%xx' escape. This is useful for escaping parts of URLs, but not entire URLs. Characters in [a-zA-Z_.-/] and SAFE(default is \"\") will never be quoted. e.g., (emms-url-quote \"abc def\") => \"abc%20def\"." (if (not (stringp s)) "" (or safe (setq safe "")) (save-match-data (let ((re (if (string-match "]" safe) ;; `]' should be placed at the beginning inside [] (format "[]a-zA-Z_.-/%s]" (emms-replace-regexp-in-string "]" "" safe)) (format "[a-zA-Z_.-/%s]" safe)))) (mapconcat (lambda (c) (let ((s1 (char-to-string c))) (if (string-match re s1) s1 (format "%%%02x" c)))) (string-to-list (encode-coding-string s 'utf-8)) ""))))) (defun emms-url-quote-plus (s &optional safe) "Run (emms-url-quote s \" \"), then replace ` ' with `+'." (emms-replace-regexp-in-string " " "+" (emms-url-quote s (concat safe " ")))) (defun emms-http-content-coding () (save-match-data (and (boundp 'url-http-content-type) (stringp url-http-content-type) (string-match ";\\s-*charset=\\([^;[:space:]]+\\)" url-http-content-type) (intern-soft (downcase (match-string 1 url-http-content-type)))))) (defun emms-http-decode-buffer (&optional buffer) "Recode the buffer with `url-retrieve's contents. Else the buffer would contain multibyte chars like \\123\\456." (with-current-buffer (or buffer (current-buffer)) (let* ((default (or (car default-process-coding-system) 'utf-8)) (coding (or (emms-http-content-coding) default))) (when coding ;; (pop-to-buffer (current-buffer)) ;; (message "content-type: %s" url-http-content-type) ;; (message "coding: %S [default: %S]" coding default) (set-buffer-multibyte t) (decode-coding-region (point-min) (point-max) coding))))) (provide 'emms-url) ;;; emms-url.el ends here ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-volume-amixer.el��������������������������������������0000664�0000000�0000000�00000004662�12205151475�0023114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-volume-amixer.el --- a mode for changing volume using amixer ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Martin Schoenmakers <aiviru@diamond-age.net> ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; This file defines a few simple functions to raise or lower the volume ;; using amixer. It can be used stand-alone, though it's meant for usage ;; with EMMS, particularly with emms-volume.el ;;; History: ;; May 30 2006: First cleanup and collation of amixer functions into a ;; separate file for releasability. ;;; Todo: ;; There probably needs to be more configurability, which may in turn ;; mean adding some more functions. ;; Some of this could benefit from adding customize interfaces. ;;; Code: (defcustom emms-volume-amixer-control "Master" "The control to change the volume with. Controls includes \"Master\", \"PCM\", etc. For a full list of available controls, run `amixer controls' in a shell." :type '(choice (const :tag "Master" "Master") (const :tag "PCM" "PCM") (string :tag "Something else: ")) :group 'emms-volume) ;;;###autoload (defun emms-volume-amixer-change (amount) "Change amixer master volume by AMOUNT." (message "Playback channels: %s" (with-temp-buffer (when (zerop (call-process "amixer" nil (current-buffer) nil "sset" emms-volume-amixer-control (format "%d%%%s" (abs amount) (if (< amount 0) "-" "+")))) (if (re-search-backward "\\[\\([0-9]+%\\)\\]" nil t) (match-string 1)))))) (provide 'emms-volume-amixer) ;;; emms-volume-amixer.el ends here ������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms-volume.el���������������������������������������������0000664�0000000�0000000�00000011356�12205151475�0021627�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms-volume.el --- Volume functions and a minor mode to adjust volume easily ;; Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Martin Schoenmakers <aiviru@diamond-age.net> ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; ;; EMMS 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 EMMS; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;; ;; This file provides generally two things: ;; Generic volume setting functions and some appropriate bindings for EMMS ;; playlist buffers. These can also be bound to global keys,however, the ;; second part may be more useful for this. This part provides functions ;; meant to be bound to a global key (the author uses C-c e + and C-c e -), ;; which then temporarily activates a minor mode allowing you to change the ;; volume with just + and -. This mode deactivates a short (configurable) ;; amount of time after the last volume change. This allows for easier volume ;; adjustment without getting in the way. ;;; History: ;; May 2006: First stab at writing the minor mode. ;; ;; 30 May 2006: Cleanup and restructuring to fit with EMMS. ;;; Todo: ;; Some of this could benefit from adding customize interfaces. ;;; Code: (require 'emms) (require 'emms-playlist-mode) (require 'emms-volume-amixer) ;; Customize group (defgroup emms-volume nil "Volume setting for EMMS." :group 'emms) ;; General volume setting related code. (defcustom emms-volume-change-function 'emms-volume-amixer-change "*The function to use to change the volume. If you have your own functions for changing volume, set this." :type '(choice (const :tag "Amixer" emms-volume-amixer-change) (const :tag "MPD" emms-volume-mpd-change) (function :tag "Lisp function")) :group 'emms-volume) (defcustom emms-volume-change-amount 2 "The amount to use when raising or lowering the volume using the emms-volume interface. This should be a positive integer." :type 'integer :group 'emms-volume) ;;;###autoload (defun emms-volume-raise () "Raise the speaker volume." (interactive) (funcall emms-volume-change-function emms-volume-change-amount)) ;;;###autoload (defun emms-volume-lower () "Lower the speaker volume." (interactive) (funcall emms-volume-change-function (- emms-volume-change-amount))) (define-key emms-playlist-mode-map (kbd "+") 'emms-volume-raise) (define-key emms-playlist-mode-map (kbd "-") 'emms-volume-lower) ;; Code specific to the minor mode. (define-minor-mode emms-volume-minor-mode "Allows volume setting with + and - after an initial key combo." :global t :init-value nil :lighter " (+/-)" :keymap '(("+" . emms-volume-mode-plus) ("-" . emms-volume-mode-minus))) (defvar emms-volume-mode-timeout 2 "*The timeout in amount of seconds used by `emms-volume-minor-mode'.") (defvar emms-volume-mode-timer nil "The timer `emms-volume-minor-mode' uses.") ;;;###autoload (defun emms-volume-mode-plus () "Raise volume and enable or extend the `emms-volume-minor-mode' timeout." (interactive) (emms-volume-raise) (emms-volume-mode-start-or-extend)) ;;;###autoload (defun emms-volume-mode-minus () "Lower volume and enable or extend the `emms-volume-minor-mode' timeout." (interactive) (emms-volume-lower) (emms-volume-mode-start-or-extend)) (defun emms-volume-mode-disable-timer () "Disable `emms-volume-minor-mode' timer." (cancel-timer emms-volume-mode-timer) (setq emms-volume-mode-timer nil)) (defun emms-volume-mode-set-timer () "Set a new `emms-volume-minor-mode' timer." (when emms-volume-mode-timer (emms-volume-mode-disable-timer)) (setq emms-volume-mode-timer (run-at-time emms-volume-mode-timeout nil 'emms-volume-mode-timer-timeout))) (defun emms-volume-mode-timer-timeout () "Function to disable `emms-volume-minor-mode' at timeout." (setq emms-volume-mode-timer nil) (emms-volume-minor-mode -1)) (defun emms-volume-mode-start-or-extend () "Start `emms-volume-minor-mode' or extend its running time." (when (null emms-volume-minor-mode) (emms-volume-minor-mode 1)) (emms-volume-mode-set-timer)) (provide 'emms-volume) ;;; emms-volume.el ends here ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/emms.el����������������������������������������������������0000664�0000000�0000000�00000143605�12205151475�0020325�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; emms.el --- The Emacs Multimedia System ;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, ;; 2009 Free Software Foundation, Inc. ;; Author: Jorgen Schäfer <forcer@forcix.cx> ;; Keywords: emms, mp3, mpeg, multimedia ;; This file is part of EMMS. ;; EMMS 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, or (at your option) ;; any later version. ;; EMMS 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 EMMS; see the file COPYING. If not, write to the Free ;; Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ;; 02110-1301, USA. ;;; Commentary: ;; This is the very core of EMMS. It provides ways to play a track ;; using `emms-start', to go through the playlist using the commands ;; `emms-next' and `emms-previous', to stop the playback using ;; `emms-stop', and to see what's currently playing using `emms-show'. ;; But in itself, this core is useless, because it doesn't know how to ;; play any tracks --- you need players for this. In fact, it doesn't ;; even know how to find any tracks to consider playing --- for this, ;; you need sources. ;; A sample configuration is offered in emms-setup.el, so you might ;; just want to use that file. ;;; Code: (defvar emms-version "3.0" "EMMS version string.") ;;; User Customization (defgroup emms nil "*The Emacs Multimedia System." :prefix "emms-" :group 'multimedia :group 'applications) (defgroup emms-player nil "*Track players for EMMS." :prefix "emms-player-" :group 'emms) (defgroup emms-source nil "*Track sources for EMMS." :prefix "emms-source-" :group 'emms) (defcustom emms-player-list nil "*List of players that EMMS can use. You need to set this!" :group 'emms :type '(repeat (symbol :tag "Player"))) (defcustom emms-show-format "Currently playing: %s" "*The format to use for `emms-show'. Any \"%s\" is replaced by what `emms-track-description-function' returns for the currently playing track." :group 'emms :type 'string) (defcustom emms-repeat-playlist nil "*Non-nil if the EMMS playlist should automatically repeat. If nil, playback will stop when the last track finishes playing. If non-nil, EMMS will wrap back to the first track when that happens." :group 'emms :type 'boolean) (defcustom emms-random-playlist nil "*Non-nil means that tracks are played randomly. If nil, tracks are played sequentially." :group 'emms :type 'boolean) (defcustom emms-repeat-track nil "Non-nil, playback will repeat current track. If nil, EMMS will play track by track normally." :group 'emms :type 'boolean) (defcustom emms-completing-read-function (if (and (boundp 'ido-mode) ido-mode) 'ido-completing-read 'completing-read) "Function to call when prompting user to choose between a list of options. This should take the same arguments as `completing-read'. Some possible values are `completing-read' and `ido-completing-read'. Note that you must set `ido-mode' if using `ido-completing-read'." :group 'emms :type 'function) (defcustom emms-track-description-function 'emms-track-simple-description "*Function for describing an EMMS track in a user-friendly way." :group 'emms :type 'function) (defcustom emms-player-delay 0 "The delay to pause after a player finished. This is a floating-point number of seconds. This is necessary for some platforms where it takes a bit to free the audio device after a player has finished. If EMMS is skipping songs, increase this number." :type 'number :group 'emms) (defcustom emms-playlist-shuffle-function 'emms-playlist-simple-shuffle "*The function to use for shuffling the playlist." :type 'function :group 'emms) (defcustom emms-playlist-sort-function 'emms-playlist-simple-sort "*The function to use for sorting the playlist." :type 'function :group 'emms) (defcustom emms-playlist-uniq-function 'emms-playlist-simple-uniq "*The function to use for removing duplicate tracks in the playlist." :type 'function :group 'emms) (defcustom emms-sort-lessp-function 'emms-sort-track-name-less-p "*Function for comparing two EMMS tracks. The function should return non-nil if and only if the first track sorts before the second (see `sort')." :group 'emms :type 'function) (defcustom emms-playlist-buffer-name " *EMMS Playlist*" "*The default name of the EMMS playlist buffer." :type 'string :group 'emms) (defcustom emms-playlist-default-major-mode default-major-mode "*The default major mode for EMMS playlist." :type 'function :group 'emms) (defcustom emms-playlist-insert-track-function 'emms-playlist-simple-insert-track "*A function to insert a track into the playlist buffer." :group 'emms :type 'function) (make-variable-buffer-local 'emms-playlist-insert-track-function) (defcustom emms-playlist-update-track-function 'emms-playlist-simple-update-track "*A function to update the track at point. This is called when the track information changed. This also shouldn't assume that the track has been inserted before." :group 'emms :type 'function) (make-variable-buffer-local 'emms-playlist-insert-track-function) (defcustom emms-playlist-delete-track-function 'emms-playlist-simple-delete-track "*A function to delete the track at point in the playlist buffer." :group 'emms :type 'function) (make-variable-buffer-local 'emms-playlist-delete-track-function) (defcustom emms-playlist-source-inserted-hook nil "*Hook run when a source got inserted into the playlist. The buffer is narrowed to the new tracks." :type 'hook :group 'emms) (defcustom emms-playlist-selection-changed-hook nil "*Hook run after another track is selected in the EMMS playlist." :group 'emms :type 'hook) (defcustom emms-playlist-cleared-hook nil "*Hook run after the current EMMS playlist is cleared. This happens both when the playlist is cleared and when a new buffer is created for it." :group 'emms :type 'hook) (defcustom emms-track-initialize-functions nil "*List of functions to call for each new EMMS track. This can be used to initialize tracks with various info." :group 'emms :type 'hook) (defcustom emms-track-info-filters nil "*List of functions to call when a track changes data, before updating the display. These functions are passed the track as an argument." :group 'emms :type 'hook) (defcustom emms-track-updated-functions nil "*List of functions to call when a track changes data, after updating the display. These functions are passed the track as an argument." :group 'emms :type 'hook) (defcustom emms-player-started-hook nil "*Hook run when an EMMS player starts playing." :group 'emms :type 'hook :options '(emms-show)) (defcustom emms-player-stopped-hook nil "*Hook run when an EMMS player is stopped by the user. See `emms-player-finished-hook'." :group 'emms :type 'hook) (defcustom emms-player-finished-hook nil "*Hook run when an EMMS player finishes playing a track. Please pay attention to the differences between `emms-player-finished-hook' and `emms-player-stopped-hook'. The former is called only when the player actually finishes playing a track; the latter, only when the player is stopped interactively." :group 'emms :type 'hook) (defcustom emms-player-next-function 'emms-next-noerror "*A function run when EMMS thinks the next song should be played." :group 'emms :type 'function :options '(emms-next-noerror emms-random)) (defcustom emms-player-paused-hook nil "*Hook run when a player is paused or resumed. Use `emms-player-paused-p' to find the current state." :group 'emms :type 'hook) (defcustom emms-seek-seconds 10 "The number of seconds to seek forward or backward when seeking." :group 'emms :type 'number) (defcustom emms-player-seeked-functions nil "*Functions called when a player is seeking. The functions are called with a single argument, the amount of seconds the player did seek." :group 'emms :type 'hook) (defcustom emms-player-time-set-functions nil "*Functions called when a player is setting the elapsed time of a track. The functions are called with a single argument, the time elapsed since the beginning of the current track." :group 'emms :type 'hook) (defcustom emms-cache-get-function nil "A function to retrieve a track entry from the cache. This is called with two arguments, the type and the name." :group 'emms :type 'function) (defcustom emms-cache-set-function nil "A function to add/set a track entry from the cache. This is called with three arguments: the type of the track, the name of the track, and the track itself." :group 'emms :type 'function) (defcustom emms-cache-modified-function nil "A function to be called when a track is modified. The modified track is passed as the argument to this function." :group 'emms :type 'function) (defcustom emms-directory "~/.emacs.d/emms" "*Directory variable from which all other emms file variables are derived." :group 'emms :type 'string) (defvar emms-player-playing-p nil "The currently playing EMMS player, or nil.") (defvar emms-player-paused-p nil "Whether the current player is paused or not.") (defvar emms-source-old-buffer nil "The active buffer before a source was invoked. This can be used if the source depends on the current buffer not being the playlist buffer.") (defvar emms-playlist-buffer nil "The current playlist buffer, if any.") ;;; Macros ;;; These need to be at the top of the file so that compilation works. (defmacro with-current-emms-playlist (&rest body) "Run BODY with the current buffer being the current playlist buffer. This also disables any read-onliness of the current buffer." `(progn (when (or (not emms-playlist-buffer) (not (buffer-live-p emms-playlist-buffer))) (emms-playlist-current-clear)) (let ((emms-source-old-buffer (or emms-source-old-buffer (current-buffer)))) (with-current-buffer emms-playlist-buffer (let ((inhibit-read-only t)) ,@body))))) (put 'with-current-emms-playlist 'lisp-indent-function 0) (put 'with-current-emms-playlist 'edebug-form-spec '(body)) (defmacro emms-with-inhibit-read-only-t (&rest body) "Simple wrapper around `inhibit-read-only'." `(let ((inhibit-read-only t)) ,@body)) (put 'emms-with-inhibit-read-only-t 'edebug-form-spec '(body)) (defmacro emms-with-widened-buffer (&rest body) `(save-restriction (widen) ,@body)) (put 'emms-with-widened-buffer 'edebug-form-spec '(body)) (defmacro emms-walk-tracks (&rest body) "Execute BODY for each track in the current buffer, starting at point. Point will be placed at the beginning of the track before executing BODY. Point will not be restored afterward." (let ((donep (make-symbol "donep"))) `(let ((,donep nil)) ;; skip to first track if not on one (unless (emms-playlist-track-at (point)) (condition-case nil (emms-playlist-next) (error (setq ,donep t)))) ;; walk tracks (while (not ,donep) ,@body (condition-case nil (emms-playlist-next) (error (setq ,donep t))))))) (put 'emms-walk-tracks 'lisp-indent-function 0) (put 'emms-walk-tracks 'edebug-form-spec '(body)) ;;; User Interface (defun emms-start () "Start playing the current track in the EMMS playlist." (interactive) (unless emms-player-playing-p (emms-player-start (emms-playlist-current-selected-track)))) (defun emms-stop () "Stop any current EMMS playback." (interactive) (when emms-player-playing-p (emms-player-stop))) (defun emms-next () "Start playing the next track in the EMMS playlist. This might behave funny if called from `emms-player-next-function', so use `emms-next-noerror' in that case." (interactive) (when emms-player-playing-p (emms-stop)) (emms-playlist-current-select-next) (emms-start)) (defun emms-next-noerror () "Start playing the next track in the EMMS playlist. Unlike `emms-next', this function doesn't signal an error when called at the end of the playlist. This function should only be called when no player is playing. This is a good function to put in `emms-player-next-function'." (interactive) (when emms-player-playing-p (error "A track is already being played")) (cond (emms-repeat-track (emms-start)) ((condition-case nil (progn (emms-playlist-current-select-next) t) (error nil)) (emms-start)) (t (message "No next track in playlist")))) (defun emms-previous () "Start playing the previous track in the EMMS playlist." (interactive) (when emms-player-playing-p (emms-stop)) (emms-playlist-current-select-previous) (emms-start)) (defun emms-random () "Jump to a random track." (interactive) (when emms-player-playing-p (emms-stop)) (emms-playlist-current-select-random) (emms-start)) (defun emms-pause () "Pause the current player. If player hasn't started, then start it now." (interactive) (if emms-player-playing-p (emms-player-pause) (emms-start))) (defun emms-seek (seconds) "Seek the current player SECONDS seconds. This can be a floating point number for sub-second fractions. It can also be negative to seek backwards." (interactive "nSeconds to seek: ") (emms-ensure-player-playing-p) (emms-player-seek seconds)) (defun emms-seek-to (seconds) "Seek the current player to SECONDS seconds. This can be a floating point number for sub-second fractions. It can also be negative to seek backwards." (interactive "nSeconds to seek to: ") (emms-ensure-player-playing-p) (emms-player-seek-to seconds)) (defun emms-seek-forward () "Seek ten seconds forward." (interactive) (when emms-player-playing-p (emms-player-seek emms-seek-seconds))) (defun emms-seek-backward () "Seek ten seconds backward." (interactive) (when emms-player-playing-p (emms-player-seek (- emms-seek-seconds)))) (defun emms-show (&optional insertp) "Describe the current EMMS track in the minibuffer. If INSERTP is non-nil, insert the description into the current buffer instead. This function uses `emms-show-format' to format the current track." (interactive "P") (let ((string (if emms-player-playing-p (format emms-show-format (emms-track-description (emms-playlist-current-selected-track))) "Nothing playing right now"))) (if insertp (insert string) (message "%s" string)))) (defun emms-shuffle () "Shuffle the current playlist. This uses `emms-playlist-shuffle-function'." (interactive) (with-current-emms-playlist (save-excursion (funcall emms-playlist-shuffle-function)))) (defun emms-sort () "Sort the current playlist. This uses `emms-playlist-sort-function'." (interactive) (with-current-emms-playlist (save-excursion (funcall emms-playlist-sort-function)))) (defun emms-uniq () "Remove duplicates from the current playlist. This uses `emms-playlist-uniq-function'." (interactive) (with-current-emms-playlist (save-excursion (funcall emms-playlist-uniq-function)))) (defun emms-toggle-random-playlist () "Toggle whether emms plays the tracks randomly or sequentially. See `emms-random-playlist'." (interactive) (setq emms-random-playlist (not emms-random-playlist)) (if emms-random-playlist (progn (setq emms-player-next-function 'emms-random) (message "Will play the tracks randomly.")) (setq emms-player-next-function 'emms-next-noerror) (message "Will play the tracks sequentially."))) (defun emms-toggle-repeat-playlist () "Toggle whether emms repeats the playlist after it is done. See `emms-repeat-playlist'." (interactive) (setq emms-repeat-playlist (not emms-repeat-playlist)) (if emms-repeat-playlist (message "Will repeat the playlist after it is done.") (message "Will stop after the playlist is over."))) (defun emms-toggle-repeat-track () "Toggle whether emms repeats the current track. See `emms-repeat-track'." (interactive) (setq emms-repeat-track (not emms-repeat-track)) (if emms-repeat-track (message "Will repeat the current track.") (message "Will advance to the next track after this one."))) (defun emms-sort-track-name-less-p (a b) "Return non-nil if the track name of A sorts before B." (string< (emms-track-name a) (emms-track-name b))) (defun emms-ensure-player-playing-p () "Raise an error if no player is playing right now." (when (not emms-player-playing-p) (error "No EMMS player playing right now"))) (defun emms-completing-read (&rest args) "Read a string in the minibuffer, with completion. Set `emms-completing-read' to determine which function to use. See `completing-read' for a description of ARGS." (apply emms-completing-read-function args)) ;;; Compatibility functions (require 'emms-compat) ;;; Utility functions (defun emms-insert-file-contents (filename &optional visit) "Insert the contents of file FILENAME after point. Do character code conversion and end-of-line conversion, but none of the other unnecessary things like format decoding or `find-file-hook'. If VISIT is non-nil, the buffer's visited filename and last save file modtime are set, and it is marked unmodified. If visiting and the file does not exist, visiting is completed before the error is signaled." (let ((format-alist nil) (after-insert-file-functions nil) (inhibit-file-name-handlers (append '(jka-compr-handler image-file-handler epa-file-handler) inhibit-file-name-handlers)) (inhibit-file-name-operation 'insert-file-contents)) (insert-file-contents filename visit))) ;;; Dictionaries ;; This is a simple helper data structure, used by both players ;; and tracks. (defsubst emms-dictionary (name) "Create a new dictionary of type NAME." (list name)) (defsubst emms-dictionary-type (dict) "Return the type of the dictionary DICT." (car dict)) (defun emms-dictionary-get (dict name &optional default) "Return the value of NAME in DICT." (let ((item (assq name (cdr dict)))) (if item (cdr item) default))) (defun emms-dictionary-set (dict name value) "Set the value of NAME in DICT to VALUE." (let ((item (assq name (cdr dict)))) (if item (setcdr item value) (setcdr dict (append (cdr dict) (list (cons name value)))))) dict) ;;; Tracks ;; This is a simple datatype to store track information. ;; Each track consists of a type (a symbol) and a name (a string). ;; In addition, each track has an associated dictionary of information. (defun emms-track (type name) "Create an EMMS track with type TYPE and name NAME." (let ((track (when emms-cache-get-function (funcall emms-cache-get-function type name)))) (when (not track) (setq track (emms-dictionary '*track*)) ;; Prevent the cache from being called for these two sets (let ((emms-cache-modified-function nil)) (emms-track-set track 'type type) (emms-track-set track 'name name)) (when emms-cache-set-function (funcall emms-cache-set-function type name track))) ;; run any hooks regardless of a cache hit, as the entry may be ;; old (run-hook-with-args 'emms-track-initialize-functions track) track)) (defun emms-track-p (obj) "True if OBJ is an emms track." (and (listp obj) (eq (car obj) '*track*))) (defun emms-track-type (track) "Return the type of TRACK." (emms-track-get track 'type)) (defun emms-track-name (track) "Return the name of TRACK." (emms-track-get track 'name)) (defun emms-track-get (track name &optional default) "Return the value of NAME for TRACK. If there is no value, return DEFAULT (or nil, if not given)." (emms-dictionary-get track name default)) (defun emms-track-set (track name value) "Set the value of NAME for TRACK to VALUE." (emms-dictionary-set track name value) (when emms-cache-modified-function (funcall emms-cache-modified-function track))) (defun emms-track-description (track) "Return a description of TRACK. This function uses the global value for `emms-track-description-function', rather than anything the current mode might have set. Use `emms-track-force-description' instead if you need to insert a description into a playlist buffer." (funcall (default-value 'emms-track-description-function) track)) (defun emms-track-updated (track) "Information in TRACK got updated." (run-hook-with-args 'emms-track-info-filters track) (emms-playlist-track-updated track) (run-hook-with-args 'emms-track-updated-functions track)) (defun emms-track-simple-description (track) "Simple function to give a user-readable description of a track. If it's a file track, just return the file name. Otherwise, return the type and the name with a colon in between. Hex-encoded characters in URLs are replaced by the decoded character." (let ((type (emms-track-type track))) (cond ((eq 'file type) (emms-track-name track)) ((eq 'url type) (emms-format-url-track-name (emms-track-name track))) (t (concat (symbol-name type) ": " (emms-track-name track)))))) (defun emms-format-url-track-name (name) "Format URL track name for better readability." (url-unhex-string name)) (defun emms-track-force-description (track) "Always return text that describes TRACK. This is used when inserting a description into a buffer. The reason for this is that if no text was returned (i.e. the user defined a track function that returned nil or the empty string), a confusing error message would result." (let ((desc (funcall emms-track-description-function track))) (if (and (stringp desc) (not (string= desc ""))) desc (emms-track-simple-description track)))) ;;; The Playlist ;; Playlists are stored in buffers. The current playlist buffer is ;; remembered in the `emms-playlist' variable. The buffer consists of ;; any kind of data. Strings of text with a `emms-track' property are ;; the tracks in the buffer. (defvar emms-playlist-buffers nil "The list of EMMS playlist buffers. You should use the `emms-playlist-buffer-list' function to retrieve a current list of EMMS buffers. Never use this variable for that purpose.") (defvar emms-playlist-selected-marker nil "The marker for the currently selected track.") (make-variable-buffer-local 'emms-playlist-selected-marker) (defvar emms-playlist-buffer-p nil "Non-nil if the current buffer is an EMMS playlist.") (make-variable-buffer-local 'emms-playlist-buffer-p) (defun emms-playlist-ensure-playlist-buffer () "Throw an error if we're not in a playlist-buffer." (when (not emms-playlist-buffer-p) (error "Not an EMMS playlist buffer"))) (defun emms-playlist-set-playlist-buffer (&optional buffer) "Set the current playlist buffer." (interactive (list (let* ((buf-list (mapcar #'(lambda (buf) (list (buffer-name buf))) (emms-playlist-buffer-list))) (default (or (and emms-playlist-buffer-p ;; default to current buffer (buffer-name)) ;; pick shortest buffer name, since it is ;; likely to be a shared prefix (car (sort buf-list #'(lambda (lbuf rbuf) (< (length (car lbuf)) (length (car rbuf))))))))) (emms-completing-read "Playlist buffer to make current: " buf-list nil t default)))) (let ((buf (if buffer (get-buffer buffer) (current-buffer)))) (with-current-buffer buf (emms-playlist-ensure-playlist-buffer)) (setq emms-playlist-buffer buf) (when (interactive-p) (message "Set current EMMS playlist buffer")) buf)) (defun emms-playlist-new (&optional name) "Create a new playlist buffer. The buffer is named NAME, but made unique. NAME defaults to `emms-playlist-buffer-name'. If called interactively, the new buffer is also selected." (interactive) (let ((buf (generate-new-buffer (or name emms-playlist-buffer-name)))) (with-current-buffer buf (when (not (eq major-mode emms-playlist-default-major-mode)) (funcall emms-playlist-default-major-mode)) (setq emms-playlist-buffer-p t)) (add-to-list 'emms-playlist-buffers buf) (when (interactive-p) (switch-to-buffer buf)) buf)) (defun emms-playlist-buffer-list () "Return a list of EMMS playlist buffers. The first element is guaranteed to be the current EMMS playlist buffer, if it exists, otherwise the slot will be used for the other EMMS buffers. The list will be in newest-first order." ;; prune dead buffers (setq emms-playlist-buffers (emms-delete-if (lambda (buf) (not (buffer-live-p buf))) emms-playlist-buffers)) ;; add new buffers (mapc (lambda (buf) (when (buffer-live-p buf) (with-current-buffer buf (when (and emms-playlist-buffer-p (not (memq buf emms-playlist-buffers))) (setq emms-playlist-buffers (cons buf emms-playlist-buffers)))))) (buffer-list)) ;; force current playlist buffer to head position (when (and (buffer-live-p emms-playlist-buffer) (not (eq (car emms-playlist-buffers) emms-playlist-buffer))) (setq emms-playlist-buffers (cons emms-playlist-buffer (delete emms-playlist-buffer emms-playlist-buffers)))) emms-playlist-buffers) (defun emms-playlist-current-kill () "Kill the current EMMS playlist buffer and switch to the next one." (interactive) (when (buffer-live-p emms-playlist-buffer) (let ((new (cadr (emms-playlist-buffer-list)))) (if new (let ((old emms-playlist-buffer)) (setq emms-playlist-buffer new emms-playlist-buffers (cdr emms-playlist-buffers)) (kill-buffer old) (switch-to-buffer emms-playlist-buffer)) (with-current-buffer emms-playlist-buffer (bury-buffer)))))) (defun emms-playlist-current-clear () "Clear the current playlist. If no current playlist exists, a new one is generated." (interactive) (if (or (not emms-playlist-buffer) (not (buffer-live-p emms-playlist-buffer))) (setq emms-playlist-buffer (emms-playlist-new)) (with-current-buffer emms-playlist-buffer (emms-playlist-clear)))) (defun emms-playlist-clear () "Clear the current buffer. If no playlist exists, a new one is generated." (interactive) (emms-playlist-ensure-playlist-buffer) (let ((inhibit-read-only t)) (widen) (delete-region (point-min) (point-max))) (run-hooks 'emms-playlist-cleared-hook)) ;;; Point movement within the playlist buffer. (defun emms-playlist-track-at (&optional pos) "Return the track at POS (point if not given), or nil if none." (emms-playlist-ensure-playlist-buffer) (emms-with-widened-buffer (get-text-property (or pos (point)) 'emms-track))) (defun emms-playlist-next () "Move to the next track in the current buffer." (emms-playlist-ensure-playlist-buffer) (let ((next (next-single-property-change (point) 'emms-track))) (when (not next) (error "No next track")) (when (not (emms-playlist-track-at next)) (setq next (next-single-property-change next 'emms-track))) (when (or (not next) (= next (point-max))) (error "No next track")) (goto-char next))) (defun emms-playlist-previous () "Move to the previous track in the current buffer." (emms-playlist-ensure-playlist-buffer) (let ((prev (previous-single-property-change (point) 'emms-track))) (when (not prev) (error "No previous track")) (when (not (get-text-property prev 'emms-track)) (setq prev (or (previous-single-property-change prev 'emms-track) (point-min)))) (when (or (not prev) (not (get-text-property prev 'emms-track))) (error "No previous track")) (goto-char prev))) (defun emms-playlist-first () "Move to the first track in the current buffer." (emms-playlist-ensure-playlist-buffer) (let ((first (condition-case nil (save-excursion (goto-char (point-min)) (when (not (emms-playlist-track-at (point))) (emms-playlist-next)) (point)) (error nil)))) (if first (goto-char first) (error "No first track")))) (defun emms-playlist-last () "Move to the last track in the current buffer." (emms-playlist-ensure-playlist-buffer) (let ((last (condition-case nil (save-excursion (goto-char (point-max)) (emms-playlist-previous) (point)) (error nil)))) (if last (goto-char last) (error "No last track")))) (defun emms-playlist-delete-track () "Delete the track at point." (emms-playlist-ensure-playlist-buffer) (funcall emms-playlist-delete-track-function)) ;;; Track selection (defun emms-playlist-selected-track () "Return the currently selected track." (emms-playlist-ensure-playlist-buffer) (when emms-playlist-selected-marker (emms-playlist-track-at emms-playlist-selected-marker))) (defun emms-playlist-current-selected-track () "Return the currently selected track in the current playlist." (with-current-emms-playlist (emms-playlist-selected-track))) (defun emms-playlist-selected-track-at-p (&optional point) "Return non-nil if POINT (defaulting to point) is on the selected track." (when emms-playlist-selected-marker (or (= emms-playlist-selected-marker (or point (point))) (let ((p (previous-single-property-change (or point (point)) 'emms-track))) (when p (= emms-playlist-selected-marker p)))))) (defun emms-playlist-select (pos) "Select the track at POS." (emms-playlist-ensure-playlist-buffer) (when (not (emms-playlist-track-at pos)) (error "No track at position %s" pos)) (when (not emms-playlist-selected-marker) (setq emms-playlist-selected-marker (make-marker))) (set-marker-insertion-type emms-playlist-selected-marker t) (set-marker emms-playlist-selected-marker pos) (run-hooks 'emms-playlist-selection-changed-hook)) (defun emms-playlist-select-next () "Select the next track in the current buffer." (emms-playlist-ensure-playlist-buffer) (save-excursion (goto-char (if (and emms-playlist-selected-marker (marker-position emms-playlist-selected-marker)) emms-playlist-selected-marker (point-min))) (condition-case nil (progn (if emms-repeat-playlist (condition-case nil (emms-playlist-next) (error (emms-playlist-first))) (emms-playlist-next)) (emms-playlist-select (point))) (error (error "No next track in playlist"))))) (defun emms-playlist-current-select-next () "Select the next track in the current playlist." (with-current-emms-playlist (emms-playlist-select-next))) (defun emms-playlist-select-previous () "Select the previous track in the current buffer." (emms-playlist-ensure-playlist-buffer) (save-excursion (goto-char (if (and emms-playlist-selected-marker (marker-position emms-playlist-selected-marker)) emms-playlist-selected-marker (point-max))) (condition-case nil (progn (if emms-repeat-playlist (condition-case nil (emms-playlist-previous) (error (emms-playlist-last))) (emms-playlist-previous)) (emms-playlist-select (point))) (error (error "No previous track in playlist"))))) (defun emms-playlist-current-select-previous () "Select the previous track in the current playlist." (with-current-emms-playlist (emms-playlist-select-previous))) (defun emms-playlist-select-random () "Select a random track in the current buffer." (emms-playlist-ensure-playlist-buffer) ;; FIXME: This is rather inefficient. (save-excursion (let ((track-indices nil)) (goto-char (point-min)) (emms-walk-tracks (setq track-indices (cons (point) track-indices))) (setq track-indices (vconcat track-indices)) (emms-playlist-select (aref track-indices (random (length track-indices))))))) (defun emms-playlist-current-select-random () "Select a random track in the current playlist." (with-current-emms-playlist (emms-playlist-select-random))) (defun emms-playlist-select-first () "Select the first track in the current buffer." (emms-playlist-ensure-playlist-buffer) (save-excursion (emms-playlist-first) (emms-playlist-select (point)))) (defun emms-playlist-current-select-first () "Select the first track in the current playlist." (with-current-emms-playlist (emms-playlist-select-first))) (defun emms-playlist-select-last () "Select the last track in the current buffer." (emms-playlist-ensure-playlist-buffer) (save-excursion (emms-playlist-last) (emms-playlist-select (point)))) (defun emms-playlist-current-select-last () "Select the last track in the current playlist." (with-current-emms-playlist (emms-playlist-select-last))) ;;; Playlist manipulation (defun emms-playlist-insert-track (track) "Insert TRACK at the current position into the playlist. This uses `emms-playlist-insert-track-function'." (emms-playlist-ensure-playlist-buffer) (funcall emms-playlist-insert-track-function track)) (defun emms-playlist-update-track () "Update TRACK at point. This uses `emms-playlist-update-track-function'." (emms-playlist-ensure-playlist-buffer) (funcall emms-playlist-update-track-function)) (defun emms-playlist-insert-source (source &rest args) "Insert tracks from SOURCE, supplying ARGS as arguments." (emms-playlist-ensure-playlist-buffer) (save-restriction (narrow-to-region (point) (point)) (apply source args) (run-hooks 'emms-playlist-source-inserted-hook))) (defun emms-playlist-current-insert-source (source &rest args) "Insert tracks from SOURCE in the current playlist. This is supplying ARGS as arguments to the source." (with-current-emms-playlist (apply 'emms-playlist-insert-source source args))) (defun emms-playlist-tracks-in-region (beg end) "Return all tracks between BEG and END." (emms-playlist-ensure-playlist-buffer) (let ((tracks nil)) (save-restriction (narrow-to-region beg end) (goto-char (point-min)) (emms-walk-tracks (setq tracks (cons (emms-playlist-track-at (point)) tracks)))) tracks)) (defun emms-playlist-track-updated (track) "Update TRACK in all playlist buffers." (mapc (lambda (buf) (with-current-buffer buf (when emms-playlist-buffer-p (save-excursion (let ((pos (text-property-any (point-min) (point-max) 'emms-track track))) (while pos (goto-char pos) (emms-playlist-update-track) (setq pos (text-property-any (next-single-property-change (point) 'emms-track) (point-max) 'emms-track track)))))))) (buffer-list)) t) ;;; Simple playlist buffer (defun emms-playlist-simple-insert-track (track) "Insert the description of TRACK at point." (emms-playlist-ensure-playlist-buffer) (let ((inhibit-read-only t)) (insert (emms-propertize (emms-track-force-description track) 'emms-track track) "\n"))) (defun emms-playlist-simple-update-track () "Update the track at point. Since we don't do anything special with the track anyway, just ignore this." nil) (defun emms-playlist-simple-delete-track () "Delete the track at point." (emms-playlist-ensure-playlist-buffer) (when (not (emms-playlist-track-at (point))) (error "No track at point")) (let ((inhibit-read-only t) (region (emms-property-region (point) 'emms-track))) (delete-region (car region) (cdr region)))) (defun emms-playlist-simple-shuffle () "Shuffle the whole playlist buffer." (emms-playlist-ensure-playlist-buffer) (let ((inhibit-read-only t) (current nil)) (widen) (when emms-player-playing-p (setq current (emms-playlist-selected-track)) (goto-char emms-playlist-selected-marker) (emms-playlist-delete-track)) (let* ((tracks (vconcat (emms-playlist-tracks-in-region (point-min) (point-max)))) (len (length tracks)) (i 0)) (delete-region (point-min) (point-max)) (run-hooks 'emms-playlist-cleared-hook) (emms-shuffle-vector tracks) (when current (emms-playlist-insert-track current)) (while (< i len) (emms-playlist-insert-track (aref tracks i)) (setq i (1+ i)))) (emms-playlist-select-first) (goto-char (point-max)))) (defun emms-playlist-simple-sort () "Sort the whole playlist buffer." (emms-playlist-ensure-playlist-buffer) (widen) (let ((inhibit-read-only t) (current (emms-playlist-selected-track)) (tracks (emms-playlist-tracks-in-region (point-min) (point-max)))) (delete-region (point-min) (point-max)) (run-hooks 'emms-playlist-cleared-hook) (mapc 'emms-playlist-insert-track (sort tracks emms-sort-lessp-function)) (let ((pos (text-property-any (point-min) (point-max) 'emms-track current))) (if pos (emms-playlist-select pos) (emms-playlist-first))))) (defun emms-uniq-list (list stringify) "Compare stringfied element of list, and remove duplicate elements." ;; This uses a fast append list, keeping a pointer to the last cons ;; cell of the list (TAIL). It might be worthwhile to provide an ;; abstraction for this eventually. (let* ((hash (make-hash-table :test 'equal)) (result (cons nil nil)) (tail result)) (dolist (element list) (let ((str (funcall stringify element))) (when (not (gethash str hash)) (setcdr tail (cons element nil)) (setq tail (cdr tail))) (puthash str t hash))) (cdr result))) (defun emms-playlist-simple-uniq () "Remove duplicate tracks." ;; TODO: This seems unnecessarily destructive. (emms-playlist-ensure-playlist-buffer) (widen) (let ((inhibit-read-only t) (current (emms-playlist-selected-track)) (tracks (emms-playlist-tracks-in-region (point-min) (point-max)))) (delete-region (point-min) (point-max)) (run-hooks 'emms-playlist-cleared-hook) (mapc 'emms-playlist-insert-track (nreverse (emms-uniq-list tracks 'emms-track-name))) (let ((pos (text-property-any (point-min) (point-max) 'emms-track current))) (if pos (emms-playlist-select pos) (emms-playlist-first))))) ;;; Helper functions (defun emms-property-region (pos prop) "Return a pair of the beginning and end of the property PROP at POS. If POS does not contain PROP, try to find PROP just before POS." (let (begin end) (if (and (> pos (point-min)) (get-text-property (1- pos) prop)) (setq begin (previous-single-property-change (1- pos) prop)) (if (get-text-property pos prop) (setq begin pos) (error "Cannot find the %s property at the given position" prop))) (if (get-text-property pos prop) (setq end (next-single-property-change pos prop)) (if (and (> pos (point-min)) (get-text-property (1- pos) prop)) (setq end pos) (error "Cannot find the %s property at the given position" prop))) (cons (or begin (point-min)) (or end (point-max))))) (defun emms-shuffle-vector (vector) "Shuffle VECTOR." (let ((i (- (length vector) 1))) (while (>= i 0) (let* ((r (random (1+ i))) (old (aref vector r))) (aset vector r (aref vector i)) (aset vector i old)) (setq i (- i 1)))) vector) ;;; Sources ;; A source is just a function which is called in a playlist buffer. ;; It should use `emms-playlist-insert-track' to insert the tracks it ;; knows about. ;; ;; The define-emms-source macro also defines functions ;; emms-play-SOURCE and emms-add-SOURCE. The former will replace the ;; current playlist, while the latter will add to the end. (defmacro define-emms-source (name arglist &rest body) "Define a new EMMS source called NAME. This macro defines three functions: `emms-source-NAME', `emms-play-NAME' and `emms-add-NAME'. BODY should use `emms-playlist-insert-track' to insert all tracks to be played, which is exactly what `emms-source-NAME' will do. The other two functions will be simple wrappers around `emms-source-NAME'; any `interactive' form that you specify in BODY will end up in these. See emms-source-file.el for some examples." (let ((source-name (intern (format "emms-source-%s" name))) (source-play (intern (format "emms-play-%s" name))) (source-add (intern (format "emms-add-%s" name))) (source-insert (intern (format "emms-insert-%s" name))) (docstring "A source of tracks for EMMS.") (interactive nil) (call-args (delete '&rest (delete '&optional arglist)))) (when (stringp (car body)) (setq docstring (car body) body (cdr body))) (when (eq 'interactive (caar body)) (setq interactive (car body) body (cdr body))) `(progn (defun ,source-name ,arglist ,docstring ,@body) (defun ,source-play ,arglist ,docstring ,interactive (if current-prefix-arg (let ((current-prefix-arg nil)) (emms-source-add ',source-name ,@call-args)) (emms-source-play ',source-name ,@call-args))) (defun ,source-add ,arglist ,docstring ,interactive (if current-prefix-arg (let ((current-prefix-arg nil)) (emms-source-play ',source-name ,@call-args)) (emms-source-add ',source-name ,@call-args))) (defun ,source-insert ,arglist ,docstring ,interactive (emms-source-insert ',source-name ,@call-args))))) (defun emms-source-play (source &rest args) "Play the tracks of SOURCE, after first clearing the EMMS playlist." (emms-stop) (emms-playlist-current-clear) (apply 'emms-playlist-current-insert-source source args) (emms-playlist-current-select-first) (emms-start)) (defun emms-source-add (source &rest args) "Add the tracks of SOURCE at the current position in the playlist." (with-current-emms-playlist (save-excursion (goto-char (point-max)) (apply 'emms-playlist-current-insert-source source args)) (when (or (not emms-playlist-selected-marker) (not (marker-position emms-playlist-selected-marker))) (emms-playlist-select-first)))) (defun emms-source-insert (source &rest args) "Insert the tracks from SOURCE in the current buffer." (if (not emms-playlist-buffer-p) (error "Not in an EMMS playlist buffer") (apply 'emms-playlist-insert-source source args))) ;;; User-defined playlists ;;; FIXME: Shuffle is bogus here! (because of narrowing) (defmacro define-emms-combined-source (name shufflep sources) "Define a `emms-play-X' and `emms-add-X' function for SOURCES." `(define-emms-source ,name () "An EMMS source for a tracklist." (interactive) (mapc (lambda (source) (apply (car source) (cdr source))) ,sources) ,(when shufflep '(save-restriction (widen) (emms-shuffle))))) ;;; Players ;; A player is a data structure created by `emms-player'. ;; See the docstring of that function for more information. (defvar emms-player-stopped-p nil "Non-nil if the last EMMS player was stopped by the user.") (defun emms-player (start stop playablep) "Create a new EMMS player. The start function will be START, and the stop function STOP. PLAYABLEP should return non-nil for tracks that this player can play. When trying to play a track, EMMS walks through `emms-player-list'. For each player, it calls the PLAYABLEP function. The player corresponding to the first PLAYABLEP function that returns non-nil is used to play the track. To actually play the track, EMMS calls the START function, passing the chosen track as a parameter. If the user tells EMMS to stop playing, the STOP function is called. Once the player has finished playing, it should call `emms-player-stopped' to let EMMS know." (let ((p (emms-dictionary '*player*))) (emms-player-set p 'start start) (emms-player-set p 'stop stop) (emms-player-set p 'playablep playablep) p)) (defun emms-player-get (player name &optional inexistent) "Return the value of entry NAME in PLAYER." (let ((p (if (symbolp player) (symbol-value player) player))) (emms-dictionary-get p name inexistent))) (defun emms-player-set (player name value) "Set the value of entry NAME in PLAYER to VALUE." (let ((p (if (symbolp player) (symbol-value player) player))) (emms-dictionary-set p name value))) (defun emms-player-for (track) "Return an EMMS player capable of playing TRACK. This will be the first player whose PLAYABLEP function returns non-nil, or nil if no such player exists." (let ((lis emms-player-list)) (while (and lis (not (funcall (emms-player-get (car lis) 'playablep) track))) (setq lis (cdr lis))) (if lis (car lis) nil))) (defun emms-player-start (track) "Start playing TRACK." (if emms-player-playing-p (error "A player is already playing") (let ((player (emms-player-for track))) (if (not player) (error "Don't know how to play track: %S" track) ;; Change default-directory so we don't accidentally block any ;; directories the current buffer was visiting. (let ((default-directory "/")) (funcall (emms-player-get player 'start) track)))))) (defun emms-player-started (player) "Declare that the given EMMS PLAYER has started. This should only be done by the current player itself." (setq emms-player-playing-p player emms-player-paused-p nil) (run-hooks 'emms-player-started-hook)) (defun emms-player-stop () "Stop the current EMMS player." (when emms-player-playing-p (let ((emms-player-stopped-p t)) (funcall (emms-player-get emms-player-playing-p 'stop))) (setq emms-player-playing-p nil))) (defun emms-player-stopped () "Declare that the current EMMS player is finished. This should only be done by the current player itself." (setq emms-player-playing-p nil) (if emms-player-stopped-p (run-hooks 'emms-player-stopped-hook) (sleep-for emms-player-delay) (run-hooks 'emms-player-finished-hook) (funcall emms-player-next-function))) (defun emms-player-pause () "Pause the current EMMS player." (cond ((not emms-player-playing-p) (error "Can't pause player, nothing is playing")) (emms-player-paused-p (let ((resume (emms-player-get emms-player-playing-p 'resume)) (pause (emms-player-get emms-player-playing-p 'pause))) (cond (resume (funcall resume)) (pause (funcall pause)) (t (error "Player does not know how to pause")))) (setq emms-player-paused-p nil) (run-hooks 'emms-player-paused-hook)) (t (let ((pause (emms-player-get emms-player-playing-p 'pause))) (if pause (funcall pause) (error "Player does not know how to pause"))) (setq emms-player-paused-p t) (run-hooks 'emms-player-paused-hook)))) (defun emms-player-seek (seconds) "Seek the current player by SECONDS seconds. This can be a floating point number for fractions of a second, or negative to seek backwards." (if (not emms-player-playing-p) (error "Can't seek player, nothing playing right now") (let ((seek (emms-player-get emms-player-playing-p 'seek))) (if (not seek) (error "Player does not know how to seek") (funcall seek seconds) (run-hook-with-args 'emms-player-seeked-functions seconds))))) (defun emms-player-seek-to (seconds) "Seek the current player to SECONDS seconds. This can be a floating point number for fractions of a second, or negative to seek backwards." (if (not emms-player-playing-p) (error "Can't seek-to player, nothing playing right now") (let ((seek (emms-player-get emms-player-playing-p 'seek-to))) (if (not seek) (error "Player does not know how to seek-to") (funcall seek seconds) (run-hook-with-args 'emms-player-time-set-functions seconds))))) (provide 'emms) ;;; emms.el ends here ���������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/jack.el����������������������������������������������������0000664�0000000�0000000�00000030741�12205151475�0020270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; jack.el --- Jack Audio Connection Kit support ;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Mario Lang <mlang@delysid.org> ;; Keywords: multimedia, processes ;; This file 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, or (at your option) ;; any later version. ;; This file 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 GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; JACK is a low-latency audio server, written for POSIX conformant ;; operating systems such as GNU/Linux and Apple's OS X. It can connect a ;; number of different applications to an audio device, as well as ;; allowing them to share audio between themselves. Its clients can run in ;; their own processes (ie. as normal applications), or they can run ;; within the JACK server (ie. as a "plugin"). ;; ;; JACK was designed from the ground up for professional audio work, and ;; its design focuses on two key areas: synchronous execution of all ;; clients, and low latency operation. ;; ;; jack.el provides a fascility for starting jackd from within Emacs. ;; It also povides convenience functions for prompting the user for ;; jack client and port names in the minibuffer, as well as the ;; functions `jack-connect' and `jack-disconnect' which can be used to ;; rearrange jack port wiring with a minimum of keystrokes. ;;; Code: (require 'emms-compat) (defgroup jack () "Jack Audio Connection Kit" :group 'processes) (defcustom jack-rc '("~/.jackdrc" "/etc/jackd.conf") "*JACK run control paths." :group 'jack :type 'repeat) (defcustom jack-use-jack-rc t "*If non-nil, try to retrieve jack startup arguments from run control files listed in `jack-rc'. If no rc file is found or this variable is set to nil, use the Emacs variables to build the startup args." :group 'jack :type 'boolean) (defcustom jack-program (executable-find "jackd") "*JACK executable path." :group 'jack :type 'file) (defcustom jack-sample-rate 44100 "*Default sampling rate for JACK." :group 'jack :type 'integer) (defcustom jack-period-size 128 "*Period size to use when launching new JACK process." :group 'jack :type 'integer) (defcustom jack-alsa-device nil "*ALSA soundcard to use." :group 'jack :type '(choice (const :tag "Ask" nil) string)) (defun jack-read-alsa-device () "Read an ALSA device name using the minibuffer." (let (cards) (with-temp-buffer (insert-file-contents "/proc/asound/cards") (while (not (eobp)) (if (looking-at "^\\([0-9]\\) \\[.+\\]: \\(.+\\)\n +\\(.*\\)$") (setq cards (append (list (cons (match-string 3) (match-string 1))) cards))) (forward-line 1))) (concat "hw:" (cdr (assoc (completing-read "Card: " cards nil t) cards))))) (defun jack-alsa-device () (or jack-alsa-device (jack-read-alsa-device))) (defcustom jack-output-buffer-name "*JACK output*" "*Output buffer name." :group 'jack :type 'string) (defun jack-args () "Return a list of startup arguments to use. First element is the executable path." (or (and jack-use-jack-rc (catch 'rc-found (let ((files (mapcar 'expand-file-name jack-rc))) (while files (if (file-exists-p (car files)) (with-temp-buffer (insert-file-contents (car files)) (when (> (buffer-size) 0) (throw 'rc-found (split-string (buffer-string) "[\n \t]+"))))) (setq files (cdr files)))) nil)) (list jack-program "-v" "-R" "-dalsa" (format "-d%s" (jack-alsa-device)) (format "-r%d" jack-sample-rate) (format "-p%d" jack-period-size)))) (defcustom jack-set-rtlimits t "*Use set_rtlimits (if available) to gain realtime priorities if -R is given in jackd command-line." :group 'jack :type 'boolean) (defcustom jack-set-rtlimits-program (executable-find "set_rtlimits") "*Path to set_rtlimits." :group 'jack :type 'file) (defun jack-maybe-rtlimits (args) (if (and jack-set-rtlimits (or (member "-R" args) (member "--realtime" args)) (file-exists-p jack-set-rtlimits-program)) (append (list jack-set-rtlimits-program "-r") args) args)) (defvar jack-process nil) (defvar jack-load 0) (defvar jack-max-usecs 0) (defvar jack-spare 0) (defun jack-output-buffer () (or (get-buffer jack-output-buffer-name) (with-current-buffer (get-buffer-create jack-output-buffer-name) (setq major-mode 'jack-mode mode-name "JACK" mode-line-format (copy-tree mode-line-format)) (setcar (nthcdr 16 mode-line-format) `(:eval (format "load:%.2f" jack-load))) (add-hook 'kill-buffer-hook 'jack-kill nil t) (current-buffer)))) (defvar jack-xruns nil) (defun jack-filter (proc string) (with-current-buffer (process-buffer proc) (let ((moving (= (point) (process-mark proc)))) (save-excursion (save-match-data (if (string-match "^load = \\([^ ]+\\) max usecs: \\([^,]+\\), spare = \\(.+\\)$" string) (setq jack-load (string-to-number (match-string 1 string)) jack-max-usecs (string-to-number (match-string 2 string)) jack-spare (string-to-number (match-string 3 string))) (if (string-match "^**** alsa_pcm: xrun of at least \\([^ ]+\\) msecs$" string) (push (string-to-number (match-string 1 string)) jack-xruns) (goto-char (process-mark proc)) (insert string) (set-marker (process-mark proc) (point)))))) (when moving (goto-char (process-mark proc)))))) (defun jack-running-p () (and jack-process (processp jack-process) (eq (process-status jack-process) 'run))) (defcustom jack-started-hook nil "*Hook run when `jack-start' successfully started a new JACK intance." :group 'jack :type 'hook) (defun jack-start () "Start the JACK process." (interactive) (if (jack-running-p) (error "JACK already running") (setq jack-process (apply 'start-process "jack" (jack-output-buffer) (jack-maybe-rtlimits (jack-args)))) (set-process-filter jack-process #'jack-filter) (run-hooks 'jack-started-hook) (switch-to-buffer (jack-output-buffer)))) (defun jack-kill () "Kill the currently running JACK process." (interactive) (when (jack-running-p) (delete-process jack-process)) (setq jack-process nil)) (defun jack-restart () "Restart JACK." (interactive) (if (jack-running-p) (jack-kill)) (sit-for 0) (jack-start)) (defun jack-list () "Retrieve a list of JACK clients/ports." (with-temp-buffer (call-process "jack_lsp" nil t nil "-cpl") (goto-char (point-min)) (let (result current-port) (while (not (eobp)) (cond ((looking-at "^\\([^ \t:]+\\):\\(.+\\)$") (let ((program (match-string 1)) (port (match-string 2))) (if (assoc program result) (setcdr (assoc program result) (append (cdr (assoc program result)) (list (setq current-port (list port))))) (setq result (append (list (list program (setq current-port (list port)))) result))))) ((looking-at "^ \\([^ \t:]+\\):\\(.+\\)$") (if (assoc 'connections (cdr current-port)) (setcdr (assoc 'connections (cdr current-port)) (append (cdr (assoc 'connections current-port)) (list (list (match-string 1) (match-string 2))))) (setcdr current-port (append (list (list 'connections (list (match-string 1) (match-string 2)))) (cdr current-port))))) ((looking-at "^\tproperties: \\(.+\\),$") (setcdr current-port (append (list (append (list 'properties) (mapcar #'intern (split-string (match-string 1) ",")))) (cdr current-port))))) (forward-line 1)) result))) (defun jack-ports (program) (cdr (assoc program (jack-list)))) (defun jack-get-port-connections (program port) (cdr (assoc 'connections (cdr (assoc port (jack-ports program)))))) (defun jack-get-port-properties (program port) (cdr (assoc 'properties (cdr (assoc port (jack-ports program)))))) (defun jack-get-direction (program port) (let ((props (jack-get-port-properties program port))) (or (car (member 'output props)) (car (member 'input props)) (error "Neither input nor output port")))) (defun jack-read-program (prompt &optional predicate) (let ((progs (if (functionp predicate) (emms-remove-if-not predicate (jack-list)) (jack-list)))) (unless progs (error "No matching JACK clients found")) (if (< (length progs) 2) (caar progs) (completing-read prompt progs nil t)))) (defun jack-unique-port-name (strings) (let ((start "") (maxlen (apply 'min (mapcar #'length strings)))) (while (and (< (length start) maxlen) (catch 'not-ok (let ((nextchar (substring (car strings) (length start) (1+ (length start))))) (mapc (lambda (str) (unless (string= (concat start nextchar) (substring str 0 (1+ (length start)))) (throw 'not-ok nil))) strings) t))) (setq start (substring (car strings) 0 (1+ (length start))))) start)) (defun jack-read-port (program prompt &optional predicate) (let ((ports (if (functionp predicate) (emms-remove-if-not predicate (jack-ports program)) (jack-ports program)))) (if (< (length ports) 2) (caar ports) (completing-read prompt ports nil t (jack-unique-port-name (mapcar 'car ports)))))) (defun jack-connect (from-program from-port to-program to-port) "Connect FROM-PROGRAM's output port FROM-PORT to TO-PROGRAM's input port TO-PORT. If called interactively, the direction does not matter." (interactive (let* ((prog (jack-read-program "Connect: ")) (port (jack-read-port prog (format "Connect %s port: " prog))) (to-type (if (eq (jack-get-direction prog port) 'input) 'output 'input)) (to-prog (jack-read-program (format "Connect %s port %s to: " prog port) (lambda (prog) (emms-find-if (lambda (port) (member to-type (assoc 'properties (cdr port)))) (cdr prog))))) (to-port (jack-read-port to-prog (format "Connect %s port %s to %s port: " prog port to-prog) (lambda (port) (member to-type (cdr (assoc 'properties (cdr port)))))))) (if (eq to-type 'input) (list prog port to-prog to-port) (list to-prog to-port prog port)))) (let ((result (call-process "jack_connect" nil nil nil (format "%s:%s" from-program from-port) (format "%s:%s" to-program to-port)))) (if (= result 0) (message "JACK: Connected %s:%s to %s:%s" from-program from-port to-program to-port)))) (defun jack-disconnect (from-program from-port to-program to-port) "Disconnect FROM-PROGRAM's output port FROM-PORT from TO-PROGRAM's input port TO-PORT. If called interactively, the direction is not relevant." (interactive (let* ((prog (jack-read-program "Disconnect: " (lambda (prog) (emms-find-if (lambda (port) (assoc 'connections (cdr port))) (cdr prog))))) (port (jack-read-port prog (format "Disconnect %s port: " prog) (lambda (port) (assoc 'connections (cdr port))))) (connections (jack-get-port-connections prog port)) (from (list prog port)) (to (if (< (length connections) 2) (car connections) (let* ((to-progs (let (result) (mapc (lambda (conn) (if (not (member (car conn) result)) (setq result (append (list (car conn)) result)))) connections) (mapcar #'list result))) (to-prog (if (< (length to-progs) 2) (caar to-progs) (completing-read (format "Disconnect %s port %s from: " prog port) to-progs nil t)))) (setq connections (emms-remove-if-not (lambda (conn) (string= (car conn) to-prog)) connections)) (if (< (length connections) 2) (car connections) (let ((to-port (completing-read (format "Disconnect %s port %s from %s port: " prog port to-prog) (mapcar #'cdr connections) nil t))) (list to-prog to-port))))))) (if (eq (jack-get-direction prog port) 'output) (append from to) (append to from)))) (let ((result (call-process "jack_disconnect" nil nil nil (format "%s:%s" from-program from-port) (format "%s:%s" to-program to-port)))) (if (= result 0) (message "JACK: Disconnected %s:%s from %s:%s" from-program from-port to-program to-port)))) (provide 'jack) ;;; jack.el ends here �������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/later-do.el������������������������������������������������0000664�0000000�0000000�00000005114�12205151475�0021063�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; later-do.el --- execute lisp code ... later ;; Copyright (C) 2004, 2005, 2006, 2007, 2008, ;; 2009 Free Software Foundation, Inc. ;; Author: Jorgen Schaefer <forcer@forcix.cx> ;;; 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, write to the Free Software ;;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ;;; 02110-1301 USA ;;; Commentary ;; This file will execute lisp code ``later on''. This way it is ;; possible to work while elisp does some longer calculations, if you ;; can convert those calculations into a sequence of function calls. ;;; Code: (defvar later-do-version "0.2emms2 (2005-09-20)" "Version string of later-do.") (defgroup later-do nil "*Running functions ... later!" :prefix "later-do-" :group 'development) (defcustom later-do-interval 0.5 "How many seconds to wait between running events." :group 'later-do :type 'number) (defvar later-do-list nil "A list of functions to be called later on.") (defvar later-do-timer nil "The timer that later-do uses.") (defun later-do (function &rest args) "Apply FUNCTION to ARGS later on. This is an unspecified amount of time after this call, and definitely not while lisp is still executing. Code added using `later-do' is guaranteed to be executed in the sequence it was added." (setq later-do-list (nconc later-do-list (list (cons function args)))) (unless later-do-timer (setq later-do-timer (run-with-timer later-do-interval nil 'later-do-timer)))) (defun later-do-timer () "Run the next element in `later-do-list', or do nothing if it's empty." (if (null later-do-list) (setq later-do-timer nil) (let ((fun (caar later-do-list)) (args (cdar later-do-list))) (setq later-do-list (cdr later-do-list)) (unwind-protect (apply fun args) (setq later-do-timer (run-with-timer later-do-interval nil 'later-do-timer)))))) (provide 'later-do) ;;; later-do.el ends here ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/lisp/tq.el������������������������������������������������������0000664�0000000�0000000�00000014160�12205151475�0020001�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������;;; tq.el --- utility to maintain a transaction queue ;; Copyright (C) 1985, 1986, 1987, 1992, 2001, 2002, 2003, 2004, ;; 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. ;; Author: Scott Draves <spot@cs.cmu.edu> ;; Maintainer: FSF ;; Adapted-By: ESR ;; Keywords: extensions ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>. ;;; Commentary: ;; This file manages receiving a stream asynchronously, parsing it ;; into transactions, and then calling the associated handler function ;; upon the completion of each transaction. ;; Our basic structure is the queue/process/buffer triple. Each entry ;; of the queue part is a list of question, regexp, closure, and ;; function that is consed to the last element. ;; A transaction queue may be created by calling `tq-create'. ;; A request may be added to the queue by calling `tq-enqueue'. If ;; the `delay-question' argument is non-nil, we will wait to send the ;; question to the process until it has finished sending other input. ;; Otherwise, once a request is enqueued, we send the given question ;; immediately to the process. ;; We then buffer bytes from the process until we see the regexp that ;; was provided in the call to `tq-enqueue'. Then we call the ;; provided function with the closure and the collected bytes. If we ;; have indicated that the question from the next transaction was not ;; sent immediately, send it at this point, awaiting the response. ;;; Code: ;;; Accessors ;; This part looks like (queue . (process . buffer)) (defun tq-queue (tq) (car tq)) (defun tq-process (tq) (car (cdr tq))) (defun tq-buffer (tq) (cdr (cdr tq))) ;; The structure of `queue' is as follows ;; ((question regexp closure . fn) ;; <other queue entries>) ;; question: string to send to the process (defun tq-queue-head-question (tq) (car (car (tq-queue tq)))) ;; regexp: regular expression that matches the end of a response from ;; the process (defun tq-queue-head-regexp (tq) (car (cdr (car (tq-queue tq))))) ;; closure: additional data to pass to the function (defun tq-queue-head-closure (tq) (car (cdr (cdr (car (tq-queue tq)))))) ;; fn: function to call upon receiving a complete response from the ;; process (defun tq-queue-head-fn (tq) (cdr (cdr (cdr (car (tq-queue tq)))))) ;; Determine whether queue is empty (defun tq-queue-empty (tq) (not (tq-queue tq))) ;;; Core functionality ;;;###autoload (defun tq-create (process) "Create and return a transaction queue communicating with PROCESS. PROCESS should be a subprocess capable of sending and receiving streams of bytes. It may be a local process, or it may be connected to a tcp server on another machine." (let ((tq (cons nil (cons process (generate-new-buffer (concat " tq-temp-" (process-name process))))))) (buffer-disable-undo (tq-buffer tq)) (set-process-filter process `(lambda (proc string) (tq-filter ',tq string))) tq)) (defun tq-queue-add (tq question re closure fn) (setcar tq (nconc (tq-queue tq) (cons (cons question (cons re (cons closure fn))) nil))) 'ok) (defun tq-queue-pop (tq) (setcar tq (cdr (car tq))) (let ((question (tq-queue-head-question tq))) (condition-case nil (process-send-string (tq-process tq) question) (error nil))) (null (car tq))) (defun tq-enqueue (tq question regexp closure fn &optional delay-question) "Add a transaction to transaction queue TQ. This sends the string QUESTION to the process that TQ communicates with. When the corresponding answer comes back, we call FN with two arguments: CLOSURE, which may contain additional data that FN needs, and the answer to the question. REGEXP is a regular expression to match the entire answer; that's how we tell where the answer ends. If DELAY-QUESTION is non-nil, delay sending this question until the process has finished replying to any previous questions. This produces more reliable results with some processes." (let ((sendp (or (not delay-question) (not (tq-queue tq))))) (tq-queue-add tq (unless sendp question) regexp closure fn) (when sendp (process-send-string (tq-process tq) question)))) (defun tq-close (tq) "Shut down transaction queue TQ, terminating the process." (delete-process (tq-process tq)) (kill-buffer (tq-buffer tq))) (defun tq-filter (tq string) "Append STRING to the TQ's buffer; then process the new data." (let ((buffer (tq-buffer tq))) (when (buffer-live-p buffer) (with-current-buffer buffer (goto-char (point-max)) (insert string) (tq-process-buffer tq))))) (defun tq-process-buffer (tq) "Check TQ's buffer for the regexp at the head of the queue." (let ((buffer (tq-buffer tq))) (when (buffer-live-p buffer) (set-buffer buffer) (if (= 0 (buffer-size)) () (if (tq-queue-empty tq) (let ((buf (generate-new-buffer "*spurious*"))) (copy-to-buffer buf (point-min) (point-max)) (delete-region (point-min) (point)) (pop-to-buffer buf nil) (error "Spurious communication from process %s, see buffer %s" (process-name (tq-process tq)) (buffer-name buf))) (goto-char (point-min)) (if (re-search-forward (tq-queue-head-regexp tq) nil t) (let ((answer (buffer-substring (point-min) (point)))) (delete-region (point-min) (point)) (unwind-protect (condition-case nil (funcall (tq-queue-head-fn tq) (tq-queue-head-closure tq) answer) (error nil)) (tq-queue-pop tq)) (tq-process-buffer tq)))))))) (provide 'tq) ;; arch-tag: 65dea08c-4edd-4cde-83a5-e8a15b993b79 ;;; tq.el ends here ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/src/������������������������������������������������������������0000775�0000000�0000000�00000000000�12205151475�0016651�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/src/emms-print-metadata.c���������������������������������������0000664�0000000�0000000�00000004204�12205151475�0022666�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* emms-print-metadata.c --- Info function for libtag Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. Author: Trent Buck <trentbuck@gmail.com> This file is part of EMMS. EMMS 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, or (at your option) any later version. EMMS 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 EMMS; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <stdlib.h> #include <stdio.h> #include <tag_c.h> int main (int argc, char **argv) { TagLib_File *file; TagLib_Tag *tag; if (argc != 2) { fprintf (stderr, "usage: emms-print-metadata file.{mp3,ogg,flac}\n"); exit (1); } file = taglib_file_new (argv[1]); if (!file) { fprintf (stderr, "%s: File does not exist or is of an unknown type\n", argv[1]); exit (1); } tag = taglib_file_tag (file); /* Apparently, if the file is named foo.mp3 or similar, the library still can open it, for whatever reason. */ if (!tag) { fprintf (stderr, "%s: File does not exist or is of an unknown type\n", argv[1]); exit (1); } printf ("info-artist=%s\n", taglib_tag_artist (tag)); printf ("info-title=%s\n", taglib_tag_title (tag)); printf ("info-album=%s\n", taglib_tag_album (tag)); printf ("info-tracknumber=%d\n", taglib_tag_track (tag)); printf ("info-year=%d\n", taglib_tag_year (tag)); printf ("info-genre=%s\n", taglib_tag_genre (tag)); printf ("info-note=%s\n", taglib_tag_comment (tag)); printf ("info-playing-time=%d\n", taglib_audioproperties_length (taglib_file_audioproperties (file))); taglib_tag_free_strings (); taglib_file_free (file); return 0; } /* emms-print-metadata.c ends here. */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������emms-3.0+20130821+1.git2fba618.orig/src/emms-print-metadata.pl��������������������������������������0000775�0000000�0000000�00000006245�12205151475�0023071�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl # emms-print-metadata.pl --- Info function for libtag # Copyright (C) 2012 Free Software Foundation, Inc. # Author: Lucas Bonnet <lbonnet@rincevent.net> # This file is part of EMMS. # EMMS 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, or (at your option) # any later version. # EMMS 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 EMMS; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. use strict; use warnings; use Audio::Scan; use File::Basename; # enable UTF-8 output binmode(STDOUT, ":utf8"); my $file = $ARGV[0]; # Scan without reading (possibly large) artwork into memory. # Instead of binary artwork data, the size of the artwork will be returned instead. local $ENV{AUDIO_SCAN_NO_ARTWORK} = 1; my $data = Audio::Scan->scan($file); # determines the names of tags according to file type my %ext_map = ("mp3" => {'artist' => 'TPE1', 'title' => 'TIT2', 'album' => 'TALB', 'tracknumber' => 'TRCK', 'composer' => 'TCOM', 'performer' => 'TPE2', 'year' => 'TDRC', 'genre' => 'TCON', 'comment' => 'COMM'}, "flc" => {'artist' => 'ARTIST', 'title' => 'TITLE', 'album' => 'ALBUM', 'tracknumber' => 'TRACKNUMBER', 'composer' => 'COMPOSER', 'performer' => 'PERFORMER', 'year' => 'DATE', 'genre' => 'GENRE', 'comment' => 'COMMENT'}, "ogg" => {'artist' => 'ARTIST', 'title' => 'TITLE', 'album' => 'ALBUM', 'tracknumber' => 'TRACKNUMBER', 'composer' => 'COMPOSER', 'performer' => 'PERFORMER', 'year' => 'DATE', 'genre' => 'GENRE', 'comment' => 'COMMENT'}, ); # find out extension my ($filename, $directories, $extension) = fileparse($file, qr/[^.]*/); my $type = Audio::Scan->type_for(lc($extension)); my $tag_map = $ext_map{$type}; # print tag info print "info-artist="; safe_print('artist'); print "info-title="; safe_print('title'); print "info-album="; safe_print('album'); print "info-tracknumber="; safe_print('tracknumber'); print "info-composer="; safe_print('composer'); print "info-performer="; safe_print('performer'); print "info-year="; safe_print('year'); print "info-genre="; safe_print('genre'); print "info-note=" ; safe_print('comment'); print "info-playing-time=",int($data->{'info'}->{'song_length_ms'} / 1000),"\n"; sub safe_print { my $k = shift; if (defined $data->{'tags'}->{ $tag_map->{$k} }) { print $data->{'tags'}->{ $tag_map->{$k} }; } else { print "<no $k>"; } print "\n"; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������