moosic-1.5.6/0000755000175000017500000000000011655460503013533 5ustar danieldaniel00000000000000moosic-1.5.6/ChangeLog0000644000175000017500000017022411655334373015320 0ustar danieldaniel00000000000000Sun 06 Nov 2011 - Copyright is unethical, so I have relinquished my intellectual monopoly over Moosic. Sat 05 Nov 2011 - The implementation of call_find in client/cli/dispatcher.py that uses subprocess.Popen does not need the directory name to be escaped for the shell, so sh_escape is only used for the old implementation. Wed 14 Sep 2011 - The change to moosic.client.factory.UnixStreamTransport in the last version fixed compatibility for Python 2.7, but broke it for Python 2.6. So the Python version is now detected and the appropriate httplib class instance is chosen accordingly. - moosic.client.cli.dispatcher.wipe() now sends a stop() message to the server instead of skip() since stop() behaves better with loop mode. - The "goto" and "gobackto" commands of the CLI client now respect the --ignore-case option. Thu 21 Jul 2011 - Version 1.5.5: Keeping up with API changes. Tue 19 Jul 2011 - moosic.server.main no longer imports SocketServer, since it's not actually used there nowadays. - Two bitrot bugs were fixed, one each in the client and the server. - The client bug was caused by using the now-deprecated httplib.HTTP class in UnixStreamTransport. HTTPConnection is now used directly instead. - The server bug was caused by SimpleXMLRPCServer.SimpleXMLRPCRequestHandler setting a variable called "disable_nagle_algorithm", which made SocketServer set socket options that are only valid for TCP sockets. Wed 20 May 2009 - The subprocess module is now used instead of os.popen, as the latter is deprecated as of Python 2.6. os.popen is still used as a backup if the subprocess module doesn't exist. - The popen2 module is no longer imported. It seems that it hadn't actually been used since some very old and long-lost version, if ever. Fri 13 Jul 2007 - Version 1.5.4: violation of OO principles finally bit me in the butt. Thu 12 Jul 2007 - The call to os.getpid() in the "quit()" signal handler function in moosic/server/main.py was corrected. The parentheses were missing, meaning that this wasn't actually a function call, but a reference to the function object itself, which really isn't what I meant. - The constructor for UnixMoosicServer and TcpMoosicServer in moosic/server/support.py now extends the constructor from SimpleXMLRPCServer instead of overriding it and duplicating its code. This prevents breakage should the internal structure of SimpleXMLRPCServer should ever change (as it did between Python 2.4 and Python 2.5). The incompatibility with Python 2.5 was brought to my attention by Jim Russell and its symptoms are detailed at https://bugs.launchpad.net/ubuntu/+source/moosic/+bug/95532. Fri 25 May 2007 - The -C option was added to the command-line client. - Version 1.5.3: You can still teach an old cow new tricks. Wed 17 May 2006 - The "move-pattern" moosic command was added. Tue 16 Aug 2005 - Version 1.5.2: Better late than never. Wed 01 Jun 2005 - A colon is no longer allowed as a bracketing character for a range string in utilities.parse_range(). - utilities.parse_range() now explicitly checks for an empty string as input and rejects such. Thu 19 May 2005 - utilities.parse_range() now (correctly) ignores bracketing characters at the start and end of an input range string to provide an easy alternative for preventing negative numbers from being interpreted as options. Similarly, the "insert", "pl-insert", and "move" commands also ignore bracketing characters around their index arguments. - The VERSION variable was moved from two separate instances in moosic/client/cli/__init__.py and moosic/server/__init__.py to a single instance in moosic/__init__.py. Wed 27 Apr 2005 - The "stagger" command of the command-line moosic client was fixed to adapt to the fact that staggered_merge() now returns a list instead of a tuple. Thu 14 Apr 2005 - A bug in the "unplayable filter" feature of the CLI client that duplicated items that matched more than one entry in the song handler configuration was fixed. Wed 06 Apr 2005 - The exception handling for loading the state file was tightened up, so uncaught exceptions should no longer occur there. Sun 27 Mar 2005 - staggered_merge() was rewritten for the pure fun of it. Now it's shorter, much easier to understand, and recursive. Lispy variable names are used in a tepid attempt to compensate for the lack of multitudinous parentheses. Sun 10 Oct 2004 - A patch from Nate Straz was applied to keep the server from saving state to disk if the state hasn't really changed. Fri 08 Oct 2004 - An off-by-one error in moosic.utilities.is_overlapping() was debugged. This mistake caused a false positive when the first number in one range was the same as the second number in the other range or vice versa. Fri 24 Sep 2004 - I've removed "greplist". It was silly. Tue 31 Aug 2004 - The "greplist" client command was added. I don't know if I'll keep it, since it's nearly as easy to type "moosic list | grep foo". Thu 19 Aug 2004 - API_MINOR_VERSION in moosic/server/methods.py should have been updated to 8 in the last version, but I must have forgotten. This has been fixed. Thanks to Florian Ragwitz for spotting this. Tue 20 Jul 2004 - moosic.server.main.play() now handles exceptions raised by its call to os.execvp(), thus fixing a terrible oversight that could cause the server to crash. - The server now logs the total playing time whenever it finishes playing a song. Mon 19 Jul 2004 - The is_overlapping() function was moved from moosic.server.methods to moosic.utilities. Fri 25 Jun 2004 - Version 1.5.1: Welcome to the wonderful world of distutils. Thu 24 Jun 2004 - The "swap" server method was completed. Thu 29 Apr 2004 - The "swap" command was implemented in the CLI client. (Todo: implement/document in server.) Tue 20 Apr 2004 - The "toggle-advance" command was added. - The moosic man page was updated to mention Audio CD support, and to document the new commands. - moosic_hackers.txt was updated to match the new code organization. Sun 18 Apr 2004 - The "interval-insert" command was implemented, and was renamed to "interval-add". - README.txt was updated to reflect the new installation process. Sat 17 Apr 2004 - The "replace" and "pl-replace" commands were added. - A stub for the "interval-insert" command was added, and needs to be implemented. Fri 02 Apr 2004 - The routine for reading playlist files now supports playlists that use relative pathnames. This support was inspired code submitted by michael d. ivey . Thu 01 Apr 2004 - moosic.server.daemonize.daemonize() now uses os._exit() after its second call to fork() so that when moosic.server.main.main() is invoked by another python program to start the server (like the moosic client), only a single SystemExit exception will be raised. - By default, the moosic client now filters out songs that the server doesn't know how to play before adding anything to the song queue. This behavior can be turned off with the -U/--allow-unplayable option. - A very heavily commented example configuration file is now distributed with the documentation. Wed 31 Mar 2004 - The server's logger was tweaked a bit. It no longer writes to stderr when an error is logged, because moosicd normally doesn't have a useful stderr associated with it. Also, the "message" parameter is used as the return value of a logging call to make it easier to reuse the log message. Tue 30 Mar 2004 - An effort was made to change the idiom of printing to stderr and then calling sys.exit(1) into an idiom of passing the error message directly to sys.exit(). This is not so important for the command-line client, but it's important for moosicd to behave this way so that startup failures can be easily caught by an invoking program. Mon 29 Mar 2004 - The modules that comprise the project were reorganized into packages. - The old Makefile-based build and install system was replaced with a standard, distutils-based setup.py script. - A Makefile is still used to translate the documentation from POD source format into various target formats. - The "moosic" client now uses a different method for automatically starting up the server. This new method doesn't use the $MOOSICD environment variable. Sun 28 Mar 2004 - daemonize.py was changed so that it doesn't reset the umask unless specifically requested. - The debian/ directory was removed, which means that support for building a .deb from the upstream source was removed. I've decided that it's too much trouble for me to stay synchronized with the official Debian package. - The experimental/ directory was removed because I'm not interested in developing the code within any further. - Version 1.5.0: Several incompatible changes. Best to get it over with all at once. Wed 24 Mar 2004 - README.developers now completely documents how to add moosic commands and moosicd methods. Tue 23 Mar 2004 - A lot of text was written for README.developers to describe how to add moosic commands and moosicd methods. It's still not quite yet finished, though. Mon 22 Mar 2004 - Various documentation files were updated in preparation for a new release. Wed 10 Mar 2004 - The repeated state-saving revealed a bug in data.getstate() which caused data.song_queue to be modified. getstate() was changed to make no modifications to any of data's attributes. Tue 09 Mar 2004 - The behavior of the previous() method was changed so that it doesn't activate queue advancement if it had been previously disabled. - The next() method was renamed to skip(), and was replaced with a next() method that is more parallel to the previous() method. - The poorly named "noplay" command was replaced with the "noadvance" and "advance" commands ("noadv" and "adv" for short). - The "sleep" and "wake" command aliases were removed from the command-line client. Too many aliases is just silly and confusing. - The "next" command now takes an argument that lets you skip ahead by more than one song. It also now has an effect when there's no current song. - The hostname is no longer appended to the names of the files stored in ~/.moosic/. (The only reason this was done in the first place was to handle the case where the user's home directory was on an NFS share and the user wanted to run a moosicd on different machines that were accessing the same home directory. This situation can now be handled more simply and appropriately by using the -c option, and I don't share /home anymore anyway.) Client developers will need to update their code, and users who want to use their old config and log files will have to rename them to remove the "-$HOSTNAME" part. - The os.kill() invocations in moosicd_methods.py have been put into try-catch blocks so that any problems they cause can be more easily traced. - The os.kill() invocation in the unpause() server method no longer raises an exception if the player process is dead already. - The "stop_hack" server attribute was renamed to "ignore_song_finish". - Songs that have no handler in the server's config are no longer placed in the history list, nor are they returned to the queue when loop mode is on. - The server's state is saved at regular time intervals rather than only saving when the server exits cleanly. This will make recovery from crashes of all sorts much more convenient. Fri 28 Nov 2003 - An effort has been started to keep instances of the empty string ('') out of the song queue, by filtering them out in the server methods that add or change queue members. Thu 27 Nov 2003 - A first implementation of the current_time() server method was completed, but not yet tested. Mon 17 Nov 2003 - The previous() server method now avoids trying to pop from an empty history list. Sun 16 Nov 2003 - A "help" command was added to moosic to allow a user to explore its commands in small, chewable chunks instead of the giant mouthful that is the output of --showcommands. - The behavior of the previous() moosicd method was corrected for the case when the server is in loop mode. When loop mode is on, the most recently played song is now taken from the end of the song queue instead of the end of the history list. Fri 14 Nov 2003 - The installation process was modified so that Python modules which would be of interest to a prospective Moosic hacker (i.e. the ones listed in the PUBLIC_MODULES makefile variable) are placed in the installation's documentation directory. This is in response to Debian Bug #196652. Thu 30 Oct 2003 - Version 1.4.10: Minor features and bugfixes. Wed 29 Oct 2003 - Typographical errors in the internal documentation for the "ispaused", "islooping", and "isadvancing" moosic commands were corrected. - I've tested it, and using TakCD as a handler for Moosic works. I just need to properly document this fact, and I'll be able to say that Moosic officially supports audio CD tracks. Tue 28 Oct 2003 - The --tcp-also option was added to moosicd, making it possible to listen on both a Unix socket and a TCP socket at the same time. Mon 27 Oct 2003 - The toggle_pause() server method now calls pause() or unpause() instead of duplicating the code in those functions. - The pause() server method now sends a STOP signal (a tenth of a second) after the TSTP, in case the song player doesn't respond to TSTP appropriately. Mon 20 Oct 2003 - moosicd now uses the TSTP signal instead of the STOP signal to pause song-player helpers. This is preparation for adding support for playing audio-CD tracks with TakCD. Fri 17 Oct 2003 - The startServer() function in moosic_factory.py now searches for a program named "MoosicDaemon" if a program named "moosicd" can't be found. This lets startServer() work even if I decide to rename the server program so that it doesn't interfere when I use my shell's tab-completion to use "moosic". - The getconfig() server method was added. Thu 16 Oct 2003 - A threading problem which occurred on *BSD (and possibly other) systems was worked around. On these platforms, moosicd's threads ran sequentially instead of in parallel, making moosicd almost useless. This is caused by the policy of the thread scheduler on these systems of not starting a new thread until the previous thread performs a blocking operation. So the work-around consists of inserting extremely brief calls to sleep() at key points when starting a new thread. (Bug reported and solved by Paul Barnfather ). Mon 13 Oct 2003 - Symlinks are now followed when scanning the music directory with "--auto-grep" or "--auto-find". Wed 08 Oct 2003 - Exceptions that occur when setting the locale in moosic are now ignored. This was done to let moosic work with the Python runtime included with Mac OS X, which throws an exception complaining that locale setting is not supported. Tue 07 Oct 2003 - A type error (attempting to concatenate a list to a tuple) in the recently modified "stagger" moosic command was fixed. Thu 02 Oct 2003 - The documentation for the moosicd methods was tweaked a bit. - The startServer() function in moosic_factory now uses a default path when the PATH environment variable isn't set. Tue 30 Sep 2003 - Since version 1.4, the return type for the "showconfig" server method has been advertised as "string", when it has really been returning "base64" data all this time. The correct return type is now registered and properly mentioned in the relevant documentation. - The behavior of the "stagger" moosic command was changed. It now puts any leftover songs (i.e. songs which didn't match any regex named on the command line) all together at the end of the queue, instead of including the leftovers within the staggered arrangement. The old behavior can be duplicated by adding a regex that matches anything (e.g. ".") as the last argument to the command. (Incompatible change. But I really doubt anyone will care enough to complain.) Fri 26 Sep 2003 - The "ispaused", "islooping", and "isadvancing" moosic commands were added. Tue 16 Sep 2003 - Some of the differences between my debian/ directory and the one in the official package were merged. - Version 1.4.7: Daemon auto-start and song auto-find. - Bah! Using the "in" operator to find a substring doesn't work in Python 2.2.x, so --auto-find fails (with an ugly crash) for Python 2.2.x users. - Version 1.4.8: Brown-paper-bag bug. - Augh! The "moosicd" target in the Makefile was missing an essential module, so the resulting executable couldn't run. I'm feeling very stupid because I really should have caught this, and I would have if I'd done proper testing. - A search string used with --auto-find no longer matches everything if the search string evaluates to the empty string after being simplified. - Version 1.4.9: Somebody just smack me with a stick. Mon 15 Sep 2003 - The command argument given to moosic now disregards non-alphanumeric characters, and the private unmangle() function in moosic_dispatcher.py was updated accordingly. Fri 12 Sep 2003 - The implementation of --auto-find was cleaned up and now respects directory seperators (i.e. slashes). - The "--no-recurse" option was documented in the manpage. (Oops, how long was that missing?) Tue 09 Sep 2003 - The "auto-grep" feature was added to moosic. - The --sort option to moosic has been properly distinguished from --inorder, both in the code and in the documentation. - The --ignore-case, --music-dir, --auto-find, and --auto-grep options were documented in the moosic manpage. - utilities.grep() and utilities.antigrep() now accept either a string or a compiled regexp as their first argument, and their implementations were very slightly optimized for speed. - The flatten() function and its helpers were added to utilities.py. - Valiant (but possibly vain) attempts were made to make utilities.staggered_merge() easier to understand. Mon 08 Sep 2003 - The functionality of the "ma" script was integrated into moosic with the addition of the "--auto-find" option (and its companion, "--music-dir"). These new options still need to be documented in the manpage. - The "--ignore-case" option was added to moosic, and needs to be documented in the manpage. Sun 07 Sep 2003 - The uniq() and sh_escape() functions were added to utilities.py. Sat 06 Sep 2003 - The "ma" script was contributed by David McCabe, and several enhancements were made to it. Mon 01 Sep 2003 - The --no-startserver option was added to moosic. - The moosic docs were updated to reflect the fact that it will now automatically start moosicd as needed. Sun 31 Aug 2003 - The startServer() function was documented and moved to moosic_factory.py. - debian/completion was updated, since I noticed that it was terribly out of date. Fri 29 Aug 2003 - moosicd now daemonizes itself with a handy daemonize function shamelessly nabbed from Noah Spurrier (http://www.noah.org/python/). - moosic now tries to start moosicd automatically if it can't contact a Moosic server at the address at which it expects to find one. This is meant to relieve users of the burden of having to start moosicd by hand. Thu 28 Aug 2003 - squeezeTool was modified to allow its generated executables to use a specific python version in the shebang line. The Makefile was modified to take advantage of this new option. - The Makefile was modified to allow a python executable to be specified via the PYTHON variable. - The Debian package is now uses python2.2 to build the executables. This is done in order to generate a package that works with both woody and sarge/sid. Debian users who wish to build the package with python2.3 instead of python2.2 should change the line that contains the string "PYTHON=python2.2" in the debian/rules file. - Version 1.4.6: Better Debian packaging, substitution feature. Wed 27 Aug 2003 - The "python" dependency in the "debian/control" file was changed to "python2.2", and "python2.2" was also added as a build dependency. Tue 26 Aug 2003 - A few bugs caused or revealed by yesterday's changes were fixed. - A seeming incompatibility between the pickling in Python 2.2.x and 2.3 was worked around. - The sub() moosicd method was finished and split into two variants: sub() and sub_all(). - The "sub" and "suball" moosic commands were added. Mon 25 Aug 2003 - The file named "README.developers" was added. - The Log class in moosicd_support was documented properly. - The race condition problem in the previous() moosicd method was explored and investigated more closely. - The "howmany" parameter to the previous() moosicd method is now effective, and the moosic command now takes advantage of it. - The stop() moosicd method now uses a flag to tell the queue consumer to act as if the song being stopped was not really finished (specifically, the song is neither appended to the queue when loop mode is on nor is it added to the history). This method was also made public since it now does things that can't be done outside of the server. - The change made to the stop() method allowed previous() to be rewritten in a way that is both much simpler and free of race conditions. Thu 17 Jul 2003 - I've decided that moosh was a fun experiment, but isn't really worth supporting officially. While it can be slightly more convenient than moosic in common cases, it will never be able to compete with the power that a good shell (like bash or zsh) lends to moosic, unless a great deal of effort was expended to duplicate advanced shell features. - The sub() moosicd method was added, but is not yet finished. Mon 21 Jul 2003 - Version 1.4.5: The "previous" command. Sun 13 Jul 2003 - The following moosh commands were implemented: "setopt", "alias", "showalias", "unalias". - Docstrings were written for all moosh-specific commands. - Tab completion in moosh was improved in various ways. - The "ls" command in moosh was improved to handle glob expansion. - The "shell" command was added to moosh. - moosh.pod needs to be written. - moosh is purposefully omitted from the Makefile since it is still experimental. Sat 12 Jul 2003 - Tab completion for moosh was worked on. Fri 11 Jul 2003 - Various parts of moosh were polished up. - moosic_dispatcher.process_filelist() now performs user-expansion on pathnames that start with '~' (tilde). - moosic_dispatcher.check_args() was added. Thu 10 Jul 2003 - I got bored and wrote most of moosh.py. Mon 07 Jul 2003 - moosic_dispatcher.wipe() was fixed to work properly when loop mode is on. Sun 06 Jul 2003 - The reason why "moosic curr" sometimes returns random junk was discovered. It turns out to be a bug in binascii.a2b_base64() in Python 2.2.2. No other Python versions seem to exhibit this bug. - The "True" and "False" constants are once again defined if they didn't already exist. This is meant to allow the program to run in Python 2.2, which doesn't pre-define these. Tue 03 Jun 2003 - A bug in which moosicd_methods.putback() didn't update data.last_queue_update was fixed. - The "previous" moosicd method was tentatively added. - The data.lock object in moosicd is now an instance of threading.RLock instead of threading.Lock to allow re-entrant locking. - The "previous" and "prev" moosic commands were added to mirror the moosicd method of the same name. Mon 02 Jun 2003 - Version 1.4.4: Minor new features and cleanups. Sun 01 Jun 2003 - The "last_queue_update" moosicd method was added, and associated changes to support this method were made throughout moosicd. - The Moosic_API document has been converted to POD, and HTML, text, and manpage versions are produced from this. - The NEWS file is now included in the distribution. - The debian/ directory was added for building Debian packages. Wed 28 May 2003 - The "install" target in the Makefile now puts the manpages in $(INSTALL_PREFIX)/share/man/ instead of $(INSTALL_PREFIX)/man/, as specified by the FHS 2.2. Sat 24 May 2003 - os.getenv is now used instead of os.environ to prevent exceptions from occurring if an environment variable isn't defined. Wed 21 May 2003 - The "stagger-merge" moosic command was added. - The "replace" moosicd method was added, and is used appropriately by the commands in moosic_dispatcher.py. - The "replace_range" moosicd method was added. Tue 20 May 2003 - Version 1.4.3: Code cleanup. Mon 19 May 2003 - The "unpause" moosicd method now refrains from sending its signal to the song player if there isn't a song player currently running. This fixes a bad-looking but harmless TypeError fault that the client would get after sending a "next" command. This fault was generated because the "next" moosicd method now calls "unpause" before finishing, and at that point data.player_pid would be reset, while data.current_song would not. - The moosic(1) manual page was polished up, and the documentation for the "stagger-add" command was completed. Thu 15 May 2003 - The "next" moosicd method now unpauses the current song when killing it, so that it will really terminate when we tell it to do so. This fixes a bug in which moosicd wouldn't die if the current song was paused. - The logic for checking the number of arguments given to a moosic command was redesigned. - moosic now prints all of its error messages to stderr instead of stdout. Wed 14 May 2003 - Lots and lots of code was moved from moosic_main.py to moosic_dispatcher.py. - The Makefile no longer generates HTML documentation for non-public Python modules. - A (harmless) "broken pipe" exception is no longer raised by moosicd when a client disconnects from the server before receiving all of the data returned by the server. Tue 13 May 2003 - Version 1.4.2: Minor bug fixes. - The moosic_dispatcher.py module was started in order to replace the Great Big Giant if-else chain that handles the command string. Mon 12 May 2003 - The cut_list and crop_list moosicd methods were added. - The utilities.parse_range() function now specially handles the case where the input is "-1". The return value is now (-1, end_arg) instead of (-1, 0). This makes "moosic list -- -1" work as expected. (This misbehavior was pointed out by Paramjit Oberoi.) - The "length" and "len" moosic commands were added. Thu 08 May 2003 - A bug (pointed out by Paramjit Oberoi) in which current_song would not be saved to disk was fixed. This happened because queue_consumer() would reset current_song before data.getstate() would be called. - A bug which broke "moosicd --version" was fixed by including "VERSION" in moosicd_support.__all__. Wed 07 May 2003 - A typo was fixed (errant comma at the end of a line) in main() in moosicd_main.py on the line that sets "logfilename". (Reported by Ray Shaw.) - The code that reads the config file is now placed before any code that uses data.confdir. This ensures that data.confdir really exists before an attempt is made to use it. - The "log" attribute is now set in the DataStore constructor in moosicd_support.py, fixing the AttributeError that was raised when moosicd_main.py tried to set this attribute. - Version 1.4.1-beta2: Critical bug fixes for the previous beta. - The play() function in moosicd_main.py no longer appends the songname to the command vector if matched-groups substition was already done. - The player log is now written with line-buffered output. - moosic.pod now mentions the loop-related commands. - Version 1.4.1: State saving and match group substition. Tue 06 May 2003 - packed_list() was commented out, since I'm not sure if it's a good idea. - A loop mode was implemented for moosicd. The appropriate moosicd methods and moosic commands were implemented, but they need further testing and documentation. - The __getitem__ and __getslice__ moosicd methods seem to work as expected. I might just make them official. - Version 1.4.1-beta1: Just for you, Ray. Mon 05 May 2003 - The indexed_list() moosicd method was fixed so that it returns a correct value for "start" in the case when the "start" input parameter is negative and its absolute value is greater than the length of the list. - The packed_list() moosicd method was added. - The implementation of saving/loading state at startup/shutdown has been completed, but hasn't been tested yet. - moosicd's option handling code and config file loading code was cleaned up and restructured to work well with saved state loading and to avoid the use of global variables. Sun 04 May 2003 - TcpMoosicServer and UnixMoosicServer now override handle_error so that the error is logged with moosicd's global log object. - split_range() was moved from moosicd_methods.py to moosicd_support.py. Sat 03 May 2003 - Match group substition was implemented in moosicd's song player. For example, if the config contains an entry with a regex of "^file://(.*)\.(.*)$" and a command of "foo -t \2 \1", "\1" and "\2" in the command will be replaced with whatever was matched by the first and second parenthesized groups, respectively. - $item substitution in moosicd's song player was improved to work on command elements that contain "$item" as a substring, in addition to command elements that are exactly "$item". Fri 02 May 2003 - The home-baked shuffle function in the utilities module has been abandoned in favor of the much faster random.shuffle function in the standard library. - The sort and reverse moosicd methods were slightly optimized for memory use. - The "sort", "shuffle", and "reverse" commands now accept a range argument which limits their operation to the items within the given range. - The singleton restriction was removed from "data" in moosicd. I decided it was a ill-conceived design. - The __setattr__ method that prevents creating new attributes for DataStore objects is now only installed at the end of executing the constructor, in order to make the constructor's code simpler and clearer. Thu 01 May 2003 - The "data" object in moosicd_support.py is now an instance of the DataStore class instead of a class object. - It is now impossible to set an attribute of the "data" object used in moosicd unless the attribute was bound in data's class definition. - The singleton pattern was implemented for the "data" object used in moosicd. Tue 29 Apr 2003 - Version 1.4: The XML-RPC revolution. Mon 28 Apr 2003 - The api_version() moosicd method was added. - The Makefile was updated to generate HTML documentation from POD files and from Python modules. - Moosic_API.txt has been completed. - A bad variable name in the get_history_limit() moosicd method was fixed. - The history list now shrinks itself down to max_hist_size when set_history_limit() is called if the length of the former is greater than the latter. Attempts to set the history limit below zero are now detected and thwarted. Sun 27 Apr 2003 - Some bugs uncovered by testing were fixed. - Explicit type checking was added to the "insert" moosicd method. This uses the introspection API that was standardized in PEP 252 and which was implemented in Python 2.2. - UnixStreamTransport was moved back outside of LocalMoosicProxy because it might be useful to someone else who wants to shove XML-RPC calls over a Unix socket. - The wrap() and xmlrpc_server_doc() functions were added to the utilities module. Thu 24 Apr 2003 - The return value of the history moosicd method was changed to a more sophisticated structure that cleanly separates the item that was played from the time it was played. - The "get_history_limit" and "set_history_limit" moosicd methods were added. - Docstrings were written for the LocalMoosicProxy and InetMoosicProxy factory functions in the utilities module, and the UnixStreamTransport class was tucked away inside the LocalMoosicProxy function. - LocalMoosicProxy and InetMoosicProxy were moved into a new module, named "moosic_factory". Tue 22 Apr 2003 - Aliases for the queue_length moosicd method were created: "length" and "__len__". Mon 21 Apr 2003 - The indexed_list() moosicd method now converts a negative index number into its non-negative equivalent. This feature was present in previously released versions, but I seem to have forgotten it when I did the big conversion to the 1.4 architecture. - The code in the play() function in moosicd_main.py that replaces occurrences of "$item" was fixed so that it doesn't append the songname to the command vector if "$item" substituion was already done. - A suggestion to use shell script wrappers to do fancy things was added to the default config file. This suggestion also notes that such shell scripts should use exec to invoke the player program. Tue 15 Apr 2003 - The eas^H^H^Hpassover egg was obfuscated a teeny bit. - The threads created by the request handler to handle individual requests are now forced to have non-daemon status so that they can properly return a response to the client even if the other threads have terminated. Mon 14 Apr 2003 - I just realized that the implementations for the grep and antigrep functions in the utilities module are more complicated than necessary. The match and antimatch classes, while cute, are completely not needed, since the expression "match(pattern)" is practically equivalent to "re.compile(pattern).search". Since I was rewriting these functions anyway, I replaced my calls to filter() with list comprehensions. Those list comprehensions are addictive little candies. - The "history" moosicd method was modified to return an array of base64 instead of an array of strings. Fri 11 Apr 2003 - Various buglets associated with the switch from "string" to "base64" were fixed. Thu 10 Apr 2003 - It turned out that all the Unicode nastiness could *not* be smoother over through liberal use of encode(). The problem is that XML-RPC insists on having all its strings in a specific, uniform text encoding (usually UTF-8), which means that if I want to pass strings through XML-RPC, they all must use a uniform codec. But that's practically impossible if I want to be able to pass around queue item names, since (1) they can potentially use any character encoding scheme which I can't sanely predict, and (2) they must not be arbitrarily translated from one codec to another without losing their validity, unless their original codec is somehow remembered, which would be a bookkeeping nightmare. The solution is to use XML-RPC's "base64" datatype instead of the "string" datatype for passing around queue item objects. - The preliminary work was done to ensure that the base64 datatype is used to pass queue item data between the client and the server. I expect to be ironing out the ramifications of this change for a little while to come. In particular, the API documentation will have to be updated to stress this fact. Additionally, care will have to be taken to ensure that appropriate conversion is done when calling any Moosic server methods that accept or return queue items (using xmlrpclib.Binary() for the former, and str() for the latter). Wed 02 Apr 2003 - The "del" command was added to moosic as an alias to "cut". - The "status" command was added to moosic as an alias to "state". - Various Python2-isms are quickly creeping into the code, such as list comprehensions (lots of them!), augmented assignments, and calling methods on instances of builtin types. I'm surprised that I bothered to pay lip service to Python 1.5.x for so long. Tue 01 Apr 2003 - I had rather painful fun debugging a slew of fatal UnicodeErrors. It turns out that all nastiness can be smoothed over by calling encode() on all strings before printing or otherwise outputting them. Mon 31 Mar 2003 - The "shutting down" log notice in moosicd was moved from the die method to the cleanup function, since the code in the die method isn't called if moosicd is killed with a signal instead of with a Moosic request. Sun 30 Mar 2003 - moosicd now cleans up stale socket files that get left behind when moosicd crashes. This is a feature that I've wanted for a long time. - The Log class was improved in minor ways: a dictionary of priority names was added, the priority name is now mentioned in the log output, and type-checking was improved. Fri 28 Mar 2003 - The flow control in moosicd was polished up so that it quits properly. - moosic.pod was updated to note that the "pl-*" commands can be used to read lists from standard input. - Version 1.3.4: Tiny fixes. Tue 25 Mar 2003 - A mistake that prevented shuffling from being applied when the "pl-*" moosic commands were used was corrected. - All critical sections in moosicd now use the proper idiom for acquiring and releasing the lock. - The 1.3.x version of moosicd was made more robust by protecting the calls to the do_command() method of MoosicHanlder inside a try block. A finally clause is also used to ensure that the lock object is released appropriately. This means that moosicd is no longer completely hosed when an unexpected exception is raised while handling a request. Mon 24 Mar 2003 - The "move_list" moosicd method was added. - The "move" moosicd method was rewritten in a simpler way. - Most lock releasing is now performed within "finally" clauses to ensure that the lock is released when the locked code raises an exception. I still need to ensure that this is done every time the lock is used. - The "append", "prepend", and "insert" moosicd methods were rewritten to use slice assignments and list comprehensions instead of the insert() or append() list methods and for loops. - The "pre" command was added to moosic as an alias for "prepend". Sun 23 Mar 2003 - The initial prototype of the moosic_proxy program was written. It still needs to be fleshed out with command-line switches and a real manpage. - The interface for passing ranges to Moosic server methods was finalized, and all the relevant methods were converted appropriately. - The following moosicd methods now accept an optional "range" argument which limits their operation to the specified range: shuffle, sort, reverse, remove, filter. - I'm not sure yet, but I think I might be finished with making backwards incompatible changes to the moosicd API. (At least until version 2.0 or something equally earth-shaking.) Thu 20 Mar 2003 - I finally broke down and added a hack to work around the evil, evil bug in ogg123. For those of you just tuning in, when SIGTERM is sent to an ogg123 process (i.e. whenever the "next" command is used), the process will appear to be terminated (as far as calls to wait()/waitpid() can determine), but the sound card will somehow remain busy for a fraction of a second. (Don't ask me why; it doesn't make any sense to me either, even after reading ogg123's source code.) As a result of this, moosicd will try and fail to play the next song in the queue, since the player that moosicd spawns generally will immediately exit with a "Device or resource busy" error. Depending on your system, the phase of the moon, and the mood of the nearest cat in the area, moosicd will quickly and repeatedly consume a large chunk of the song queue quite unsuccessfully. Fortunately, ogg123 seems to behave properly when SIGINT is sent to it, so I've devised a shameful and unreliable hack that detects when the current song is being played by ogg123, and sends SIGINT instead of SIGTERM. - Version 1.3.3: I hate you, ogg123. Sun 16 Mar 2003 - The makefile no longer includes MoosicClient.py when it squeezes moosic into its executable form, since this module has been made practically obsolete. Sun 09 Mar 2003 - The listlock member of the data class in moosicd was renamed to "lock" because it really is used for locking any of the data in the data class, not just the song list. Fri 07 Mar 2003 - Some moosicd methods were renamed to use under_scores rather than StudlyCaps. I can still do fun API-breaking things like this since the moosicd API isn't officially finalized yet. - A moosicd method for querying how long the current song has been playing was partially written. There are still some implementation issues to work out. Thu 06 Mar 2003 - The request handler has been made asynchronous by using the SocketServer.ThreadingMixIn class. As a result of this, the lock acquisition was made more fine grained by limiting it to non-read-only requests. - The qrunning flag in moosicd is no longer overloaded as a flag that is used to signal moosicd to quit. A separate quitFlag is now used instead. Wed 05 Mar 2003 - A misnamed variable in moosicd was fixed. Specifically, "log" was used instead of "data.log" in the last else clause in the do_command method in the MoosicHandler class. This bug would cause moosicd to crash if it received an unrecognized command from a client. - A bug in moosicd which allowed an IOError to go uncaught if the client closed its end of the socket too early was fixed. - Version 1.3.2: Emergency bug fixes. Thu 27 Feb 2003 - A lot the code in moosicd_main.py was moved into its own file, moosicd_support.py. Wed 26 Feb 2003 - Several variables in moosicd were renamed for increased clarity. - moosicd was given a heavy and comprehensive massage in preparation for phasing out the old home-brewed client-server RPC protocol in favor of XML-RPC. - The functions that implement the commands that moosicd can receive from a client have been split out from moosicd into a separate module: moosic_methods.py. - The parse_range function has been moved from moosicd_main.py into the utilities.py module. - Python 2.2 or higher is now officially required, due to the use of the xmlrpclib module. Mon 24 Feb 2003 - moosicd now has its implementation spread across multiple source files, and thus must also be joined with squeezeTool. Sun 23 Feb 2003 - Several functions which are common to both moosic and moosicd were moved into the utilities.py module. The Makefile was updated accordingly. - This marks the beginning of the 1.4 branch of Moosic. Thu 20 Feb 2003 - The --config-dir option is now properly mentioned in the output of "moosic --help" - A "clean" target was added to the Makefile. - The server log file is now line-buffered to help prevent data loss in case of a server crash. - Version 1.3.1: New minor features, and small annoyances squashed. Wed 19 Feb 2003 - The --tcp option was added to moosic and used to test moosicd's new TCP/IP capabilities. Tue 18 Feb 2003 - The --sort commandline option was added to moosic. Mon 17 Feb 2003 - In the response handler for the INSERT command to moosicd, the code now returns if the expected space character is missing, instead of wrongly attempting to execute the rest of the code in the INSERT handler. - All exceptions thrown by the code in the MoosicClient module are now derived from the MoosicError class. - moosic now catches all the exceptions that might be thrown by the code in the MoosicClient module. Sun 16 Feb 2003 - I changed my mind about moving the documentation files into their own directory, so I moved them back. Fri 14 Feb 2003 - Options to the moosic program can now be specified either before the command or after (or both). Wed 12 Feb 2003 - The call to wait() in the song player function was replaced with a call to waitpid(). This was done in an attempt to ensure proper behavior for when the player spawns a multi-threaded program (such as ogg123) to play a song. The player function now returns only when the process that was specifically spawned by the player exits, instead of returning whenever at least one of the player's children finishes. Unfortunately, this doesn't really fix the reported problem of ogg123 leaving the sound card locked after it has seemingly exited, but I've let this change remain since it hurts nothing and makes the purpose of the code a little more clear and explicit to those reading it. Wed 29 Jan 2003 - The documentation files (manpages and API document) were moved into their own directory, and the Makefiles were updated accordingly. - An option was added to moosicd which causes it to listen to a TCP/IP socket for client requests instead of using the normal client-server communication method. (Still untested.) - An option was added to moosic to prevent replacing directories named on the command line with their contents ("--no-recurse"). - The "--no-file-munge" option to moosic was renamed to "--non-file-args", and is now meant to indicate that command-line arguments should not be interpreted as filenames. (The old name is still around for backward compatibility, but you didn't hear me say that.) Mon 27 Jan 2003 - A little work was done on Moosic_Client_API.txt, including the addition of a notice of the document's state of incompleteness. - The "broken pipe" error messages associated with the "list" command were eliminated by catching and ignoring pipe-related IOErrors in the moosic client. Wed 11 Dec 2002 - The MoosicClient class was was split out of the command-line client into its own module. The squeezeTool.py is now used to squoosh the two parts into a single executable. Tue 10 Dec 2002 - Version 1.3: Little changes on the outside, big changes on the inside. Sun 8 Dec 2002 - A bug in the 'stagger' command was fixed. This bug was only triggered when there was at least one item in the list that didn't match any of the regular expressions named on the command line. Fri 1 Nov 2002 - The shell-quoting used when invoking the find command was improved to properly handle pathnames containing single-quotes. Tue 13 Aug 2002 - The documentation for the 'history' command in moosic was updated. Sun 21 Jul 2002 - The gazillions of lines of conditional debugging messages were removed from moosicd. I really wasn't using them, and they were just creating a huge clutter. This means that the -d option is pretty useless. - The 'unpause' command was added to moosic. - The --config-dir option was added to moosic. - Lots of debugging was done. moosic amd moosicd are now actually usable enough to start a serious testing phase. Tue 16 Jul 2002 - The "How to Write Your Own Moosic Client" document was started. - The song player component of moosicd now captures the output of the programs it spawns and places this output into the file named "$HOME/.moosic/player_log-$HOSTNAME". - moosicd now creates the ~/.moosic/ directory with its mode set to 0700, reducing the possibility of malicious exploitation. Wed 10 Jul 2002 - WARNING: Interface change. The regular expressions in the ~/.moosic/config file are no longer implicitly case-insensitive. Case insensitive matching must now be explicitly requested in the config file, presumably by adding the "(?i)" switch to the regexps (which has been done in the default config file). - The Log class was completed. - The song player now outputs a relevant message when it has no handler for a particular file. Tue 09 Jul 2002 - The "True" and "False" constants are now used for boolean values instead of literal 1 and 0. - Stubs for the various MoosicClient command methods were written, and several of them were implemented. - The 'partial-sort' command was added to moosic. The documentation for this command and for the 'stagger' command was completed. Tue 02 Jul 2002 - The 'stagger' command was added to moosic. The documentation for this command still needs to be added to the manpage and the internal help. Tue 04 Jun 2002 - The staggered_merge() function was added to moosic in anticipation of adding a new shuffling method that takes advantage of it. It's chock full of functional programming goodness. - More whitespace was added to shuffle() in an attempt to make it less magical-looking. I don't think it worked very well. It's just a magical function. Fri 31 May 2002 - The MoosicData class was created within moosicd in an effort to purge the use of global variables. - An overhaul of the logging system in moosicd was started. Tue 28 May 2002 - Several changes from versions 1.2.6 and 1.2.7 were merged into the 1.3 codebase. - Both moosic and moosicd were adapted in various ways to accomodate the new communication protocol. Mon 27 May 2002 - In the client, a MoosicClient class was created to provide a simple programmatic interface for sending commands to moosicd. The communication protocol between a client and the server was clearly defined. The constructor and the internal _generic_request methods were completed. Tue 21 May 2002 - Version 1.2.7: Band-aid for the "broken pipe" bug. - Exception handling in the client and the server was improved so that fewer exceptions go uncaught. This drastically reduces the nastiness that results from the "broken pipe" error that can be produced when piping the output of "moosic list" to another program. Tue 07 May 2002 - The output of "moosic --showcommands" was updated. - Version 1.2.6: Documentation, finally! Mon 06 May 2002 - The manual pages for moosic and moosicd have been completed. Tue 23 Apr 2002 - A "toggle-pause" command was added. Tue 16 Apr 2002 - Proper manuals (in the POD format) for moosic and moosicd have been started. Tue 09 Apr 2002 - The play() function now uses a different method for silencing the stdout and stderr of the processes it spawns. Instead of closing stdin, stdout, and stderr, a file descriptor referencing /dev/null is dup2()ed onto these streams. This idea was suggested by David Bustos . - In the process of doing the above, a bug that interfered with the suppression of stdout and stderr was fixed. This bug was fixed by working with sys.__stdout__ instead of sys.stdout. - When the play() function looks up the command to use for playing a file, it now makes a copy of the command list named in the config, instead of working with a reference to this list. This allows me to safely perform $item replacement on the command list before executing it. - The part of the code contained in the listener_thread() function which instantiated the MusicServer was moved outside this function, slightly increasing code clarity and simplicity. Mon 18 Mar 2002 - Eek. I've let circumstances distract me from this project for quite a long time. Version 1.2.5 was released for real this time. Tue 05 Feb 2002 - The "list" command is now able to take a range argument which limits the listing to the given range. - Everything has been nicely tested and debugged. We're long overdue for a new release. - Version 1.2.5: Lots of new and interesting commands. Mon 04 Feb 2002 - The following commands were completed, but still may have bugs: "insert", "pl-insert", "move", and "pause". - A helper function was written for commands that accept ranges in their argument list (such as "cut", "crop", and "move"). - The output of the "state" command was greatly changed. It is now more informative and easier to understand. - The documentation for the moosic client commands was reformatted. - Command arguments to the moosic client are now case-insensitive. Wed 30 Jan 2002 - More code documentation tweaks. The ambiguous and icky "state" variable was renamed to "qrunning", and its role in the program was more clearly defined and described. This is only of interest to people who are reading the moosicd code. Thu 24 Jan 2002 - The documentation for the "debug" command was removed, since it is no longer useful for debugging anything. (The only things it was useful for debugging have already been thoroughly debugged.) Wed 23 Jan 2002 - Work was started on a "pause" command, which suspends the process that is playing the current song so that a song can be paused in the middle of being played and later resumed at the same point. - More documenting comments were added to the source code, and various other user-invisible name changes were made for the sake of code clarity. Mon 07 Jan 2002 - The "help" command was removed. It didn't work properly unless the moosic server was running, and the documentation for the command was incorrect, and it was never anything more than a sugary alias for "--help" anyway. Sun 06 Jan 2002 - BEWARE! I've made an imcompatible change to the interface! The behavior of the "stop" command is now identical to that of the "sleep" command. The new "wipe" command now does what the "stop" command previously did (i.e. clear the playlist and stop the current song). - For some strange reason, the "die" command was not mentioned in the command documentation. This was fixed, and some aliases for "die" were added. - Playlist saving and loading was implemented. New commands: "pl-add", "pl-append", "pl-prepend", "pl-mixlist", and "plainlist". OK, playlist saving wasn't really implemented: you have to redirect the output of "plainlist" to a file. Cry me a river. - The "cleanup" handler function in moosicd now ignores various exceptions that might occur as it runs. Thu 15 Nov 2001 - Ok, I think version 1.2 has been tested enough. It's time to release it. - Version 1.2: The portable release. Wed 14 Nov 2001 - The "play" shell script (which usually comes with sox) is no longer used to play files in the default configuration file, since it doesn't work properly with the "next" command. Instead, sox is called directly. - You can now use a special "$item" token in the command lines in the filetype association (configuration) file. This token is replaced with the playlist item to be played. - Some kinks in moosicd were ironed out to make sure that it always exits in a clean fashion under normal circumstances. The is especially important now that it has a socket file that it has to clean up after itself. - Some basic testing has been completed, and everything seems to actually work now. Mon 12 Nov 2001 - The named Unix-domain sockets used to communicate between client and server now exist within the filesystem, rather than taking advantage of the Linux-only abstract namespace thingy. - The "crop" and "cut" commands were added. - moosicd now closes the stdin of external players, in addition to stdout and stderr. - The "list" command no longer includes the currently playing song as the zeroth item. I decided that it was more redundant and confusing than convenient. Wed 07 Nov 2001 - The "showconfig" command was added. Sun 04 Nov 2001 - A bug in which the moosic client would prepend items to the playlist in reverse order has been fixed. This bug was introduced by the change in client/server protocol that was made on Mon 29 Oct 2001. Fri 02 Nov 2001 - The "reverse" command was added. Mon 29 Oct 2001 - The protocol between the client and the server was changed so that the APPEND and PREPEND server commands only take a single item as their argument, rather than a whole list of items. Similarly, when the server sends data to the client in response to a LIST command, each item is sent in a separate packet instead of sending the entire list of items in one great big giant packet. This solves the problem which prevented the client and server from sending a list of items to each other if the list was too long. - Some of the server code was rearranged for greater clarity. Specifically, the definitions for the MusicHandler and MusicServer classes were moved outside of the definition of the listener_thread() function. Sun 28 Oct 2001 - Version 1.1.2: More small fixes and improvements. - This changelog now is ordered from newest to oldest instead of the other way around. Fri 26 Oct 2001 - The "version" command was added to the client. This prints out the version number of both the client and the currently running server. Thu 25 Oct 2001 - "fuser -ks /dev/dsp" is no longer used to stop playing the current song. This method stopping the current song was evil in several ways. First, it required fuser to be installed and in the user's path. Second, it would blindly kill whatever process was using /dev/dsp whether or not moosic had actually spawned that process. Third, it only produced any useful effect if the program that was playing the current song actually used /dev/dsp, which is not the case for programs such as playmidi. - moosicd now prints out information regarding the configured filetype associations if the debugging messages are turned on. Wed 24 Oct 2001 - The --help option to moosic was shortened considerably, and the --showcommands option was added. - The formatting of the documentation of the commands was adjusted for readability (dots were added). Tue 16 Oct 2001 - The default configuration file was tweaked (the MIDI regexp was slightly changed). - The "reconfig" command should now actually work, as it never did before. This still needs to be tested to be certain. Mon 15 Oct 2001 - Version 1.1.1: Random tweaks and fixes. Thu 11 Oct 2001 - The version number of moosic and moosicd is now listed as 1.1.1. I'll probably tar this up and call it a formal release later today. - The problem where moosicd would crash if its log file didn't already exist was fixed. Sun 30 Sep 2001 - The moosic client now accepts "shuffle" as equivalent to the "reshuffle" command. Mon 17 Sep 2001 - moosicd now uses socket.gethostname() instead of the HOSTNAME environment variable. Thu 13 Sep 2001 - INCOMPATIBLE CHANGE: The -c option to moosicd now specifies the server's configuration directory instead of the configuration file. - moosicd now defaults to writing all its output to a file (~/.moosic/log-$HOSTNAME) instead of printing to the screen. Mon 03 Sep 2001 - The code in the server that handles commands now catches any socket exceptions which are thrown. - When the server gets the DIE command, it now only kills whatever process has /dev/dsp open if the server is actually playing a song. Fri 10 Aug 2001 - The standard pre-release bugsearch has been declared complete. - Version 1.1: Yummy new features. Tue 07 Aug 2001 - Support for the "filter" command was added to the client. (Tee hee, small detail to forget.) - A deadlock that prevented the "die" command from working was fixed. The "die" command _really_ works now. Really. I'm not lying this time. - The "reconfigure" command was added. - The "--no-file-munge" option was added. Wed 01 Aug 2001 - The "die" command was disabled because it doesn't actually work. - The "filter" command was added. This is a converse to the "remove" command in that it removes everything that doesn't match the given regexp(s). - The "die" command was fixed and re-enabled. Tue 31 Jul 2001 - The association of file types to player programs has been moved into a separate configuration file. Fri 27 Jul 2001 - The server now numbers the list of items in the playlist, and includes the current song as item zero. - The "history" command was added. Thu 26 Jul 2001 - The "die" command was added. Wed 25 Jul 2001 - The program's history was moved out of the ChangeLog file and into the HISTORY file. - The dependency upon the external "date" command was removed. Tue 24 Jul 2001 - This changelog was created. - Stuff was added to the TODO list. - A history of the program was written. Mon 23 Jul 2001 - Version 1.0: Initial public release. moosic-1.5.6/doc/0000755000175000017500000000000011655460503014300 5ustar danieldaniel00000000000000moosic-1.5.6/doc/Moosic_API.sect1.pod0000644000175000017500000000675407722475604017770 0ustar danieldaniel00000000000000=head1 Section 1: The Moosic Server's Data Model This section describes, in precise terms, the data objects that can be accessed and manipulated by sending requests to the Moosic server. =over =item song queue This is the foremost data object maintained by the Moosic server. It is an ordered sequence of strings that represents the queue of songs that are waiting to be played. Each item in this list identifies a song that will be played by the Moosic server. Usually, these items are the names of files on disk that contain each song, but this does not have to be the case. For instance, an HTTP URL might be used to name a song if a program that can play songs from the Web is appropriately registered with the Moosic server (see "player configuration" later in this section). =item current song This is a string that identifies the song that is currently being played by the Moosic server. If nothing is currently playing, then this will be the empty string. =item queue running flag This is a boolean value that indicates whether the Moosic server will start playing a new song as soon as the current song has finished and the song queue is not empty. =item pause flag This is a boolean value that indicates whether the current song is paused or not. =item loop mode flag This is a boolean value that sets "loop mode". When loop mode is on, songs are returned to the end of the song queue when they finish playing instead of being discarded. =item history This is a list of songs that the Moosic server has finished playing. Note that songs named in this list may have finished playing early at the request of a user (i.e. through use of the "next" command). Each entry in this list is actually a 3-tuple of (song, start time, finish time). =item maximum history size This is the maximum number of songs that will be stored in the history list. Old entries are removed from the history to make room for newer entries when this limit is reached. =item player configuration This is an ordered mapping that associates regular expressions (text patterns) to programs. For each regular expression, the associated program is expected to be able to play any queue items that match that regular expression. Each program is a list in which the name of the executable file that contains the program is the first element and the program's arguments are the rest of the elements. =item last queue update This is the time at which the song queue was last modified. It a floating-point number that represents time as the number of seconds since the epoch. =item server version This is a string that describes the version of the program that implements the Moosic server. It has no specific, well-defined semantics. =item API version This is a pair of integers, one representing the "major" version, and the other representing the "minor" version. These numbers are meant to provide some useful compatibility information to Moosic clients. As this API changes, these numbers will change in the following ways. If the API has been changed in a backward-compatible way (e.g. a new method was added or an existing method was overloaded), then the minor version will increase and the major version will remain unchanged. However, if the API has been changed in such a way that existing code that uses the API might break (e.g. a method was removed or its return value or parameter types were changed), then the major version will increase and the minor version may be reset to any value (although it will usually be reset zero). =back moosic-1.5.6/doc/Moosic_API.sect4.pod0000644000175000017500000000302207666424170017753 0ustar danieldaniel00000000000000=head1 Section 4: Writing a Moosic Client in Python As demonstrated in section 0, communicating with a Moosic server is very easy to do with Python. In this section, I'll merely elaborate on the details that were omitted from section 0 for the sake of brevity. First of all, you should know that LocalMoosicProxy() can be called with a filename argument to specify the location of the Moosic server's socket file. This is useful if moosicd was started with the "-c" option. Refer to the moosic_factory.py module's documentation (moosic_factory.html). Next, note that many of the Moosic server's methods accept or return special types of objects from the xmlrpclib module, namely Boolean and Binary. These object types serve the purpose of bridging the small mismatch between the data-types supported by XML-RPC and Python's intrinsic data-types. Boolean objects present no unusual problem, since they evaluate to a correct truth value without any extra effort. However, you must take care when using the Moosic methods that accept or return Binary objects. Read the documentation for the xmlrpclib module for details on how to work with these objects. The basic technique boils down to wrapping up a string inside a Binary object before sending it to the server, and using the "data" attribute to access the string data within the Binary objects returned by the server. Regular strings can't be used because XML-RPC's normal string data-type can't handle multiple 8-bit strings within a single request if the strings use different encodings. moosic-1.5.6/doc/Moosic_API.txt0000644000175000017500000013021010031502722016743 0ustar danieldaniel00000000000000NAME Moosic_API - How to write your own Moosic client. Introduction "moosicd" is a program that implements the server portion of the Moosic jukebox system. This server provides services for manipulating a queue of songs to be played, as well as for controlling the playing of these songs. A Moosic client sends requests to the server, and receives data in response to these requests. The "moosic" command line utility is the canonical Moosic client, and provides a way to control a Moosic server from an interactive command shell or from a shell script. However, you might not be satisfied by the interface that is provided by the "moosic" command, so I have written this document to describe how to communicate with a Moosic server in your own programs. [This document was written by Daniel Pearson , and has been placed into the public domain.] Section 0: Instructions for Impatient Developers 1. Write your program with Python and xmlrpclib. xmlrpclib is included with Python 2.2 or later, but if you need to use an earlier version of Python, xmlrpclib can be downloaded from . 2. Copy the moosic_factory.py module from the Moosic source code tarball to a place where your program can reach it. 3. Create a proxy which communicates with moosicd: >>> import moosic_factory >>> proxy = moosic_factory.LocalMoosicProxy() 4. Read section 3 for the documentation of all the methods supported by the proxy object. 5. Use the proxy to get information from the server and to send commands to it. For example: >>> proxy.list() [] >>> proxy.is_queue_running() >>> proxy.haltqueue() >>> proxy.is_queue_running() >>> proxy.append([xmlrpc.Binary(i) for i in ... ['/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3', ... '/home/daniel/music/Fiona_Apple/When_The_Pawn/04-Love_Ridden.ogg', ... "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"]]) >>> proxy.list() [, , ] >>> [i.data for i in proxy.list()] ['/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3', '/home/daniel/music/Fiona_Apple/When_The_Pawn/04-Love_Ridden.ogg', "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"] >>> proxy.sort() >>> [i.data for i in proxy.list()] ['/home/daniel/music/Fiona_Apple/When_The_Pawn/04-Love_Ridden.ogg', '/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3', "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"] 6. If you wish to communicate with a moosicd that is listening for requests on an IP socket instead of a Unix domain socket, you should use InetMoosicProxy instead of LocalMoosicProxy. Here's an example: >>> import moosic_factory >>> proxy = moosic_factory.InetMoosicProxy('example.com', 8080) Section 1: The Moosic Server's Data Model This section describes, in precise terms, the data objects that can be accessed and manipulated by sending requests to the Moosic server. song queue This is the foremost data object maintained by the Moosic server. It is an ordered sequence of strings that represents the queue of songs that are waiting to be played. Each item in this list identifies a song that will be played by the Moosic server. Usually, these items are the names of files on disk that contain each song, but this does not have to be the case. For instance, an HTTP URL might be used to name a song if a program that can play songs from the Web is appropriately registered with the Moosic server (see "player configuration" later in this section). current song This is a string that identifies the song that is currently being played by the Moosic server. If nothing is currently playing, then this will be the empty string. queue running flag This is a boolean value that indicates whether the Moosic server will start playing a new song as soon as the current song has finished and the song queue is not empty. pause flag This is a boolean value that indicates whether the current song is paused or not. loop mode flag This is a boolean value that sets "loop mode". When loop mode is on, songs are returned to the end of the song queue when they finish playing instead of being discarded. history This is a list of songs that the Moosic server has finished playing. Note that songs named in this list may have finished playing early at the request of a user (i.e. through use of the "next" command). Each entry in this list is actually a 3-tuple of (song, start time, finish time). maximum history size This is the maximum number of songs that will be stored in the history list. Old entries are removed from the history to make room for newer entries when this limit is reached. player configuration This is an ordered mapping that associates regular expressions (text patterns) to programs. For each regular expression, the associated program is expected to be able to play any queue items that match that regular expression. Each program is a list in which the name of the executable file that contains the program is the first element and the program's arguments are the rest of the elements. last queue update This is the time at which the song queue was last modified. It a floating-point number that represents time as the number of seconds since the epoch. server version This is a string that describes the version of the program that implements the Moosic server. It has no specific, well-defined semantics. API version This is a pair of integers, one representing the "major" version, and the other representing the "minor" version. These numbers are meant to provide some useful compatibility information to Moosic clients. As this API changes, these numbers will change in the following ways. If the API has been changed in a backward-compatible way (e.g. a new method was added or an existing method was overloaded), then the minor version will increase and the major version will remain unchanged. However, if the API has been changed in such a way that existing code that uses the API might break (e.g. a method was removed or its return value or parameter types were changed), then the major version will increase and the minor version may be reset to any value (although it will usually be reset zero). Section 2: The Low-Level Details of Client-Server Communication The information in this section is generally only necessary to people who wish to write a Moosic client in a programming language other than Python. If you are using Python to write a Moosic client, then you can use the classes LocalMoosicProxy and InetMoosicProxy from the moosic_factory.py module, and blissfully ignore most of these gory details. However, Python programmers can also benefit from reading this section, as it will deepen their understanding of Moosic's inter-process communication model. The first thing to know about writing your own Moosic client is that communication between the client and server is done through a BSD-style socket. Read the "socket" manual page (and related manual pages) on a Unix system if you are unfamiliar with BSD sockets. The socket used by Moosic belongs to the Unix-domain protocol family (PF_UNIX or PF_LOCAL) and has a type of SOCK_STREAM. This means that a Moosic client can only communicate with a Moosic server that is running on the same computer as the client. This limitation is a very purposeful part of Moosic's design. It has the advantage of vastly reducing the consequences of any security flaws that Moosic might have. If you really, really think that you need the client and the server to run on separate hosts, then you can run moosicd with the -t option, which tells it to listen on a TCP/IP socket instead of a Unix domain socket. I recommend firewalling such a port very carefully. Regardless of which kind of socket is used by the server, XML-RPC is used as the data protocol for requests and responses. For an introduction to XML-RPC, see the XML-RPC homepage and the XML-RPC HOWTO . Python users should note that if the XML-RPC HOWTO tells you that you need to install a third-party library to use XML-RPC, it is assuming that you are using a Python version earlier than 2.2. Since version 2.2, Python has included the xmlrpclib module in its standard library. In summary, all you need to do to talk to a Moosic server in your own program is to send XML-RPC requests to the appropriate address. By default, the appropriate address for contacting moosicd is the file named "socket" in a directory named ".moosic" in the home directory of the user that started moosicd. If moosicd is started with the -c option, then the directory that contains "socket" will be the argument provided to the -c option instead of ~/.moosic. If moosicd is started with the -t option, then clients will have to address it by using a (host, port) pair instead of a filename. Section 3: Valid Moosic Server Methods moosicd's XML-RPC server implements the introspection API mentioned on , so the API presented by moosicd is essentially self-documenting. Thus, the information in this section has been automatically generated by querying a running Moosic server. The Moosic API contains the following methods: array "api_version" () Returns the version number for the API that the server implements. Arguments: None. Return value: The version number, which is a 2-element array of integers. The first element is the major version, and the second element is the minor version. boolean "append" (array) Adds items to the end of the queue. Argument: An array of (base64-encoded) strings, representing the items to be added. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server has no idea what the client's current working directory is. Return value: Nothing meaningful. boolean "clear" () Removes all items from the queue. Arguments: None. Return value: Nothing meaningful. boolean "crop" (array) Remove all queued items that do not fall within the given range. Arguments: An array of integers that represents a range. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. boolean "crop_list" (array) Removes all items except for those referenced by a list of positions. Arguments: An array of integers that represents a list of the positions of the items to be kept. Return value: Nothing meaningful. base64 "current" () Returns the name of the currently playing song. Arguments: None. Return value: The name of the currently playing song. double "current_time" () Returns the amount of time that the current song has been playing. Arguments: None. Return value: The number of seconds that the current song has been playing. boolean "cut" (array) Remove all queued items that fall within the given range. Arguments: An array of integers that represents a range. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. boolean "cut_list" (array) Removes the items referenced by a list of positions within the queue. Arguments: An array of integers that represents a list of the positions of the items to be removed. Return value: Nothing meaningful. boolean "die" () Tells the server to terminate itself. Arguments: None. Return value: Nothing meaningful. boolean "filter" (base64) boolean "filter" (base64, array) Removes all items that don't match the given regular expression. Arguments: A regular expression that specifies which items to keep. * Optionally, an array of integers may be given as a second argument. This argument represents a range to which the filtering will be limited. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. int "get_history_limit" () Gets the limit on the size of the history list stored in memory. Arguments: None. Return value: The maximum number of history entries that the server will remember. array "getconfig" () Returns a list of the server's filetype-player associations. Arguments: None. Return value: An array of pairs. The first element of each pair is a (base64-encoded) string that represents a regular expression pattern, and the second element is a (base64-encoded) string that represents the system command that should be used to handle songs that match the corresponding pattern. boolean "halt_queue" () Stops any new songs from being played. Use run_queue() to reverse this state. Arguments: None. Return value: Nothing meaningful. boolean "haltqueue" () Stops any new songs from being played. Use run_queue() to reverse this state. Arguments: None. Return value: Nothing meaningful. array "history" () array "history" (int) Returns a list of the items that were recently played. Arguments: If a positive integer argument is given, then no more than that number of entries will be printed. If a number is not specified, or if zero is given, then the entire history is printed. The result is undefined if a negative integer argument is given (but does not raise an exception). Return value: An array of triples, each representing a song that was played along with the times that it started and finished playing. * The first member of the pair is a (base64-encoded) string which represents the song that was previously played. * The second member of the pair is a floating point number which represents the time that the song started playing in seconds since the epoch. * The third member of the pair is a floating point number which represents the time that the song finished playing in seconds since the epoch. struct "indexed_list" () struct "indexed_list" (array) Lists the song queue's contents. If a range is specified, only the items that fall within that range are listed. This differs from list() only in its return value, and is useful when you want to know the starting position of your selected range within the song queue (which can be different than the starting index of the specified range if, for example, the starting index is a negative integer). Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is returned. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: A struct with two elements. This first is "list", an array of (base64-encoded) strings, representing the selected range from the song queue's contents. The second is "start", an integer index value that represents the position of the first item of the returned list in the song queue. boolean "insert" (array, int) Inserts items at a given position in the queue. Arguments: The first argument is an array of (base64-encoded) strings, representing the items to be added. * The second argument specifies the position in the queue where the items will be inserted. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server has no idea what the client's current working directory is. Return value: Nothing meaningful. boolean "is_looping" () Tells you whether loop mode is on or not. If loop mode is on, songs are returned to the end of the song queue after they finish playing. If loop mode is off, songs that have finished playing are not returned to the queue. Arguments: None. Return value: True if loop mode is set, False if it is not. boolean "is_paused" () Tells you whether the current song is paused or not. Arguments: None. Return value: True if the current song is paused, otherwise False. boolean "is_queue_running" () Tells you whether the queue consumption (advancement) is activated. Arguments: None. Return value: True if new songs are going to be played from the queue after the current song is finished, otherwise False. double "last_queue_update" () Returns the time at which the song queue was last modified. This method is intended for use by GUI clients that don't want to waste time downloading the entire contents of the song queue if it hasn't changed. Arguments: None. Return value: A floating-point number that represents time as the number of seconds since the epoch. int "length" () Returns the number of items in the song queue. Arguments: None. Return value: The number of items in the song queue. array "list" () array "list" (array) Lists the song queue's contents. If a range is specified, only the items that fall within that range are listed. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is returned. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: An array of (base64-encoded) strings, representing the selected range from the song queue's contents. boolean "move" (array, int) Moves a range of items to a new position within the queue. Arguments: The first argument is an array of integers that represents a range of items to be moved. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. * The second argument, "destination", specifies the position in the queue where the items will be moved. Return value: Nothing meaningful. boolean "move_list" (array, int) Moves the items referenced by a list of positions to a new position. Arguments: The first argument is an array of integers that represents a list of the positions of the items to be moved. * The second argument, "destination", specifies the position in the queue where the items will be moved. Return value: Nothing meaningful. boolean "next" () boolean "next" (int) Stops the current song (if any), and jumps ahead to a song that is currently in the queue. The skipped songs are recorded in the history as if they had been played. When called without arguments, this behaves very much like the skip() method, except that it will have an effect even if nothing is currently playing. Arguments: A single integer that tells how far forward into the song queue to advance. A value of 1 will cause the first song in the queue to play, 2 will cause the second song in the queue to play, and so on. If no argument is given, a value of 1 is assumed. Return value: Nothing meaningful. boolean "no_op" () Does nothing, successfully. Arguments: None. Return value: Nothing meaningful. boolean "pause" () Pauses the currently playing song. Arguments: None. Return value: Nothing meaningful. boolean "prepend" (array) Adds items to the beginning of the queue. Argument: An array of (base64-encoded) strings, representing the items to be added. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server has no idea what the client's current working directory is. Return value: Nothing meaningful. boolean "previous" () boolean "previous" (int) Stops the current song (if any), removes the most recently played song from the history, and puts these songs at the head of the queue. When loop mode is on, the songs at the tail of the song queue are used instead of the most recently played songs in the history. Arguments: A single integer that tells how far back in the history list to retreat. A value of 1 will cause the most recent song to play, 2 will cause the second most recent song to play, and so on. If no argument is given, a value of 1 is assumed. Return value: Nothing meaningful. boolean "putback" () Places the currently playing song at the beginning of the queue. Arguments: None. Return value: Nothing meaningful. int "queue_length" () Returns the number of items in the song queue. Arguments: None. Return value: The number of items in the song queue. boolean "reconfigure" () Tells the server to reread its player configuration file. Arguments: None. Return value: Nothing meaningful. boolean "remove" (base64) boolean "remove" (base64, array) Removes all items that match the given regular expression. Arguments: A regular expression that specifies which items to remove. * Optionally, an array of integers may be given as a second argument. This argument represents a range to which the removal will be limited. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. boolean "replace" (array) Replaces the contents of the queue with the given items. This is equivalent to calling clear() and prepend() in succession, except that this operation is atomic. Argument: An array of (base64-encoded) strings, representing the items to be added. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server isn't aware of the client's current working directory. Return value: Nothing meaningful. boolean "reverse" () boolean "reverse" (array) Reverses the order of the items in the queue. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is affected. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. boolean "run_queue" () Allows new songs to be played again after halt_queue() has been called. Arguments: None. Return value: Nothing meaningful. boolean "runqueue" () Allows new songs to be played again after halt_queue() has been called. Arguments: None. Return value: Nothing meaningful. boolean "set_history_limit" (int) Sets the limit on the size of the history list stored in memory. This will irrevocably discard history entries if the new limit is lower than the current size of the history list. Arguments: The new maximum number of history entries. If this value is negative, the history limit will be set to zero. Return value: Nothing meaningful. boolean "set_loop_mode" (boolean) Turns loop mode on or off. If loop mode is on, songs are returned to the end of the song queue after they finish playing. If loop mode is off, songs that have finished playing are not returned to the queue. Arguments: True if you want to turn loop mode on, False if you want to turn it off. Return value: Nothing meaningful. base64 "showconfig" () Returns a textual description of the server's player configuration. Arguments: None. Return value: A (base64-encoded) string that shows which programs will be used to play the various file-types recognized by the Moosic server. boolean "shuffle" () boolean "shuffle" (array) Rearrange the contents of the queue into a random order. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is affected. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. boolean "skip" () Skips the rest of the current song to play the next song in the queue. This only has an effect if there actually is a current song. Arguments: None. Return value: Nothing meaningful. boolean "sort" () boolean "sort" (array) Arranges the contents of the queue into sorted order. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is affected. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. boolean "stop" () Stops playing the current song and stops new songs from playing. The current song is returned to the head of the song queue and is not recorded in the history list. If loop mode is on, the current song won't be placed at the end of the song queue when it is stopped. Arguments: None. Return value: Nothing meaningful. boolean "sub" (base64, base64) boolean "sub" (base64, base64, array) Performs a regular expression substitution on the items in the queue. Arguments: The first is a (base64-encoded) regular expression that specifies the text to be replaced. * The second argument is the (base64-encoded) string that will be used to replace the first occurrence of the regular expression within each queue item. Any backslash escapes in this string will be processed, including special character translation (e.g. "\n" to newline) and backreferences to groups within the match. * Optionally, an array of integers may be given as a third argument. This argument represents a range to which the substitution will be limited. This range is interpreted in the same way as the range argument in other Moosic methods. * If performing a replacement changes an item in the queue into the empty string, then it is removed from the queue. Return value: Nothing meaningful. boolean "sub_all" (base64, base64) boolean "sub_all" (base64, base64, array) Performs a global regular expression substitution on the items in the queue. Arguments: The first is a (base64-encoded) regular expression that specifies the text to be replaced. * The second argument is the (base64-encoded) string that will be used to replace all occurrences of the regular expression within each queue item. Any backslash escapes in this string will be processed, including special character translation (e.g. "\n" to newline) and backreferences to the substrings matched by individual groups in the pattern. * Optionally, an array of integers may be given as a third argument. This argument represents a range to which the substitution will be limited. This range is interpreted in the same way as the range argument in other Moosic methods. * If performing a replacement changes an item in the queue into the empty string, then it is removed from the queue. Return value: Nothing meaningful. array "system.listMethods" () Return an array of all available XML-RPC methods on this server. string "system.methodHelp" (string) Given the name of a method, return a help string. array "system.methodSignature" (string) Given the name of a method, return an array of legal signatures. Each signature is an array of strings. The first item of each signature is the return type, and any others items are parameter types. array "system.multicall" (array) Process an array of calls, and return an array of results. Calls should be structs of the form {'methodName': string, 'params': array}. Each result will either be a single-item array containg the result value, or a struct of the form {'faultCode': int, 'faultString': string}. This is useful when you need to make lots of small calls without lots of round trips. boolean "toggle_loop_mode" () Turns loop mode on if it is off, and turns it off if it is on. If loop mode is on, songs are returned to the end of the song queue after they finish playing. If loop mode is off, songs that have finished playing are not returned to the queue. Arguments: None. Return value: Nothing meaningful. boolean "toggle_pause" () Pauses the current song if it is playing, and unpauses if it is paused. Arguments: None. Return value: Nothing meaningful. boolean "unpause" () Unpauses the current song. Arguments: None. Return value: Nothing meaningful. string "version" () Returns the Moosic server's version string. Arguments: None. Return value: The version string for the Moosic server. Section 4: Writing a Moosic Client in Python As demonstrated in section 0, communicating with a Moosic server is very easy to do with Python. In this section, I'll merely elaborate on the details that were omitted from section 0 for the sake of brevity. First of all, you should know that LocalMoosicProxy() can be called with a filename argument to specify the location of the Moosic server's socket file. This is useful if moosicd was started with the "-c" option. Refer to the moosic_factory.py module's documentation (moosic_factory.html). Next, note that many of the Moosic server's methods accept or return special types of objects from the xmlrpclib module, namely Boolean and Binary. These object types serve the purpose of bridging the small mismatch between the data-types supported by XML-RPC and Python's intrinsic data-types. Boolean objects present no unusual problem, since they evaluate to a correct truth value without any extra effort. However, you must take care when using the Moosic methods that accept or return Binary objects. Read the documentation for the xmlrpclib module for details on how to work with these objects. The basic technique boils down to wrapping up a string inside a Binary object before sending it to the server, and using the "data" attribute to access the string data within the Binary objects returned by the server. Regular strings can't be used because XML-RPC's normal string data-type can't handle multiple 8-bit strings within a single request if the strings use different encodings. Section 5: Writing a Moosic Client in Another Language If you are not using Python to write your Moosic client, the first issue to deal with is deciding upon an XML-RPC implementation. For most popular programming languages, there are multiple XML-RPC implementations available. Most of the possibilities are listed at . Since XML-RPC is an open specification, you can create your own implementation if you don't like any of the ones that already exist. Once you've got an XML-RPC library that you like, the big hurdle to overcome is to make that library send its RPC calls over a Unix socket instead of an IP socket. I was able to do this pretty easily with Python's xmlrpclib since it is designed to allow pluggable transport methods: all I had to do was subclass my own Transport type and plug it back into the original library's classes. (If your language and/or library of choice makes this task difficult, then you may begin to understand why some Python programmers are so smug.) After you are capable of sending XML-RPC requests through a Unix socket, you can go ahead and start sending requests to a Moosic server. Refer to the end of section 2 for information on how to address a Moosic server. Refer to section 3 for a list of valid server requests. If you can't be bothered to find or hack together an XML-RPC library that works with Unix sockets, then you can still talk to a Moosic server that is listening on an IP socket, but this is less than ideal since listening on an IP socket is not default behavior for most Moosic servers. Section 6: API Version History (ChangeLog) * 1.7 First implemented by moosicd 1.5.0. The following methods were added: skip, current_time The following methods had their behavior significantly changed: previous, next Specifically, the previous() method no longer activates queue advancement if it had been disabled before. This means that calling previous() no longer necessarily causes a song to start playing. The next() method was changed to more closely parallel the behavior of previous(): it takes a single optional integer argument to allow immediate advancement by more than one song at a time, and it has an effect even when no song is currently playing. The new skip() method implements the exact same behavior that was previously exhibited by next(). Last, and most importantly, the name of the default socket file for communicating with the server via unix sockets was changed from $CONFIG_DIR/socket-$HOSTNAME to $CONFIG_DIR/socket. If you are a Python programmer and you use the updated moosic_factory.py file from Moosic version 1.5.0, then you don't have to make any changes. Otherwise, you must change your client's code to connect to the file named "socket" instead of the file named "socket-something.example.com". If your client only talks to the Moosic server through TCP/IP, then you don't have to make any changes, of course. * 1.6 First implemented by moosicd 1.4.10. The following methods were added: getconfig * 1.5 First implemented by moosicd 1.4.6. The following methods were added: sub, sub_all, stop * 1.4 First implemented by moosicd 1.4.5. The following methods were added: previous * 1.3 First implemented by moosicd 1.4.4. The following methods were added: replace, replace_range, last_queue_update * 1.2 First implemented by moosicd 1.4.2. The following methods were added: cut_list, crop_list * 1.1 First implemented by moosicd 1.4.1. The following methods were added: is_looping, set_loop_mode, toggle_loop_mode * 1.0 First implemented by moosicd 1.4.0. The following methods were included: api_version, append, clear, crop, current, cut, die, filter, get_history_limit, halt_queue, haltqueue, history, indexed_list, insert, is_paused, is_queue_running, length, list, move, move_list, next, no_op, pause, prepend, putback, queue_length, reconfigure, remove, reverse, run_queue, runqueue, set_history_limit, showconfig, shuffle, sort, system.listMethods, system.methodHelp, system.methodSignature, system.multicall, toggle_pause, unpause, version moosic-1.5.6/doc/Makefile0000644000175000017500000000323211655324401015734 0ustar danieldaniel00000000000000# This makefile is used for converting the POD documents for Moosic into various # output formats, such as the manpage format. This requires GNU make, the # "install" utility program, python, and the pod2man and pod2html utilities (the # latter two are usually included with Perl). VERSION=1.5.6 PROGRAMS=moosic moosicd STATIC_DOCS=../README.txt ../License.txt ../ChangeLog ../NEWS \ Todo History Moosic_API.txt moosic_hackers.txt MANPAGES_1:=$(PROGRAMS:=.1) MANPAGES_3:=Moosic_API.3 HTMLDOCS:=$(PROGRAMS:=.html) Moosic_API.html .PHONY: all all: $(MANPAGES_1) $(MANPAGES_3) $(HTMLDOCS) - Moosic_API.pod: Moosic_API.intro.pod Moosic_API.sect0.pod Moosic_API.sect1.pod Moosic_API.sect2.pod Moosic_API.sect3.pod Moosic_API.sect4.pod Moosic_API.sect5.pod Moosic_API.sect6.pod cat $^ > $@ %.txt: %.pod pod2text -l $< $@ %.1: %.pod pod2man --section 1 --release "Moosic ${VERSION}" --center "" $< $@ %.3: %.pod pod2man --section 3 --release "Moosic ${VERSION}" --center "" $< $@ %.html: %.pod pod2html --podpath=. --htmlroot=. --infile=$< --outfile=$@ rm -f pod2htm* #.PHONY: install #install: all # install -d $(INSTALL_PREFIX) # install -d $(INSTALL_PREFIX)/share/man/man1 # install -m 644 $(MANPAGES_1) $(INSTALL_PREFIX)/share/man/man1/ # install -d $(INSTALL_PREFIX)/share/man/man3 # install -m 644 $(MANPAGES_3) $(INSTALL_PREFIX)/share/man/man3/ # install -d $(INSTALL_PREFIX)/share/doc/moosic # install -m 644 $(STATIC_DOCS) $(PYTHON_MODULES) $(INSTALL_PREFIX)/share/doc/moosic/ # install -d $(INSTALL_PREFIX)/share/doc/moosic/html # install -m 644 $(HTMLDOCS) $(INSTALL_PREFIX)/share/doc/moosic/html/ .PHONY: clean clean: rm -f $(MANPAGES_1) $(MANPAGES_3) $(HTMLDOCS) moosic-1.5.6/doc/Moosic_API.sect5.pod0000644000175000017500000000312607723127406017755 0ustar danieldaniel00000000000000=head1 Section 5: Writing a Moosic Client in Another Language If you are not using Python to write your Moosic client, the first issue to deal with is deciding upon an XML-RPC implementation. For most popular programming languages, there are multiple XML-RPC implementations available. Most of the possibilities are listed at L. Since XML-RPC is an open specification, you can create your own implementation if you don't like any of the ones that already exist. Once you've got an XML-RPC library that you like, the big hurdle to overcome is to make that library send its RPC calls over a Unix socket instead of an IP socket. I was able to do this pretty easily with Python's xmlrpclib since it is designed to allow pluggable transport methods: all I had to do was subclass my own Transport type and plug it back into the original library's classes. (If your language and/or library of choice makes this task difficult, then you may begin to understand why some Python programmers are so smug.) After you are capable of sending XML-RPC requests through a Unix socket, you can go ahead and start sending requests to a Moosic server. Refer to the end of section 2 for information on how to address a Moosic server. Refer to section 3 for a list of valid server requests. If you can't be bothered to find or hack together an XML-RPC library that works with Unix sockets, then you can still talk to a Moosic server that is listening on an IP socket, but this is less than ideal since listening on an IP socket is not default behavior for most Moosic servers. moosic-1.5.6/doc/moosicd.10000644000175000017500000002670110037722705016023 0ustar danieldaniel00000000000000.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.14 .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "MOOSICD 1" .TH MOOSICD 1 "2004-04-02" "Moosic 1.5.1" "" .SH "NAME" moosicd \- the server for the Moosic jukebox system. .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBmoosicd\fR \fB\-\-help\fR|\fB\-h\fR|\fB\-\-version\fR|\fB\-v\fR .PP \&\fBmoosicd\fR [\fB\-\-history\-size\fR|\fB\-s\fR \fIsize\fR] [\fB\-\-config\fR|\fB\-c\fR \fIdirectory\fR] [\fB\-\-quiet\fR|\fB\-q\fR|\fB\-\-debug\fR|\fB\-d\fR] [\fB\-S\fR|\fB\-\-stdout\fR] [\fB\-t\fR|\fB\-\-tcp\fR \fIport\fR] [\fB\-T\fR|\fB\-\-tcp\-also\fR \fIport\fR] [\fB\-l\fR|\fB\-\-local\-only\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\fBmoosicd\fR is the server for the Moosic jukebox system. It sits around, waiting to respond to commands given by a Moosic client (such as moosic(1)). It also maintains a queue of items to be played, and if this queue is not empty, it pops off the first item from the head of the queue and executes a user-configurable command on that item. When this command terminates, \&\fBmoosicd\fR goes on to the next item in its queue, assuming that the queue is not empty. .PP \&\fBmoosicd\fR is not meant to be used as a system-wide daemon that serves all users on a system. Rather, every user that wishes to use Moosic should start a separate instance of \fBmoosicd\fR, and one user cannot communicate with the Moosic server of another user without taking special measures (i.e. using the \fB\-c\fR or \&\fB\-t\fR options to \fBmoosic\fR). .SH "OPTIONS" .IX Header "OPTIONS" \&\fBmoosicd\fR is designed so that you normally don't need to use any of these options. .IP "\fB\-h\fR, \fB\-\-help\fR" 4 .IX Item "-h, --help" Prints help text and exits. .IP "\fB\-v\fR, \fB\-\-version\fR" 4 .IX Item "-v, --version" Prints version information and exits. .IP "\fB\-s\fR \fIsize\fR, \fB\-\-history\-size\fR \fIsize\fR" 4 .IX Item "-s size, --history-size size" \&\fBmoosicd\fR remembers the file names of previous songs that it played. This option sets the maximum size of this history list. The default value is 50. .IP "\fB\-f\fR, \fB\-\-foreground\fR" 4 .IX Item "-f, --foreground" By default, \fBmoosicd\fR detaches itself from the current terminal and puts itself in the background (i.e. it becomes a daemon). This option disables such behavior. .IP "\fB\-S\fR, \fB\-\-stdout\fR" 4 .IX Item "-S, --stdout" By default, \fBmoosicd\fR logs almost all of its printed output to a file. When this option is used, the output is instead printed to the standard output device. This also prevents the program from putting itself in the background and detaching from the current terminal. .IP "\fB\-q\fR, \fB\-\-quiet\fR" 4 .IX Item "-q, --quiet" This option suppresses almost all printed output from \fBmoosicd\fR. Note that, by default, \fBmoosicd\fR prints this output to a file, not the standard output device. .IP "\fB\-d\fR, \fB\-\-debug\fR" 4 .IX Item "-d, --debug" This option causes \fBmoosicd\fR to print lots and lots of messages about what it's doing. These messages are usually quite superfluous and bothersome. Note that unless the \fB\-S\fR option is used these messages will appear in the log file instead of the standard output device. .IP "\fB\-c\fR \fIdirectory\fR, \fB\-\-config\fR \fIdirectory\fR" 4 .IX Item "-c directory, --config directory" Specifies the directory where moosicd should keep the various files that it uses. The default directory is \fI~/.moosic/\fR. This option is useful only in extraordinary circumstances. If this option is used, any Moosic clients that wish to communicate with \fBmoosicd\fR must be told to use the specified directory instead of the default. .IP "\fB\-t\fR \fIport\fR, \fB\-\-tcp\fR \fIport\fR" 4 .IX Item "-t port, --tcp port" This option directs the server to listen to the given \s-1TCP\s0 port number for client requests instead of using the normal communication method. Use of this option without the \fB\-l\fR option is \fIhighly discouraged\fR unless you know what you are doing because there is no guarantee that \fBmoosicd\fR is secure against malicious input from a remote location. Note that an instance of \fBmoosicd\fR which is started with this option will not accept requests from a client that is using the normal communication method. .IP "\fB\-T\fR \fIport\fR, \fB\-\-tcp\-also\fR \fIport\fR" 4 .IX Item "-T port, --tcp-also port" This option directs the server to listen to the given \s-1TCP\s0 port number for client requests in addition to using the normal communication method. Use of this option without the \fB\-l\fR option is \fIhighly discouraged\fR unless you know what you are doing because there is no guarantee that \fBmoosicd\fR is secure against malicious input from a remote location. .IP "\fB\-l\fR, \fB\-\-local\-only\fR" 4 .IX Item "-l, --local-only" This directs the server to only listen for \s-1TCP\s0 connections that originate from the local computer, refusing connections from remote hosts. This only has an effect when \fB\-\-tcp\fR or \fB\-\-tcp\-also\fR is used. .SH "CONFIGURATION" .IX Header "CONFIGURATION" \&\fBmoosicd\fR figures out how to play items in its queue by consulting its configuration file, which associates string patterns (in the form of Perl-compatible regular expressions) with commands. .PP The format of this file is as follows: Every pair of lines forms a unit. The first line in a pair is a regular expression that will be matched against items in the queue. The second line in a pair is the command that will be used to play any items that match the regular expression. The name of the item to be played will be appended to the end of this command, unless the command line includes a special substitution string. .PP The simplest kind of substitution string is \*(L"$item\*(R". Every occurrence of \&\*(L"$item\*(R" in the command will be replaced with the name of the song to be played. The other kind of substitution is called \*(L"matched group substitution\*(R", and is used to refer to specific parts of the name of the song to be played. .PP The command will not be interpreted by a shell, so don't bother trying to use shell variables or globbing or I/O redirection, and be mindful of how you use quotes and parentheses. If you need any of these fancy features, wrap up the command in a real shell script (and remember to use an \*(L"exec\*(R" statement in your shell script to invoke the program that does the actual song playing, otherwise Moosic won't be able to do things like stop or pause the song). .PP Blank lines and lines starting with a '#' character are ignored. Regular expressions specified earlier in this file take precedence over those specified later. .SH "FILES" .IX Header "FILES" \&\fBmoosicd\fR makes use of several files, all of which are found in the \&\fI.moosic/\fR directory in the home directory of the user who invoked the program (unless the \-c or \-\-config option is used). .IP "\fIconfig\fR" 4 .IX Item "config" This is the configuration file that \fBmoosicd\fR uses to associate file-types to player commands, as explained above. .IP "\fIserver_log\fR" 4 .IX Item "server_log" \&\fBmoosicd\fR logs short notices of its activities to this file unless the \fB\-S\fR option is used. It usually contains nothing more than a history of what songs have been played. .IP "\fIplayer_log\fR" 4 .IX Item "player_log" This file contains the output of the player commands which are spawned by \&\fBmoosicd\fR. .IP "\fIsocket\fR" 4 .IX Item "socket" This is a socket file which is (normally) used to allow Moosic clients to contact the Moosic server. If \fBmoosicd\fR isn't shut down properly, this file will need to be removed by hand. You should leave this file alone under other circumstances. .SH "SEE ALSO" .IX Header "SEE ALSO" moosic(1), the standard command-line Moosic client. .PP The chapter entitled \*(L"Regular Expression Syntax\*(R" from the section dealing with the \fBre\fR module in the \fIPython Library Reference\fR, for details on the syntax of a regular expression. .SH "AUTHOR" .IX Header "AUTHOR" Daniel Pearson moosic-1.5.6/doc/History.txt0000644000175000017500000001177307653354042016516 0ustar danieldaniel00000000000000This program has a long and boring history, with many past incarnations. The primordial ancestor of moosic harks back to the age when all the music that I stored on my computer was in MIDI format, this being the age of tiny hard drives and slow Internet connections. I quickly found that the very best software for playing MIDI music was Timidity. My most frequent usage of Timidity involved a moderately long series of command-line options, so I used a shell alias to abbreviate this. Now, instead of typing "timidity -idqq -Od", I would just type "tim". "OK, big deal, a shell alias," you might say to yourself. Well yeah. I told you this would be boring. But it's not over yet. After a while, my music collection started to include more interesting formats, including MP3, WAV, AU, and the various MOD formats. And so I wound up doing what most people probably do: I used a different command to play every different type of file. Of course, this got old very quickly. To me, it was all music. What did I care what format it was in? Even worse, what if I wanted to queue up a long list of songs to be played? I would have to use a different command for every other file, and shuffling the list of songs to be played would be next to impossible. So what I did was write a shell script named "timmy" that processed files listed on the command-line in order by identifying the file's extension, and then calling the appropriate player for that file. At first, I did any shuffling before timmy was called by using the randomize command (included with the Tracker MOD player) inside of backticks. I soon tired of this and put a randomization option directly into timmy. The next big feature to be added was directory recursion. Why say "timmy directory/*" when I could avoid hitting that hard-to-reach asterisk by typing "timmy directory" instead? It was around this time, that I renamed timmy to "moosic". I experimented with several ways of implementing this, and the last I used was a technique where the script to called itself every time it encountered a directory, passing the results of a "find" on that directory to the new invocation. The problem with this was that I wanted to do more sophisticated forms of shuffling, and this was just a nightmare when you are programming in a language that doesn't have real lists. Handling files with spaces in their names was also a nightmare. So I rewrote moosic in Perl. At first it was a fairly direct transliteration, but it soon evolved into a very different sort of beast, internally. However, I still could not manage to properly implement the types of shuffling that I wanted. I eventually came to the conclusion that Perl's API for working with lists was just too annoying. So I rewrote moosic in Python. I also became a bit more ambitious in my goals. I was starting to get rather annoyed that if I queued up a bunch of songs for playing, I had to wait for the current batch of songs to finish playing before being able to queue up more songs to be played afterward. I realized that what I really wanted was a jukebox program. I sat down and sketched out a little design for what I wanted, and decided that the client/server model would be the best solution to this problem. This changed the user interface considerably. Previously, one would run the script on a bunch of files, and it would not return until all the files had finished being played. Now, one uses the client to send a short command to the server and then returns immediately. I originally used UDP Internet sockets to facilitate communication between the client and the server, but I eventually switched to datagram-oriented Unix Domain sockets because they provide reliability and because they only allow communication among processes on the local machine (which means no security risk from remote network attacks). Since Moosic 1.3, stream-oriented sockets are used instead of datagram-oriented sockets to resolve some ugliness issues that plagued earlier versions. Version 1.4 brought about a big change in the format of the data that the client and server sent to each other. Previously, the data was formatted according to a simplistic RPC protocol of my own design. Eventually the limitations of this protocol (such as lack of data-types) began to catch up with me. I could have extended and improved the design of the existing protocol (and, in fact, I had done so once before between version 1.2 and version 1.3), but design work is always very hard, so I put this off for as long as I could. Until one day, someone suggested that I provide an interface to allow clients to use XML-RPC to send requests to the server. After researching XML-RPC, I decided that it would be an excellent replacement for my home-brewed RPC protocol. This decision meant a lot less work for me, since I wouldn't have to maintain the implementation of my own protocol anymore. It also meant a lot less work for third parties who were developing alternative clients, since they wouldn't have to reimplement a non-standard protocol or integrate my own implementation. moosic-1.5.6/doc/Moosic_API.intro.pod0000644000175000017500000000156707757737557020121 0ustar danieldaniel00000000000000=head1 NAME Moosic_API - How to write your own Moosic client. =head1 Introduction "moosicd" is a program that implements the server portion of the Moosic jukebox system. This server provides services for manipulating a queue of songs to be played, as well as for controlling the playing of these songs. A Moosic client sends requests to the server, and receives data in response to these requests. The "moosic" command line utility is the canonical Moosic client, and provides a way to control a Moosic server from an interactive command shell or from a shell script. However, you might not be satisfied by the interface that is provided by the "moosic" command, so I have written this document to describe how to communicate with a Moosic server in your own programs. [This document was written by Daniel Pearson , and has been placed into the public domain.] moosic-1.5.6/doc/moosic.pod0000644000175000017500000007152410625506037016305 0ustar danieldaniel00000000000000=head1 NAME moosic - a command-line client for the Moosic jukebox system. =head1 SYNOPSIS B [I] I [I] [I] =head1 DESCRIPTION The B program is the command-line interface to the Moosic jukebox system. It communicates with L(1), the Moosic server, querying the server for information and telling the server what to do. B will not be able to do very much unless B is running. When B isn't already running, B will automatically start it for you, unless you specifically request otherwise (with the --no-startserver option). =head1 USAGE B works by sending a command to the Moosic server and returning the response, if any. The first non-option argument given to B is the name of the command to be performed. This command name is case-insensitive, and all non-alphanumeric characters in it are ignored. You can use the "help" command to quickly and easily view the names of all the available commands and to get a brief description of individual commands. You can also use C to display the short descriptions of all the commands at once. The L section below lists the full details of each command. There are very many commands, so you should start by just learning a few commonly used commands, and only learning others as you feel the need. I recommend starting with the following short command vocabulary: add, list, stop, play, and shuffle. For example, C adds the file F (in the current directory) to the end of the song queue and returns you immediately back to your shell prompt without printing any output (unless an error occurs). Compare with C, which will list the contents of the song queue. Note that if the song queue is empty, C will not display anything. =head1 OPTIONS Most of the options for B are only relevant if they are used with one of the commands that take a I argument. See L for the definition of a I. The only shuffling options that don't mutually exclude each other are B<-d> and B<-a>. Shuffling options that are named later on the command line take precedence over ones that occur earlier. All options must be named immediately before the I given to B or immediately after the I; options placed within the list of the command's arguments will not be interpreted as options. =over =item B<-g>, B<--shuffle-global> This option causes B to shuffle the entire I after directory expansion has taken place, before sending the I to the Moosic server. This is the default behavior. This option is only meaningful if used in conjunction with a command that accepts a I. =item B<-d>, B<--shuffle-dir> This option causes B to shuffle the results of expanding the directories named in the I. This option is only meaningful if used in conjunction with a command that accepts a I. =item B<-a>, B<--shuffle-args> This option causes B to shuffle the actual command line arguments that comprise the I. This option is only meaningful if used in conjunction with a command that accepts a I. =item B<-o>, B<--inorder> When this option is used, B doesn't shuffle the I named on the command line at all. Rather, the order specified on the command line is preserved. This option is only meaningful if used in conjunction with a command that accepts a I. =item B<-s>, B<--sort> When this option is used, B sorts the I lexicographically after it has been expanded (through directory recursion or auto-finding or the like). The order specified on the command line is ignored. This option is only meaningful if used in conjunction with a command that accepts a I. =item B<-r>, B<--no-recurse> Using this option prevents B from replacing directories named in the I with a recursive traversal of their contents. =item B<-n>, B<--no-file-munge> Using this option prevents B from modifying the names in the expanded I. Normally, B converts relative filenames into absolute filenames before sending the names to B, but this is generally not desirable behavior if you want to insert items that aren't local files into the queue (such as URLs). This option is only meaningful if used in conjunction with a command that accepts a I. =item B<-i>, B<--ignore-case> Treats any given regular expressions as if they were case-insensitive. This option is only meaningful if used in conjunction with a command that accepts one or more regular expressions as arguments. This option is syntactic sugar, since the regular expressions supported by Moosic can also be made case-insensitive by including "(?i)" within the regular expression. =item B<-f>, B<--auto-find> This option causes each string in the I with the results of performing a "fuzzy" search for music files. "Fuzzy" matching is done by simplifying all the candidate filenames (by lowering the case and removing all non-alphanumeric characters except slashes) and then testing to see if the search string (which has been similarly simplified) is contained in any of the filenames. The list of candidate filenames is obtained by recursively traversing the file hierarchy rooted at the directory specified by the B<--music-dir> option (which has a default value of F<~/music/>). For example, if you use C, and the file F<~/music/Meat_Puppets/Severed_Goddess_Hand.mp3> exists, then this file will be included in the list of files to be added to the queue. Similarly, if you use C, and the directory F<~/music/J/Jane's Addiction/> exists, then all the files in this directory (and its subdirectories) will be included in the list of files to be prepended to the queue. This option is only meaningful if used in conjunction with a command that accepts a I. Beware that using this option can cause B to take a long time to complete if the directory tree being searched contains a very large number of files. =item B<-F>, B<--auto-grep> This option enables behavior very much like that of the B<--auto-find> option, except that regular expression searching is used instead of the "fuzzy" search scheme. Specifically, each string in the I is treated as a regular expression, and is replaced with all the filenames that match the expression. As with B<--auto-find>, the filenames that are eligible for matching are obtained by traversing the directory named with the B<--music-dir> option (defaulting to F<~/music/> if B<--music-dir> is not used). Essentially, C is semantically equivalent to C, but is syntactically a lot sweeter. This option is only meaningful if used in conjunction with a command that accepts a I. Beware that using this option can cause B to take a long time to complete if the directory tree being searched contains a very large number of files. =item B<-m> I, B<--music-dir> I This option controls which directory is used for searching when the "auto-find" or "auto-grep" feature is enabled. These automatic searches are limited to the file hierarchy rooted at the directory specified by this option. When this option is not used, the F<~/music/> directory is used as a default. This option is only meaningful if either B<--auto-find> or B<--auto-grep> is used. =item B<-S>, B<--showcommands> Prints a list of the commands that may be used with B and then exits. Note that this output is quite copious, so you will probably want to pipe it to a text pager, such as B. =item B<-h>, B<--help> Prints a short help message that explains the command line options and then exits. =item B<-v>, B<--version> Prints version information and then exits. =item B<-c> I, B<--config-dir> I This option is not needed under normal circumstances. It should only be used if you want B to communicate with an instance of B which was invoked with the B<-c>/B<--config> option. Using this option tells B to search the specified directory for the files which are usually found in I<~/.moosic/>. =item B<-t> I:I, B<--tcp> I:I This option tells B to communicate with a Moosic server that is listening to the specified TCP/IP port on the specified host. Running a Moosic server that accepts requests via TCP/IP is not recommended because it is a security risk. =item B<-N>, B<--no-startserver> This option prevents B from trying to automatically start B if it can't contact a Moosic server. =item B<-U>, B<--allow-unplayable> This option allows songs that the server doesn't know how to play to be added into the song queue. =item B<-C>, B<--current-in-list> This option causes the currently playing song to be printed at the top of the output of the "list" and "plainlist" commands. It has no effect if an argument is given to these commands or if used with other commands. =back =head1 COMMANDS Any of these commands may be specified with any mixture of upper-case and lower-case letters, and non-alphabetic characters (such as '-') may be omitted. Many of these commands accept a I argument. A I is a pair of colon-separated numbers. Such a I addresses all items whose index in the song queue is both greater than or equal to the first number and less than the second number. For example, "3:7" addresses items 3, 4, 5, and 6. If the first number in the pair is omitted, then the I starts at the beginning of the song queue. If the second number in the pair is omitted, then the I extends to include the last item in the song queue. A I can also be a single number (with no colon), in which case it addresses the single item whose index is that of the given number. Negative numbers may be used to index items from the end of the list instead of the beginning. Thus, -1 refers to the last item in the song queue, -2 refers to the second-to-last item, etc. Beware that a negative number that immediately follows a moosic I is liable to be incorrectly interpreted as an option, so option processing should be explicitly terminated with an argument of "--" between the I and the number. This is illustrated by the following example, which removes the last item in the queue: C Alternatively (and perhaps more conveniently), you can prevent negative numbers from being interpreted as options by preceding the range with a single character that can't be mistaken for a number or an option (i.e. any character that isn't a digit or a dash). Example: C. You can also place such a character at the end of the range if you think it makes it look prettier. Example: C. The bracketing characters surrounding a range need not be the same: C. Notice how the preceding example surrounded the range in quotes to prevent the shell from treating the "[" and "]" characters specially (since shells have a habit of doing things like that). =head2 Querying for information These commands print useful bits of information to standard output. =over =item B [I ...] Prints a brief description of the moosic commands named as arguments. If no arguments are given, a list of all the available moosic commands is printed. =item B Print the name of the song that is currently playing. =item B An alias for "current". =item B [I] Print the amount of time that the current song has been playing. By default, this time is printed in a format of "hours:minutes:seconds", but if a different format is desired, a string argument can be given to specify it. The format should be a string that is appropriate for passing to the strftime(3) function. =item B [I] Print the list of items in the current song queue. A whole number is printed before each item in the list, indicating its position in the queue. If a range is specified, only the items that fall within that range are listed. Remember that the song queue does not contain the currently playing song. =item B [I] Print the current song queue without numbering each line. If a range is specified, only the items that fall within that range are listed. This output is suitable for saving to a file which can be reloaded by the "pl-append", "pl-prepend", "pl-insert", and "pl-mixin" commands. =item B [I] Print a list of items that were recently played. The times mentioned in the output of this command represents the time that a song finished playing. If a number is specified, then no more than that number of entries will be printed. If a number is not specified, then the entire history is printed. Note that B limits the number of items stored in its history list. =item B [I] An alias for "history". =item B Print the current state of the music daemon. =item B An alias for "state". =item B Print the number of items in the queue. =item B An alias for "length". =item B Show whether the current song is paused or not. If the song is paused, "True" is printed and B returns normally. If the song is not paused, "False" is printed and B returns with a non-zero exit status (which happens to be 2 for no particular reason). =item B Show whether the server is in loop mode. If the server is in loop mode, "True" is printed and B returns normally. If not, "False" is printed and B returns with a non-zero exit status (which happens to be 2 for no particular reason). =item B Show whether the server is advancing through the song queue. If the server is advancing, "True" is printed and B returns normally. If not, "False" is printed and B returns with a non-zero exit status (which happens to be 2 for no particular reason). =item B Print version information for both the client and the server, and then exit. =back =head2 Adding to the song queue These commands will add to the queue of items to be played. Many of these commands accept a I argument. A I is a list of one or more files or directories. Any directories named in the list will be replaced by a list of files produced by recursively traversing the contents of the directory (unless the B<--no-file-munge> option or B<--no-recurse> option is being used). Depending on the shuffling options specified when invoking B, the list will be shuffled before being added to the Moosic server's queue. =over =item B I Add the files to be played to the end of the song queue. =item B I An alias for "append". =item B I ... Add the items listed in the given playlist files to the end of the song queue. If "-" (a single dash) is given as the name of a playlist file, data will be read from from standard input instead of trying to read from a file named "-". =item B I ... An alias for "pl-append". =item B I Add the files to be played to the beginning of the song queue. =item B
 I

An alias for "prepend".

=item B I ...

Add the items listed in the given playlist files to the beginning of the song
queue.  If "-" (a single dash) is given as the name of a playlist file, data
will be read from from standard input instead of trying to read from a file
named "-".

=item B I

Add the files to the song queue and reshuffle the entire song queue.

=item B I ...

Add the items listed in the given playlist files to the song queue and reshuffle
the entire song queue.  If "-" (a single dash) is given as the name of a
playlist file, data will be read from from standard input instead of trying to
read from a file named "-".

=item B I

Replace the current contents of the song queue with the songs contained in the
filelist.

=item B I ...

Replace the current contents of the song queue with the songs named in the given
playlists.

=item B I I

Insert the given items at a given point in the song queue.  The items are
inserted such that they will precede the item that previously occupied the
specified index.

=item B I ... I

Insert the items specified in the given playlist files at a specified point in
the song queue.  If "-" (a single dash) is given as the name of a playlist file,
data will be read from from standard input instead of trying to read from a file
named "-".

=item B

Reinsert the current song at the start of the song queue.

=item B I

Adds the file list to the end of the song queue, but only after rearranging it
into a "staggered" order.  This staggered order is very similar the order created
by the B command (described below).  Each element of the file list
(before replacing directories with their contents) specifies a category into
which the expanded file list will be divided.  The staggered order of the list
being added is formed by taking the first item from each category in turn until
all the categories are empty.  This may be a bit difficult to understand without
an example, so here is a typical case:

Initially, the queue contains a few items.

    [0] /music/a.ogg
    [1] /music/b.mp3
    [2] /music/c.mid

Additionally, there are two directories that each contain a few files:

    $ ls /music/X/ /music/Y/
    X:
    1.ogg  2.ogg  3.ogg

    Y:
    1.ogg  2.ogg  3.ogg  4.ogg

After executing C, the queue now
contains:

    [0] /music/a.ogg
    [1] /music/b.mp3
    [2] /music/c.mid
    [3] /music/Y/1.ogg
    [4] /music/X/1.ogg
    [5] /music/Y/2.ogg
    [6] /music/X/2.ogg
    [7] /music/Y/3.ogg
    [8] /music/X/3.ogg
    [9] /music/Y/4.ogg

=item B I

Adds the given file list to the queue in an interleaved fashion.  More
specifically, the new song queue will consist of a list that alternates between
the items from the given file list and the items from the existing song queu.
For example, if the queue initially contains:

    [0] /music/a.ogg
    [1] /music/b.mp3
    [2] /music/c.mid

And the F directory contains:

    1.ogg  2.ogg  3.ogg  4.ogg

Then, after executing C, the queue will
contain:

    [0] /music/Y/1.ogg
    [1] /music/a.ogg
    [2] /music/Y/2.ogg
    [3] /music/b.mp3
    [4] /music/Y/3.ogg
    [5] /music/c.mid
    [6] /music/Y/4.ogg

=item B I I

Inserts the given songs into the current song queue with a regular frequency
that is specified with the given I argument (which must be an
integer).  

For example, if the queue initially contains:

    [0] /music/a.mod
    [1] /music/b.mod
    [2] /music/c.mod
    [3] /music/d.mod
    [4] /music/e.mod
    [5] /music/f.mod
    [6] /music/g.mod

And the F directory contains:

    aleph.wav  bet.wav  gimmel.wav

Then, after executing C, the queue will
contain:

    [0] aleph.wav
    [1] /music/a.mod
    [2] /music/b.mod
    [3] bet.wav
    [4] /music/c.mod
    [5] /music/d.mod
    [6] gimmel.wav
    [7] /music/e.mod
    [8] /music/f.mod
    [9] /music/g.mod

=back

=head2 Removing from the song queue

These commands will remove from the queue of items to be played.

=over

=item B I

Removes all song queue items that fall within the given range.

=item B I

An alias for "cut".

=item B I

Removes all song queue items that do not fall within the given range.

=item B I ...

Remove all song queue items that match the given regular expression.  If multiple
regular expressions are given, any song that matches any one of the expressions
will be removed.

=item B I ...

Remove all song queue items that do not match the given regular expression.  If
multiple regular expressions are given, only those songs that match all the
regular expressions will remain afterward.

=item B

Clear the song queue.

=item B

Clear the song queue and stop the current song.

=back

=head2 Rearranging the song queue

These commands let you change the order of the items in the queue.

=over

=item B I I

Moves all items in the given range to a new position in the song queue.
If you want to move items to the end of the queue, use C<`moosic length`> as the
final argument.  For example, to move the first 10 songs to the end of the
queue, use the following command: C

=item B I I

Moves all items that match the given regular expression to a new position in
the song queue.

=item B I I

Causes the songs contained within the two specified ranges to trade places.

=item B [I]

Reshuffle the song queue.  If a range is specified, only items that fall within
that range will be shuffled.

=item B [I]

An alias for "reshuffle".

=item B [I]

Rearrange the song queue in sorted order.  If a range is specified, only items
that fall within that range will be sorted.

=item B [I]

Reverse the order of the song queue.  If a range is specified, only items that
fall within that range will be reversed.

=item B I ...

For each specified regular expression, the items in the song queue that match
that expression are removed from the queue and gathered into their own list.
All of these lists (plus the list of items that did not match any regular
expression) are then stitched back together through simple concatenation.
Finally, this unified list replaces the contents of the song queue.

The items that match a particular regular expression will remain in the same
order with respect to each other.  Each group of matched items will appear in
the reordered song queue in the order that the corresponding regular
expressions were specified on the command line.

=item B I ...

For each specified regular expression, the items in the song queue that match
that expression are removed from the queue and gathered into their own list.
All of these lists are then merged together in a staggered fashion. All the
leftover items (i.e. the ones that weren't matched by any regex on the command
line) are appended to this unified list, which then replaces the contents of the
song queue.

For example, if you use C and the queue
originally contains only names that either contain the string "red" or "blue" or
"green", then the members of the reordered queue will alternate between "red"
items, "blue" items, and "green" items.  If the queue does contain items that
are neither "red" nor "green" nor "blue", then these will be collected and
placed at the end of the queue, after all the "red", "green", and "blue" items.

=item B I I [I]

Perform a regular expression substitution on all items in the song queue.  More
precisely, this searches each queue item for the regular expression specified by
the first argument, and replaces it with the text specified by the second
argument.  Any backslash escapes in the replacement text will be processed,
including special character translation (e.g. "\n" to newline) and
backreferences to groups within the match.  If a range is given, then the
substitution will only be applied to the items that fall within the range,
instead of all items.  Only the first matching occurrence of the pattern is
replaced in each item.

=item B I I [I]

This is identical to the "sub" command, except that all occurrences of the
pattern within each queue item are replaced instead of just the first
occurrence.

=back

=head2 General management

These commands affect the state of the Moosic server in various ways.

=over

=item B [I]

Stops the current song (if any), and jumps ahead to a song that is currently in
the queue.  The argument specifies the number of songs to be skipped, including
the currently playing song.  Its default value is 1.  The skipped songs are
recorded in the history as if they had been played.  If queue advancement is
disabled, this command merely stops the current song and removes the appropriate
number of songs from the queue, and does not cause a new song to be played.

=item B [I]

Retreats to a previously played song (from the history list) and begins playing
it if queue advancement is enabled.  If a number is given as an argument, then
the music daemon will retreat by that number of songs.  If no argument is given,
then the music daemon will retreat to the most recent song in the history.  More
precisely, this command stops the current song (without recording it in the song
history) and returns the most recently played song or songs to the queue.  This
command removes songs from the history when it returns them to the queue, thus
modifying the song history.

When loop mode is on, this command retreats into the tail end of the queue
instead of the song history.  This produces wrap-around behavior that you would
expect from loop mode, and does not modify the song history.

=item B

An alias for "previous".

=item B I

Jumps to the next song in the queue that matches the given regular expression.

=item B I

Jumps back to the most recent previous song that matches the given regular
expression.

=item B

Tell the music daemon to stop playing any new songs, but without interrupting
the current song.  In other words, this halts queue advancement.

=item B

An alias for "noadvance".

=item B

Tell the music daemon to resume queue advancement (i.e. play new songs when the
current one is finished).  Obviously, this has no effect if queue advancement
hasn't been disabled.

=item B

An alias for "advance".

=item B

Halts queue advancement if it is enabled, and enables advancement if it is
halted.

=item B

Tell the music daemon to stop playing the current song and stop processing the
song queue.  The current song is put back into the song queue and is not
recorded in the song history.

=item B

Suspend the current song so that it can be resumed at the exact same point at a
later time.  Note: this often leaves the sound device locked.

=item B

Unpause the current song, if the current song is paused, otherwise do nothing.

=item B

Tell the music daemon to resume playing.  (Use after "stop", "noadv", or
"pause".)

=item B

Turn loop mode on.  When loop mode is on, songs are returned to the end of the
queue when they finish playing instead of being thrown away.

=item B

Turn loop mode off.

=item B

Turn loop mode on if it is off, and turn it off if it is on.

=item B

Tell the music daemon to reload its configuration file.

=item B

An alias for "reconfigure".

=item B

Query and print the music daemon's filetype associations.

=item B [I]

Start a new instance of the music daemon (also known as B).  If option
arguments are given, they will be used as the options for invoking B.
The options that are accepted by B can be found in its own manual page,
L(1).

=item B

Tell the music daemon to quit.

=item B

An alias for "exit".

=item B

An alias for "exit".

=back

=head1 AUDIO CD SUPPORT

If you have the takcd program installed, and you have an appropriate entry for
it in the Moosic server's player configuration, then you can play audio CD
tracks with Moosic.  The following entry should be in F<~/.moosic/config>:

    (?i)^cda://(\S*)
    takcd \1

To put CD tracks into the song queue, you should name them with the prefix
"cda://", followed immediately by the number of the track you wish to play.  For
example, C will add the third track on the CD to the end
of the song queue.

The takcd program can be found at L.

=head1 FILES

=over

=item F

This is a socket file which is used to allow Moosic clients to contact the
Moosic server.  It is generally located in the I<~/.moosic/> directory, unless
B was invoked with the B<-c>/B<--config> option.

=back

=head1 SEE ALSO

L(1), for details on invoking the Moosic server by hand.

Various B commands accept regular expressions arguments.  The syntax
used for these regular expressions is identical to the syntax used by Python's
regular expression library.  The details of this syntax are explained in the
chapter entitled "Regular Expression Syntax"
L from the section dealing
with the B module in the I.

=head1 AUTHOR

Daniel Pearson 
moosic-1.5.6/doc/Moosic_API.sect3.pod0000644000175000017500000006231010300320624017731 0ustar  danieldaniel00000000000000
=over

=item array B ()

   Returns the version number for the API that the server implements.
    
       Arguments: None.
       Return value: The version number, which is a 2-element array of
           integers.  The first element is the major version, and the second
           element is the minor version.
        

=item boolean B (array)

   Adds items to the end of the queue.
    
       Argument: An array of (base64-encoded) strings, representing the items to be
           added.
         * When adding local filenames to the queue, only absolute pathnames should
           be used.  Using relative pathnames would be foolish because the server
           has no idea what the client's current working directory is.
       Return value: Nothing meaningful.
        

=item boolean B ()

   Removes all items from the queue.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item boolean B (array)

   Remove all queued items that do not fall within the given range.
    
       Arguments: An array of integers that represents a range.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
        

=item boolean B (array)

   Removes all items except for those referenced by a list of positions.
       
       Arguments: An array of integers that represents a list of the positions of
           the items to be kept. 
       Return value: Nothing meaningful.
        

=item base64 B ()

   Returns the name of the currently playing song.
    
       Arguments: None.
       Return value: The name of the currently playing song.
        

=item double B ()

   Returns the amount of time that the current song has been playing.
    
       Arguments: None.
       Return value: The number of seconds that the current song has been playing.
        

=item boolean B (array)

   Remove all queued items that fall within the given range.
    
       Arguments: An array of integers that represents a range.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
        

=item boolean B (array)

   Removes the items referenced by a list of positions within the queue.
       
       Arguments: An array of integers that represents a list of the positions of
           the items to be removed. 
       Return value: Nothing meaningful.
        

=item boolean B ()

   Tells the server to terminate itself.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item boolean B (base64)

=item boolean B (base64, array)

   Removes all items that don't match the given regular expression.
    
       Arguments: A regular expression that specifies which items to keep.
         * Optionally, an array of integers may be given as a second argument.
           This argument represents a range to which the filtering will be
           limited.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
        

=item int B ()

   Gets the limit on the size of the history list stored in memory.
    
       Arguments: None.
       Return value: The maximum number of history entries that the server will
           remember.
        

=item array B ()

   Returns a list of the server's filetype-player associations.
       
       Arguments: None.
       Return value: An array of pairs. The first element of each pair is a
           (base64-encoded) string that represents a regular expression pattern,
           and the second element is a (base64-encoded) string that represents the
           system command that should be used to handle songs that match the
           corresponding pattern.
        

=item boolean B ()

   Stops any new songs from being played. Use run_queue() to reverse this
       state.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item boolean B ()

   Stops any new songs from being played. Use run_queue() to reverse this
       state.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item array B ()

=item array B (int)

   Returns a list of the items that were recently played.
    
       Arguments: If a positive integer argument is given, then no more than that
           number of entries will be returned.  If a number is not specified, or if
           zero is given, then the entire history is returned.  The result is
           undefined if a negative integer argument is given (but does not raise an
           exception).
       Return value: An array of triples, each representing a song that was played
           along with the times that it started and finished playing.
         * The first member of the pair is a (base64-encoded) string which
           represents the song that was previously played.
         * The second member of the pair is a floating point number which
           represents the time that the song started playing in seconds since the
           epoch.
         * The third member of the pair is a floating point number which
           represents the time that the song finished playing in seconds since the
           epoch.
        

=item struct B ()

=item struct B (array)

   Lists the song queue's contents. If a range is specified, only the
       items that fall within that range are listed.
    
       This differs from list() only in its return value, and is useful when you
       want to know the starting position of your selected range within the song
       queue (which can be different than the starting index of the specified range
       if, for example, the starting index is a negative integer).
    
       Arguments: Either none, or an array of integers that represents a range.
         * If no range is given, the whole list is returned.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: A struct with two elements. This first is "list", an array of
           (base64-encoded) strings, representing the selected range from the song
           queue's contents. The second is "start", an integer index value that
           represents the position of the first item of the returned list in the
           song queue.
        

=item boolean B (array, int)

   Inserts items at a given position in the queue.
    
       Arguments: The first argument is an array of (base64-encoded) strings,
           representing the items to be added.
         * The second argument specifies the position in the queue where the items
           will be inserted.
         * When adding local filenames to the queue, only absolute pathnames should
           be used.  Using relative pathnames would be foolish because the server
           has no idea what the client's current working directory is.
       Return value: Nothing meaningful.
        

=item boolean B ()

   Tells you whether loop mode is on or not.
    
       If loop mode is on, songs are returned to the end of the song queue after
       they finish playing.  If loop mode is off, songs that have finished playing
       are not returned to the queue.
    
       Arguments: None.
       Return value: True if loop mode is set, False if it is not.
        

=item boolean B ()

   Tells you whether the current song is paused or not.
    
       Arguments: None.
       Return value: True if the current song is paused, otherwise False.
        

=item boolean B ()

   Tells you whether the queue consumption (advancement) is activated.
    
       Arguments: None.
       Return value: True if new songs are going to be played from the queue after
           the current song is finished, otherwise False.
        

=item double B ()

   Returns the time at which the song queue was last modified.
    
       This method is intended for use by GUI clients that don't want to waste time
       downloading the entire contents of the song queue if it hasn't changed.
       
       Arguments: None.
       Return value: A floating-point number that represents time as the number of
           seconds since the epoch.
        

=item int B ()

   Returns the number of items in the song queue.
    
       Arguments: None.
       Return value: The number of items in the song queue.
        

=item array B ()

=item array B (array)

   Lists the song queue's contents. If a range is specified, only the
       items that fall within that range are listed.
    
       Arguments: Either none, or an array of integers that represents a range.
         * If no range is given, the whole list is returned.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: An array of (base64-encoded) strings, representing the
           selected range from the song queue's contents.
        

=item boolean B (array, int)

   Moves a range of items to a new position within the queue.
    
       Arguments: The first argument is an array of integers that represents a
           range of items to be moved. 
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
         * The second argument, "destination", specifies the position in the queue
           where the items will be moved.
       Return value: Nothing meaningful.
        

=item boolean B (array, int)

   Moves the items referenced by a list of positions to a new position.
       
       Arguments: The first argument is an array of integers that represents a
           list of the positions of the items to be moved. 
         * The second argument, "destination", specifies the position in the queue
           where the items will be moved.
       Return value: Nothing meaningful.
        

=item boolean B ()

=item boolean B (int)

   Stops the current song (if any), and jumps ahead to a song that is
       currently in the queue. The skipped songs are recorded in the history as if
       they had been played. When called without arguments, this behaves very
       much like the skip() method, except that it will have an effect even if
       nothing is currently playing.
    
       Arguments: A single integer that tells how far forward into the song queue
           to advance. A value of 1 will cause the first song in the queue to play,
           2 will cause the second song in the queue to play, and so on. If no
           argument is given, a value of 1 is assumed.
       Return value: Nothing meaningful.
        

=item boolean B ()

   Does nothing, successfully.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item boolean B ()

   Pauses the currently playing song.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item boolean B (array)

   Adds items to the beginning of the queue.
    
       Argument: An array of (base64-encoded) strings, representing the items to be
           added.
         * When adding local filenames to the queue, only absolute pathnames should
           be used.  Using relative pathnames would be foolish because the server
           has no idea what the client's current working directory is.
       Return value: Nothing meaningful.
        

=item boolean B ()

=item boolean B (int)

   Stops the current song (if any), removes the most recently played song
       from the history, and puts these songs at the head of the queue. When loop
       mode is on, the songs at the tail of the song queue are used instead of the
       most recently played songs in the history.
    
       Arguments: A single integer that tells how far back in the history list to
           retreat. A value of 1 will cause the most recent song to play, 2 will
           cause the second most recent song to play, and so on. If no argument is
           given, a value of 1 is assumed.
       Return value: Nothing meaningful.
        

=item boolean B ()

   Places the currently playing song at the beginning of the queue.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item int B ()

   Returns the number of items in the song queue.
    
       Arguments: None.
       Return value: The number of items in the song queue.
        

=item boolean B ()

   Tells the server to reread its player configuration file.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item boolean B (base64)

=item boolean B (base64, array)

   Removes all items that match the given regular expression.
    
       Arguments: A regular expression that specifies which items to remove.
         * Optionally, an array of integers may be given as a second argument.
           This argument represents a range to which the removal will be limited.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
        

=item boolean B (array)

   Replaces the contents of the queue with the given items.
    
       This is equivalent to calling clear() and prepend() in succession, except that this
       operation is atomic.
    
       Argument: An array of (base64-encoded) strings, representing the items to be
           added.
         * When adding local filenames to the queue, only absolute pathnames
           should be used.  Using relative pathnames would be foolish because
           the server isn't aware of the client's current working directory.
       Return value: Nothing meaningful.
        

=item boolean B ()

=item boolean B (array)

   Reverses the order of the items in the queue.
    
       Arguments: Either none, or an array of integers that represents a range.
         * If no range is given, the whole list is affected.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
        

=item boolean B ()

   Allows new songs to be played again after halt_queue() has been called.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item boolean B ()

   Allows new songs to be played again after halt_queue() has been called.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item boolean B (int)

   Sets the limit on the size of the history list stored in memory.
    
       This will irrevocably discard history entries if the new limit is lower than
       the current size of the history list.
    
       Arguments: The new maximum number of history entries. If this value is
           negative, the history limit will be set to zero.
       Return value: Nothing meaningful.
        

=item boolean B (boolean)

   Turns loop mode on or off.
    
       If loop mode is on, songs are returned to the end of the song queue after
       they finish playing.  If loop mode is off, songs that have finished playing
       are not returned to the queue.
    
       Arguments: True if you want to turn loop mode on, False if you want to turn
           it off.
       Return value: Nothing meaningful.
        

=item base64 B ()

   Returns a textual description of the server's player configuration.
    
       Arguments: None.
       Return value: A (base64-encoded) string that shows which programs will be
           used to play the various file-types recognized by the Moosic server.
        

=item boolean B ()

=item boolean B (array)

   Rearrange the contents of the queue into a random order.
    
       Arguments: Either none, or an array of integers that represents a range.
         * If no range is given, the whole list is affected.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
        

=item boolean B ()

   Skips the rest of the current song to play the next song in the queue.
       This only has an effect if there actually is a current song.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item boolean B ()

=item boolean B (array)

   Arranges the contents of the queue into sorted order.
    
       Arguments: Either none, or an array of integers that represents a range.
         * If no range is given, the whole list is affected.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
        

=item boolean B ()

   Stops playing the current song and stops new songs from playing. The
       current song is returned to the head of the song queue and is not recorded
       in the history list. If loop mode is on, the current song won't be placed at
       the end of the song queue when it is stopped.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item boolean B (base64, base64)

=item boolean B (base64, base64, array)

   Performs a regular expression substitution on the items in the queue.
       
       Arguments: The first is a (base64-encoded) regular expression that specifies
           the text to be replaced.
         * The second argument is the (base64-encoded) string that will be used to
           replace the first occurrence of the regular expression within each queue
           item. Any backslash escapes in this string will be processed, including
           special character translation (e.g. "\n" to newline) and backreferences
           to groups within the match.
         * Optionally, an array of integers may be given as a third argument.
           This argument represents a range to which the substitution will be
           limited. This range is interpreted in the same way as the range argument
           in other Moosic methods.
         * If performing a replacement changes an item in the queue into the empty
           string, then it is removed from the queue.
       Return value: Nothing meaningful.
        

=item boolean B (base64, base64)

=item boolean B (base64, base64, array)

   Performs a global regular expression substitution on the items in the queue.
       
       Arguments: The first is a (base64-encoded) regular expression that specifies
           the text to be replaced.
         * The second argument is the (base64-encoded) string that will be used to
           replace all occurrences of the regular expression within each queue
           item. Any backslash escapes in this string will be processed, including
           special character translation (e.g. "\n" to newline) and backreferences
           to the substrings matched by individual groups in the pattern.
         * Optionally, an array of integers may be given as a third argument.
           This argument represents a range to which the substitution will be
           limited. This range is interpreted in the same way as the range argument
           in other Moosic methods.
         * If performing a replacement changes an item in the queue into the empty
           string, then it is removed from the queue.
       Return value: Nothing meaningful.
        

=item boolean B (array, array)

   Swaps the items contained in one range with the items contained in the
       other range.
       
       Return value: Nothing meaningful.
        

=item array B ()

   Return an array of all available XML-RPC methods on this server.
            

=item string B (string)

   Given the name of a method, return a help string.
            

=item array B (string)

   Given the name of a method, return an array of legal signatures. Each
           signature is an array of strings. The first item of each signature is
           the return type, and any others items are parameter types.
            

=item array B (array)

   Process an array of calls, and return an array of results. Calls
           should be structs of the form {'methodName': string, 'params': array}.
           Each result will either be a single-item array containg the result
           value, or a struct of the form {'faultCode': int, 'faultString':
           string}. This is useful when you need to make lots of small calls
           without lots of round trips.
            

=item boolean B ()

   Turns loop mode on if it is off, and turns it off if it is on.
    
       If loop mode is on, songs are returned to the end of the song queue after
       they finish playing.  If loop mode is off, songs that have finished playing
       are not returned to the queue.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item boolean B ()

   Pauses the current song if it is playing, and unpauses if it is paused.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item boolean B ()

   Unpauses the current song.
    
       Arguments: None.
       Return value: Nothing meaningful.
        

=item string B ()

   Returns the Moosic server's version string.
    
       Arguments: None.
       Return value: The version string for the Moosic server.
        


=back

moosic-1.5.6/doc/Moosic_API.html0000644000175000017500000014353310300321617017105 0ustar  danieldaniel00000000000000


Moosic_API - How to write your own Moosic client.






NAME

Moosic_API - How to write your own Moosic client.


Introduction

``moosicd'' is a program that implements the server portion of the Moosic jukebox system. This server provides services for manipulating a queue of songs to be played, as well as for controlling the playing of these songs. A Moosic client sends requests to the server, and receives data in response to these requests. The ``moosic'' command line utility is the canonical Moosic client, and provides a way to control a Moosic server from an interactive command shell or from a shell script. However, you might not be satisfied by the interface that is provided by the ``moosic'' command, so I have written this document to describe how to communicate with a Moosic server in your own programs.

[This document was written by Daniel Pearson <daniel@nanoo.org>, and has been placed into the public domain.]


Section 0: Instructions for Impatient Developers

  1. Plan to write your program with Python and xmlrpclib. xmlrpclib is included with Python 2.2 or later, but if you need to use an earlier version of Python, xmlrpclib can be downloaded from http://www.pythonware.com/products/xmlrpc/.

  2. If you can't or don't want to write your program with Python and xmlrpclib, you won't benefit from this section and will have to read section 2 of this document.

  3. Create a proxy which communicates with moosicd:
       >>> import moosic.client.factory
       >>> proxy = moosic.client.factory.LocalMoosicProxy()

  4. Read section 3 for the documentation of all the methods supported by the proxy object.

  5. Use the proxy to get information from the server and to send commands to it. For example:
       >>> proxy.list()
       []
       >>> proxy.is_queue_running()
       True
       >>> proxy.haltqueue()
       True
       >>> proxy.is_queue_running()
       False
       >>> proxy.append([xmlrpc.Binary(i) for i in 
       ...       ['/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3',
       ...        '/home/daniel/music/Fiona_Apple/When_The_Pawn/04-Love_Ridden.ogg',
       ...        "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"]])
       True
       >>> proxy.list()
       [<xmlrpclib.Binary instance at 0x843cf3c>,
        <xmlrpclib.Binary instance at 0x8440e94>,
        <xmlrpclib.Binary instance at 0x8440ebc>]
       >>> [i.data for i in proxy.list()]
       ['/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3',
        '/home/daniel/music/Fiona_Apple/When_The_Pawn/04-Love_Ridden.ogg',
        "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"]
       >>> proxy.sort()
       True
       >>> [i.data for i in proxy.list()]
       ['/home/daniel/music/Fiona_Apple/When_The_Pawn/04-Love_Ridden.ogg',
        '/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3',
        "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"]

  6. If you wish to communicate with a moosicd that is listening for requests on an IP socket instead of a Unix domain socket, you should use InetMoosicProxy instead of LocalMoosicProxy. Here's an example:
       >>> import moosic.client.factory
       >>> proxy = moosic.client.factory.InetMoosicProxy('example.com', 8080)


Section 1: The Moosic Server's Data Model

This section describes, in precise terms, the data objects that can be accessed and manipulated by sending requests to the Moosic server.

song queue
This is the foremost data object maintained by the Moosic server. It is an ordered sequence of strings that represents the queue of songs that are waiting to be played. Each item in this list identifies a song that will be played by the Moosic server. Usually, these items are the names of files on disk that contain each song, but this does not have to be the case. For instance, an HTTP URL might be used to name a song if a program that can play songs from the Web is appropriately registered with the Moosic server (see ``player configuration'' later in this section).

current song
This is a string that identifies the song that is currently being played by the Moosic server. If nothing is currently playing, then this will be the empty string.

queue running flag
This is a boolean value that indicates whether the Moosic server will start playing a new song as soon as the current song has finished and the song queue is not empty.

pause flag
This is a boolean value that indicates whether the current song is paused or not.

loop mode flag
This is a boolean value that sets ``loop mode''. When loop mode is on, songs are returned to the end of the song queue when they finish playing instead of being discarded.

history
This is a list of songs that the Moosic server has finished playing. Note that songs named in this list may have finished playing early at the request of a user (i.e. through use of the ``next'' command). Each entry in this list is actually a 3-tuple of (song, start time, finish time).

maximum history size
This is the maximum number of songs that will be stored in the history list. Old entries are removed from the history to make room for newer entries when this limit is reached.

player configuration
This is an ordered mapping that associates regular expressions (text patterns) to programs. For each regular expression, the associated program is expected to be able to play any queue items that match that regular expression. Each program is a list in which the name of the executable file that contains the program is the first element and the program's arguments are the rest of the elements.

last queue update
This is the time at which the song queue was last modified. It a floating-point number that represents time as the number of seconds since the epoch.

server version
This is a string that describes the version of the program that implements the Moosic server. It has no specific, well-defined semantics.

API version
This is a pair of integers, one representing the ``major'' version, and the other representing the ``minor'' version. These numbers are meant to provide some useful compatibility information to Moosic clients. As this API changes, these numbers will change in the following ways. If the API has been changed in a backward-compatible way (e.g. a new method was added or an existing method was overloaded), then the minor version will increase and the major version will remain unchanged. However, if the API has been changed in such a way that existing code that uses the API might break (e.g. a method was removed or its return value or parameter types were changed), then the major version will increase and the minor version may be reset to any value (although it will usually be reset zero).


Section 2: The Low-Level Details of Client-Server Communication

The information in this section is generally only necessary to people who wish to write a Moosic client in a programming language other than Python. If you are using Python to write a Moosic client, then you can use the classes LocalMoosicProxy and InetMoosicProxy from the moosic_factory.py module, and blissfully ignore most of these gory details. However, Python programmers can also benefit from reading this section, as it will deepen their understanding of Moosic's inter-process communication model.

The first thing to know about writing your own Moosic client is that communication between the client and server is done through a BSD-style socket. Read the ``socket'' manual page (and related manual pages) on a Unix system if you are unfamiliar with BSD sockets. The socket used by Moosic belongs to the Unix-domain protocol family (PF_UNIX or PF_LOCAL) and has a type of SOCK_STREAM. This means that a Moosic client can only communicate with a Moosic server that is running on the same computer as the client. This limitation is a very purposeful part of Moosic's design. It has the advantage of vastly reducing the consequences of any security flaws that Moosic might have.

If you really, really think that you need the client and the server to run on separate hosts, then you can run moosicd with the -t option, which tells it to listen on a TCP/IP socket instead of a Unix domain socket. I recommend firewalling such a port very carefully.

Regardless of which kind of socket is used by the server, XML-RPC is used as the data protocol for requests and responses. For an introduction to XML-RPC, see the XML-RPC homepage http://www.xmlrpc.com/ and the XML-RPC HOWTO http://xmlrpc-c.sourceforge.net/xmlrpc-howto/xmlrpc-howto.html. Python users should note that if the XML-RPC HOWTO tells you that you need to install a third-party library to use XML-RPC, it is assuming that you are using a Python version earlier than 2.2. Since version 2.2, Python has included the xmlrpclib module in its standard library.

In summary, all you need to do to talk to a Moosic server in your own program is to send XML-RPC requests to the appropriate address. By default, the appropriate address for contacting moosicd is the file named ``socket'' in a directory named ``.moosic'' in the home directory of the user that started moosicd (i.e. ``~/.moosic/socket''). If moosicd is started with the -c option, then the directory that contains ``socket'' will be the argument provided to the -c option instead of ~/.moosic. If moosicd is started with the -t option, then clients will have to address it by using a (host, port) pair instead of a filename.


Section 3: Valid Moosic Server Methods

moosicd's XML-RPC server implements the introspection API mentioned on http://xmlrpc-c.sourceforge.net/xmlrpc-howto/xmlrpc-howto-api-introspection.html, so the API presented by moosicd is essentially self-documenting. Thus, the information in this section has been automatically generated by querying a running Moosic server.

The Moosic API contains the following methods:

array api_version ()
   Returns the version number for the API that the server implements.
    
       Arguments: None.
       Return value: The version number, which is a 2-element array of
           integers.  The first element is the major version, and the second
           element is the minor version.
boolean append (array)
   Adds items to the end of the queue.
    
       Argument: An array of (base64-encoded) strings, representing the items to be
           added.
         * When adding local filenames to the queue, only absolute pathnames should
           be used.  Using relative pathnames would be foolish because the server
           has no idea what the client's current working directory is.
       Return value: Nothing meaningful.
boolean clear ()
   Removes all items from the queue.
    
       Arguments: None.
       Return value: Nothing meaningful.
boolean crop (array)
   Remove all queued items that do not fall within the given range.
    
       Arguments: An array of integers that represents a range.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
boolean crop_list (array)
   Removes all items except for those referenced by a list of positions.
       
       Arguments: An array of integers that represents a list of the positions of
           the items to be kept. 
       Return value: Nothing meaningful.
base64 current ()
   Returns the name of the currently playing song.
    
       Arguments: None.
       Return value: The name of the currently playing song.
double current_time ()
   Returns the amount of time that the current song has been playing.
    
       Arguments: None.
       Return value: The number of seconds that the current song has been playing.
boolean cut (array)
   Remove all queued items that fall within the given range.
    
       Arguments: An array of integers that represents a range.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
boolean cut_list (array)
   Removes the items referenced by a list of positions within the queue.
       
       Arguments: An array of integers that represents a list of the positions of
           the items to be removed. 
       Return value: Nothing meaningful.
boolean die ()
   Tells the server to terminate itself.
    
       Arguments: None.
       Return value: Nothing meaningful.
boolean filter (base64)
boolean filter (base64, array)
   Removes all items that don't match the given regular expression.
    
       Arguments: A regular expression that specifies which items to keep.
         * Optionally, an array of integers may be given as a second argument.
           This argument represents a range to which the filtering will be
           limited.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
int get_history_limit ()
   Gets the limit on the size of the history list stored in memory.
    
       Arguments: None.
       Return value: The maximum number of history entries that the server will
           remember.
array getconfig ()
   Returns a list of the server's filetype-player associations.
       
       Arguments: None.
       Return value: An array of pairs. The first element of each pair is a
           (base64-encoded) string that represents a regular expression pattern,
           and the second element is a (base64-encoded) string that represents the
           system command that should be used to handle songs that match the
           corresponding pattern.
boolean halt_queue ()
   Stops any new songs from being played. Use run_queue() to reverse this
       state.
    
       Arguments: None.
       Return value: Nothing meaningful.
boolean haltqueue ()
   Stops any new songs from being played. Use run_queue() to reverse this
       state.
    
       Arguments: None.
       Return value: Nothing meaningful.
array history ()
array history (int)
   Returns a list of the items that were recently played.
    
       Arguments: If a positive integer argument is given, then no more than that
           number of entries will be returned.  If a number is not specified, or if
           zero is given, then the entire history is returned.  The result is
           undefined if a negative integer argument is given (but does not raise an
           exception).
       Return value: An array of triples, each representing a song that was played
           along with the times that it started and finished playing.
         * The first member of the pair is a (base64-encoded) string which
           represents the song that was previously played.
         * The second member of the pair is a floating point number which
           represents the time that the song started playing in seconds since the
           epoch.
         * The third member of the pair is a floating point number which
           represents the time that the song finished playing in seconds since the
           epoch.
struct indexed_list ()
struct indexed_list (array)
   Lists the song queue's contents. If a range is specified, only the
       items that fall within that range are listed.
    
       This differs from list() only in its return value, and is useful when you
       want to know the starting position of your selected range within the song
       queue (which can be different than the starting index of the specified range
       if, for example, the starting index is a negative integer).
    
       Arguments: Either none, or an array of integers that represents a range.
         * If no range is given, the whole list is returned.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: A struct with two elements. This first is "list", an array of
           (base64-encoded) strings, representing the selected range from the song
           queue's contents. The second is "start", an integer index value that
           represents the position of the first item of the returned list in the
           song queue.
boolean insert (array, int)
   Inserts items at a given position in the queue.
    
       Arguments: The first argument is an array of (base64-encoded) strings,
           representing the items to be added.
         * The second argument specifies the position in the queue where the items
           will be inserted.
         * When adding local filenames to the queue, only absolute pathnames should
           be used.  Using relative pathnames would be foolish because the server
           has no idea what the client's current working directory is.
       Return value: Nothing meaningful.
boolean is_looping ()
   Tells you whether loop mode is on or not.
    
       If loop mode is on, songs are returned to the end of the song queue after
       they finish playing.  If loop mode is off, songs that have finished playing
       are not returned to the queue.
    
       Arguments: None.
       Return value: True if loop mode is set, False if it is not.
boolean is_paused ()
   Tells you whether the current song is paused or not.
    
       Arguments: None.
       Return value: True if the current song is paused, otherwise False.
boolean is_queue_running ()
   Tells you whether the queue consumption (advancement) is activated.
    
       Arguments: None.
       Return value: True if new songs are going to be played from the queue after
           the current song is finished, otherwise False.
double last_queue_update ()
   Returns the time at which the song queue was last modified.
    
       This method is intended for use by GUI clients that don't want to waste time
       downloading the entire contents of the song queue if it hasn't changed.
       
       Arguments: None.
       Return value: A floating-point number that represents time as the number of
           seconds since the epoch.
int length ()
   Returns the number of items in the song queue.
    
       Arguments: None.
       Return value: The number of items in the song queue.
array list ()
array list (array)
   Lists the song queue's contents. If a range is specified, only the
       items that fall within that range are listed.
    
       Arguments: Either none, or an array of integers that represents a range.
         * If no range is given, the whole list is returned.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: An array of (base64-encoded) strings, representing the
           selected range from the song queue's contents.
boolean move (array, int)
   Moves a range of items to a new position within the queue.
    
       Arguments: The first argument is an array of integers that represents a
           range of items to be moved. 
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
         * The second argument, "destination", specifies the position in the queue
           where the items will be moved.
       Return value: Nothing meaningful.
boolean move_list (array, int)
   Moves the items referenced by a list of positions to a new position.
       
       Arguments: The first argument is an array of integers that represents a
           list of the positions of the items to be moved. 
         * The second argument, "destination", specifies the position in the queue
           where the items will be moved.
       Return value: Nothing meaningful.
boolean next ()
boolean next (int)
   Stops the current song (if any), and jumps ahead to a song that is
       currently in the queue. The skipped songs are recorded in the history as if
       they had been played. When called without arguments, this behaves very
       much like the skip() method, except that it will have an effect even if
       nothing is currently playing.
    
       Arguments: A single integer that tells how far forward into the song queue
           to advance. A value of 1 will cause the first song in the queue to play,
           2 will cause the second song in the queue to play, and so on. If no
           argument is given, a value of 1 is assumed.
       Return value: Nothing meaningful.
boolean no_op ()
   Does nothing, successfully.
    
       Arguments: None.
       Return value: Nothing meaningful.
boolean pause ()
   Pauses the currently playing song.
    
       Arguments: None.
       Return value: Nothing meaningful.
boolean prepend (array)
   Adds items to the beginning of the queue.
    
       Argument: An array of (base64-encoded) strings, representing the items to be
           added.
         * When adding local filenames to the queue, only absolute pathnames should
           be used.  Using relative pathnames would be foolish because the server
           has no idea what the client's current working directory is.
       Return value: Nothing meaningful.
boolean previous ()
boolean previous (int)
   Stops the current song (if any), removes the most recently played song
       from the history, and puts these songs at the head of the queue. When loop
       mode is on, the songs at the tail of the song queue are used instead of the
       most recently played songs in the history.
    
       Arguments: A single integer that tells how far back in the history list to
           retreat. A value of 1 will cause the most recent song to play, 2 will
           cause the second most recent song to play, and so on. If no argument is
           given, a value of 1 is assumed.
       Return value: Nothing meaningful.
boolean putback ()
   Places the currently playing song at the beginning of the queue.
    
       Arguments: None.
       Return value: Nothing meaningful.
int queue_length ()
   Returns the number of items in the song queue.
    
       Arguments: None.
       Return value: The number of items in the song queue.
boolean reconfigure ()
   Tells the server to reread its player configuration file.
    
       Arguments: None.
       Return value: Nothing meaningful.
boolean remove (base64)
boolean remove (base64, array)
   Removes all items that match the given regular expression.
    
       Arguments: A regular expression that specifies which items to remove.
         * Optionally, an array of integers may be given as a second argument.
           This argument represents a range to which the removal will be limited.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
boolean replace (array)
   Replaces the contents of the queue with the given items.
    
       This is equivalent to calling clear() and prepend() in succession, except that this
       operation is atomic.
    
       Argument: An array of (base64-encoded) strings, representing the items to be
           added.
         * When adding local filenames to the queue, only absolute pathnames
           should be used.  Using relative pathnames would be foolish because
           the server isn't aware of the client's current working directory.
       Return value: Nothing meaningful.
boolean reverse ()
boolean reverse (array)
   Reverses the order of the items in the queue.
    
       Arguments: Either none, or an array of integers that represents a range.
         * If no range is given, the whole list is affected.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
boolean run_queue ()
   Allows new songs to be played again after halt_queue() has been called.
    
       Arguments: None.
       Return value: Nothing meaningful.
boolean runqueue ()
   Allows new songs to be played again after halt_queue() has been called.
    
       Arguments: None.
       Return value: Nothing meaningful.
boolean set_history_limit (int)
   Sets the limit on the size of the history list stored in memory.
    
       This will irrevocably discard history entries if the new limit is lower than
       the current size of the history list.
    
       Arguments: The new maximum number of history entries. If this value is
           negative, the history limit will be set to zero.
       Return value: Nothing meaningful.
boolean set_loop_mode (boolean)
   Turns loop mode on or off.
    
       If loop mode is on, songs are returned to the end of the song queue after
       they finish playing.  If loop mode is off, songs that have finished playing
       are not returned to the queue.
    
       Arguments: True if you want to turn loop mode on, False if you want to turn
           it off.
       Return value: Nothing meaningful.
base64 showconfig ()
   Returns a textual description of the server's player configuration.
    
       Arguments: None.
       Return value: A (base64-encoded) string that shows which programs will be
           used to play the various file-types recognized by the Moosic server.
boolean shuffle ()
boolean shuffle (array)
   Rearrange the contents of the queue into a random order.
    
       Arguments: Either none, or an array of integers that represents a range.
         * If no range is given, the whole list is affected.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
boolean skip ()
   Skips the rest of the current song to play the next song in the queue.
       This only has an effect if there actually is a current song.
    
       Arguments: None.
       Return value: Nothing meaningful.
boolean sort ()
boolean sort (array)
   Arranges the contents of the queue into sorted order.
    
       Arguments: Either none, or an array of integers that represents a range.
         * If no range is given, the whole list is affected.
         * If the range contains a single integer, it will represent all members
           of the queue whose index is greater than or equal to the value of the
           integer.
         * If the range contains two integers, it will represent all members of
           the queue whose index is greater than or equal to the value of the
           first integer and less than the value of the second integer.
         * If the range contains more than two integers, an error will occur.
       Return value: Nothing meaningful.
boolean stop ()
   Stops playing the current song and stops new songs from playing. The
       current song is returned to the head of the song queue and is not recorded
       in the history list. If loop mode is on, the current song won't be placed at
       the end of the song queue when it is stopped.
    
       Arguments: None.
       Return value: Nothing meaningful.
boolean sub (base64, base64)
boolean sub (base64, base64, array)
   Performs a regular expression substitution on the items in the queue.
       
       Arguments: The first is a (base64-encoded) regular expression that specifies
           the text to be replaced.
         * The second argument is the (base64-encoded) string that will be used to
           replace the first occurrence of the regular expression within each queue
           item. Any backslash escapes in this string will be processed, including
           special character translation (e.g. "\n" to newline) and backreferences
           to groups within the match.
         * Optionally, an array of integers may be given as a third argument.
           This argument represents a range to which the substitution will be
           limited. This range is interpreted in the same way as the range argument
           in other Moosic methods.
         * If performing a replacement changes an item in the queue into the empty
           string, then it is removed from the queue.
       Return value: Nothing meaningful.
boolean sub_all (base64, base64)
boolean sub_all (base64, base64, array)
   Performs a global regular expression substitution on the items in the queue.
       
       Arguments: The first is a (base64-encoded) regular expression that specifies
           the text to be replaced.
         * The second argument is the (base64-encoded) string that will be used to
           replace all occurrences of the regular expression within each queue
           item. Any backslash escapes in this string will be processed, including
           special character translation (e.g. "\n" to newline) and backreferences
           to the substrings matched by individual groups in the pattern.
         * Optionally, an array of integers may be given as a third argument.
           This argument represents a range to which the substitution will be
           limited. This range is interpreted in the same way as the range argument
           in other Moosic methods.
         * If performing a replacement changes an item in the queue into the empty
           string, then it is removed from the queue.
       Return value: Nothing meaningful.
boolean swap (array, array)
   Swaps the items contained in one range with the items contained in the
       other range.
       
       Return value: Nothing meaningful.
array system.listMethods ()
   Return an array of all available XML-RPC methods on this server.
string system.methodHelp (string)
   Given the name of a method, return a help string.
array system.methodSignature (string)
   Given the name of a method, return an array of legal signatures. Each
           signature is an array of strings. The first item of each signature is
           the return type, and any others items are parameter types.
array system.multicall (array)
   Process an array of calls, and return an array of results. Calls
           should be structs of the form {'methodName': string, 'params': array}.
           Each result will either be a single-item array containg the result
           value, or a struct of the form {'faultCode': int, 'faultString':
           string}. This is useful when you need to make lots of small calls
           without lots of round trips.
boolean toggle_loop_mode ()
   Turns loop mode on if it is off, and turns it off if it is on.
    
       If loop mode is on, songs are returned to the end of the song queue after
       they finish playing.  If loop mode is off, songs that have finished playing
       are not returned to the queue.
    
       Arguments: None.
       Return value: Nothing meaningful.
boolean toggle_pause ()
   Pauses the current song if it is playing, and unpauses if it is paused.
    
       Arguments: None.
       Return value: Nothing meaningful.
boolean unpause ()
   Unpauses the current song.
    
       Arguments: None.
       Return value: Nothing meaningful.
string version ()
   Returns the Moosic server's version string.
    
       Arguments: None.
       Return value: The version string for the Moosic server.


Section 4: Writing a Moosic Client in Python

As demonstrated in section 0, communicating with a Moosic server is very easy to do with Python. In this section, I'll merely elaborate on the details that were omitted from section 0 for the sake of brevity.

First of all, you should know that LocalMoosicProxy() can be called with a filename argument to specify the location of the Moosic server's socket file. This is useful if moosicd was started with the ``-c'' option. Refer to the moosic_factory.py module's documentation (moosic_factory.html).

Next, note that many of the Moosic server's methods accept or return special types of objects from the xmlrpclib module, namely Boolean and Binary. These object types serve the purpose of bridging the small mismatch between the data-types supported by XML-RPC and Python's intrinsic data-types. Boolean objects present no unusual problem, since they evaluate to a correct truth value without any extra effort. However, you must take care when using the Moosic methods that accept or return Binary objects. Read the documentation for the xmlrpclib module for details on how to work with these objects. The basic technique boils down to wrapping up a string inside a Binary object before sending it to the server, and using the ``data'' attribute to access the string data within the Binary objects returned by the server. Regular strings can't be used because XML-RPC's normal string data-type can't handle multiple 8-bit strings within a single request if the strings use different encodings.


Section 5: Writing a Moosic Client in Another Language

If you are not using Python to write your Moosic client, the first issue to deal with is deciding upon an XML-RPC implementation. For most popular programming languages, there are multiple XML-RPC implementations available. Most of the possibilities are listed at http://www.xmlrpc.com/directory/1568/implementations. Since XML-RPC is an open specification, you can create your own implementation if you don't like any of the ones that already exist.

Once you've got an XML-RPC library that you like, the big hurdle to overcome is to make that library send its RPC calls over a Unix socket instead of an IP socket. I was able to do this pretty easily with Python's xmlrpclib since it is designed to allow pluggable transport methods: all I had to do was subclass my own Transport type and plug it back into the original library's classes. (If your language and/or library of choice makes this task difficult, then you may begin to understand why some Python programmers are so smug.)

After you are capable of sending XML-RPC requests through a Unix socket, you can go ahead and start sending requests to a Moosic server. Refer to the end of section 2 for information on how to address a Moosic server. Refer to section 3 for a list of valid server requests.

If you can't be bothered to find or hack together an XML-RPC library that works with Unix sockets, then you can still talk to a Moosic server that is listening on an IP socket, but this is less than ideal since listening on an IP socket is not default behavior for most Moosic servers.


Section 6: API Version History (ChangeLog)

  • 1.8
  • First implemented in moosicd 1.5.1. The following methods were added:
        swap

  • 1.7
  • First implemented by moosicd 1.5.0. The following methods were added:
        skip, current_time

    The following methods had their behavior significantly changed:

        previous, next

    Specifically, the previous() method no longer activates queue advancement if it had been disabled before. This means that calling previous() no longer necessarily causes a song to start playing. The next() method was changed to more closely parallel the behavior of previous(): it takes a single optional integer argument to allow immediate advancement by more than one song at a time, and it has an effect even when no song is currently playing. The new skip() method implements the exact same behavior that was previously exhibited by next().

    Last, and most importantly, the name of the default socket file for communicating with the server via unix sockets was changed from $CONFIG_DIR/socket-$HOSTNAME to $CONFIG_DIR/socket. If you are a Python programmer and you use the updated moosic_factory.py file from Moosic version 1.5.0, then you don't have to make any changes. Otherwise, you must change your client's code to connect to the file named ``socket'' instead of the file named ``socket-something.example.com''. If your client only talks to the Moosic server through TCP/IP, then you don't have to make any changes, of course.

  • 1.6
  • First implemented by moosicd 1.4.10. The following methods were added:
        getconfig

  • 1.5
  • First implemented by moosicd 1.4.6. The following methods were added:
        sub, sub_all, stop

  • 1.4
  • First implemented by moosicd 1.4.5. The following methods were added:
        previous

  • 1.3
  • First implemented by moosicd 1.4.4. The following methods were added:
        replace, replace_range, last_queue_update

  • 1.2
  • First implemented by moosicd 1.4.2. The following methods were added:
        cut_list, crop_list

  • 1.1
  • First implemented by moosicd 1.4.1. The following methods were added:
        is_looping, set_loop_mode, toggle_loop_mode

  • 1.0
  • First implemented by moosicd 1.4.0. The following methods were included:
        api_version, append, clear, crop, current, cut, die, filter,
        get_history_limit, halt_queue, haltqueue, history, indexed_list, insert,
        is_paused, is_queue_running, length, list, move, move_list, next, no_op,
        pause, prepend, putback, queue_length, reconfigure, remove, reverse,
        run_queue, runqueue, set_history_limit, showconfig, shuffle, sort,
        system.listMethods, system.methodHelp, system.methodSignature,
        system.multicall, toggle_pause, unpause, version

moosic-1.5.6/doc/Moosic_API.sect0.pod0000644000175000017500000000427010131341374017736 0ustar danieldaniel00000000000000=head1 Section 0: Instructions for Impatient Developers =over =item 1. Plan to write your program with Python and xmlrpclib. xmlrpclib is included with Python 2.2 or later, but if you need to use an earlier version of Python, xmlrpclib can be downloaded from L. =item 2. If you can't or don't want to write your program with Python and xmlrpclib, you won't benefit from this section and will have to read section 2 of this document. =item 3. Create a proxy which communicates with moosicd: >>> import moosic.client.factory >>> proxy = moosic.client.factory.LocalMoosicProxy() =item 4. Read section 3 for the documentation of all the methods supported by the proxy object. =item 5. Use the proxy to get information from the server and to send commands to it. For example: >>> proxy.list() [] >>> proxy.is_queue_running() True >>> proxy.haltqueue() True >>> proxy.is_queue_running() False >>> proxy.append([xmlrpc.Binary(i) for i in ... ['/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3', ... '/home/daniel/music/Fiona_Apple/When_The_Pawn/04-Love_Ridden.ogg', ... "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"]]) True >>> proxy.list() [, , ] >>> [i.data for i in proxy.list()] ['/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3', '/home/daniel/music/Fiona_Apple/When_The_Pawn/04-Love_Ridden.ogg', "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"] >>> proxy.sort() True >>> [i.data for i in proxy.list()] ['/home/daniel/music/Fiona_Apple/When_The_Pawn/04-Love_Ridden.ogg', '/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3', "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"] =item 6. If you wish to communicate with a moosicd that is listening for requests on an IP socket instead of a Unix domain socket, you should use InetMoosicProxy instead of LocalMoosicProxy. Here's an example: >>> import moosic.client.factory >>> proxy = moosic.client.factory.InetMoosicProxy('example.com', 8080) =back moosic-1.5.6/doc/Moosic_API.pod0000644000175000017500000012116010300321616016712 0ustar danieldaniel00000000000000=head1 NAME Moosic_API - How to write your own Moosic client. =head1 Introduction "moosicd" is a program that implements the server portion of the Moosic jukebox system. This server provides services for manipulating a queue of songs to be played, as well as for controlling the playing of these songs. A Moosic client sends requests to the server, and receives data in response to these requests. The "moosic" command line utility is the canonical Moosic client, and provides a way to control a Moosic server from an interactive command shell or from a shell script. However, you might not be satisfied by the interface that is provided by the "moosic" command, so I have written this document to describe how to communicate with a Moosic server in your own programs. [This document was written by Daniel Pearson , and has been placed into the public domain.] =head1 Section 0: Instructions for Impatient Developers =over =item 1. Plan to write your program with Python and xmlrpclib. xmlrpclib is included with Python 2.2 or later, but if you need to use an earlier version of Python, xmlrpclib can be downloaded from L. =item 2. If you can't or don't want to write your program with Python and xmlrpclib, you won't benefit from this section and will have to read section 2 of this document. =item 3. Create a proxy which communicates with moosicd: >>> import moosic.client.factory >>> proxy = moosic.client.factory.LocalMoosicProxy() =item 4. Read section 3 for the documentation of all the methods supported by the proxy object. =item 5. Use the proxy to get information from the server and to send commands to it. For example: >>> proxy.list() [] >>> proxy.is_queue_running() True >>> proxy.haltqueue() True >>> proxy.is_queue_running() False >>> proxy.append([xmlrpc.Binary(i) for i in ... ['/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3', ... '/home/daniel/music/Fiona_Apple/When_The_Pawn/04-Love_Ridden.ogg', ... "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"]]) True >>> proxy.list() [, , ] >>> [i.data for i in proxy.list()] ['/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3', '/home/daniel/music/Fiona_Apple/When_The_Pawn/04-Love_Ridden.ogg', "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"] >>> proxy.sort() True >>> [i.data for i in proxy.list()] ['/home/daniel/music/Fiona_Apple/When_The_Pawn/04-Love_Ridden.ogg', '/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3', "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"] =item 6. If you wish to communicate with a moosicd that is listening for requests on an IP socket instead of a Unix domain socket, you should use InetMoosicProxy instead of LocalMoosicProxy. Here's an example: >>> import moosic.client.factory >>> proxy = moosic.client.factory.InetMoosicProxy('example.com', 8080) =back =head1 Section 1: The Moosic Server's Data Model This section describes, in precise terms, the data objects that can be accessed and manipulated by sending requests to the Moosic server. =over =item song queue This is the foremost data object maintained by the Moosic server. It is an ordered sequence of strings that represents the queue of songs that are waiting to be played. Each item in this list identifies a song that will be played by the Moosic server. Usually, these items are the names of files on disk that contain each song, but this does not have to be the case. For instance, an HTTP URL might be used to name a song if a program that can play songs from the Web is appropriately registered with the Moosic server (see "player configuration" later in this section). =item current song This is a string that identifies the song that is currently being played by the Moosic server. If nothing is currently playing, then this will be the empty string. =item queue running flag This is a boolean value that indicates whether the Moosic server will start playing a new song as soon as the current song has finished and the song queue is not empty. =item pause flag This is a boolean value that indicates whether the current song is paused or not. =item loop mode flag This is a boolean value that sets "loop mode". When loop mode is on, songs are returned to the end of the song queue when they finish playing instead of being discarded. =item history This is a list of songs that the Moosic server has finished playing. Note that songs named in this list may have finished playing early at the request of a user (i.e. through use of the "next" command). Each entry in this list is actually a 3-tuple of (song, start time, finish time). =item maximum history size This is the maximum number of songs that will be stored in the history list. Old entries are removed from the history to make room for newer entries when this limit is reached. =item player configuration This is an ordered mapping that associates regular expressions (text patterns) to programs. For each regular expression, the associated program is expected to be able to play any queue items that match that regular expression. Each program is a list in which the name of the executable file that contains the program is the first element and the program's arguments are the rest of the elements. =item last queue update This is the time at which the song queue was last modified. It a floating-point number that represents time as the number of seconds since the epoch. =item server version This is a string that describes the version of the program that implements the Moosic server. It has no specific, well-defined semantics. =item API version This is a pair of integers, one representing the "major" version, and the other representing the "minor" version. These numbers are meant to provide some useful compatibility information to Moosic clients. As this API changes, these numbers will change in the following ways. If the API has been changed in a backward-compatible way (e.g. a new method was added or an existing method was overloaded), then the minor version will increase and the major version will remain unchanged. However, if the API has been changed in such a way that existing code that uses the API might break (e.g. a method was removed or its return value or parameter types were changed), then the major version will increase and the minor version may be reset to any value (although it will usually be reset zero). =back =head1 Section 2: The Low-Level Details of Client-Server Communication The information in this section is generally only necessary to people who wish to write a Moosic client in a programming language other than Python. If you are using Python to write a Moosic client, then you can use the classes LocalMoosicProxy and InetMoosicProxy from the moosic_factory.py module, and blissfully ignore most of these gory details. However, Python programmers can also benefit from reading this section, as it will deepen their understanding of Moosic's inter-process communication model. The first thing to know about writing your own Moosic client is that communication between the client and server is done through a BSD-style socket. Read the "socket" manual page (and related manual pages) on a Unix system if you are unfamiliar with BSD sockets. The socket used by Moosic belongs to the Unix-domain protocol family (PF_UNIX or PF_LOCAL) and has a type of SOCK_STREAM. This means that a Moosic client can only communicate with a Moosic server that is running on the same computer as the client. This limitation is a very purposeful part of Moosic's design. It has the advantage of vastly reducing the consequences of any security flaws that Moosic might have. If you really, really think that you need the client and the server to run on separate hosts, then you can run moosicd with the -t option, which tells it to listen on a TCP/IP socket instead of a Unix domain socket. I recommend firewalling such a port very carefully. Regardless of which kind of socket is used by the server, XML-RPC is used as the data protocol for requests and responses. For an introduction to XML-RPC, see the XML-RPC homepage L and the XML-RPC HOWTO L. Python users should note that if the XML-RPC HOWTO tells you that you need to install a third-party library to use XML-RPC, it is assuming that you are using a Python version earlier than 2.2. Since version 2.2, Python has included the xmlrpclib module in its standard library. In summary, all you need to do to talk to a Moosic server in your own program is to send XML-RPC requests to the appropriate address. By default, the appropriate address for contacting moosicd is the file named "socket" in a directory named ".moosic" in the home directory of the user that started moosicd (i.e. "~/.moosic/socket"). If moosicd is started with the -c option, then the directory that contains "socket" will be the argument provided to the -c option instead of ~/.moosic. If moosicd is started with the -t option, then clients will have to address it by using a (host, port) pair instead of a filename. =head1 Section 3: Valid Moosic Server Methods moosicd's XML-RPC server implements the introspection API mentioned on L, so the API presented by moosicd is essentially self-documenting. Thus, the information in this section has been automatically generated by querying a running Moosic server. The Moosic API contains the following methods: =over =item array B () Returns the version number for the API that the server implements. Arguments: None. Return value: The version number, which is a 2-element array of integers. The first element is the major version, and the second element is the minor version. =item boolean B (array) Adds items to the end of the queue. Argument: An array of (base64-encoded) strings, representing the items to be added. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server has no idea what the client's current working directory is. Return value: Nothing meaningful. =item boolean B () Removes all items from the queue. Arguments: None. Return value: Nothing meaningful. =item boolean B (array) Remove all queued items that do not fall within the given range. Arguments: An array of integers that represents a range. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. =item boolean B (array) Removes all items except for those referenced by a list of positions. Arguments: An array of integers that represents a list of the positions of the items to be kept. Return value: Nothing meaningful. =item base64 B () Returns the name of the currently playing song. Arguments: None. Return value: The name of the currently playing song. =item double B () Returns the amount of time that the current song has been playing. Arguments: None. Return value: The number of seconds that the current song has been playing. =item boolean B (array) Remove all queued items that fall within the given range. Arguments: An array of integers that represents a range. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. =item boolean B (array) Removes the items referenced by a list of positions within the queue. Arguments: An array of integers that represents a list of the positions of the items to be removed. Return value: Nothing meaningful. =item boolean B () Tells the server to terminate itself. Arguments: None. Return value: Nothing meaningful. =item boolean B (base64) =item boolean B (base64, array) Removes all items that don't match the given regular expression. Arguments: A regular expression that specifies which items to keep. * Optionally, an array of integers may be given as a second argument. This argument represents a range to which the filtering will be limited. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. =item int B () Gets the limit on the size of the history list stored in memory. Arguments: None. Return value: The maximum number of history entries that the server will remember. =item array B () Returns a list of the server's filetype-player associations. Arguments: None. Return value: An array of pairs. The first element of each pair is a (base64-encoded) string that represents a regular expression pattern, and the second element is a (base64-encoded) string that represents the system command that should be used to handle songs that match the corresponding pattern. =item boolean B () Stops any new songs from being played. Use run_queue() to reverse this state. Arguments: None. Return value: Nothing meaningful. =item boolean B () Stops any new songs from being played. Use run_queue() to reverse this state. Arguments: None. Return value: Nothing meaningful. =item array B () =item array B (int) Returns a list of the items that were recently played. Arguments: If a positive integer argument is given, then no more than that number of entries will be returned. If a number is not specified, or if zero is given, then the entire history is returned. The result is undefined if a negative integer argument is given (but does not raise an exception). Return value: An array of triples, each representing a song that was played along with the times that it started and finished playing. * The first member of the pair is a (base64-encoded) string which represents the song that was previously played. * The second member of the pair is a floating point number which represents the time that the song started playing in seconds since the epoch. * The third member of the pair is a floating point number which represents the time that the song finished playing in seconds since the epoch. =item struct B () =item struct B (array) Lists the song queue's contents. If a range is specified, only the items that fall within that range are listed. This differs from list() only in its return value, and is useful when you want to know the starting position of your selected range within the song queue (which can be different than the starting index of the specified range if, for example, the starting index is a negative integer). Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is returned. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: A struct with two elements. This first is "list", an array of (base64-encoded) strings, representing the selected range from the song queue's contents. The second is "start", an integer index value that represents the position of the first item of the returned list in the song queue. =item boolean B (array, int) Inserts items at a given position in the queue. Arguments: The first argument is an array of (base64-encoded) strings, representing the items to be added. * The second argument specifies the position in the queue where the items will be inserted. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server has no idea what the client's current working directory is. Return value: Nothing meaningful. =item boolean B () Tells you whether loop mode is on or not. If loop mode is on, songs are returned to the end of the song queue after they finish playing. If loop mode is off, songs that have finished playing are not returned to the queue. Arguments: None. Return value: True if loop mode is set, False if it is not. =item boolean B () Tells you whether the current song is paused or not. Arguments: None. Return value: True if the current song is paused, otherwise False. =item boolean B () Tells you whether the queue consumption (advancement) is activated. Arguments: None. Return value: True if new songs are going to be played from the queue after the current song is finished, otherwise False. =item double B () Returns the time at which the song queue was last modified. This method is intended for use by GUI clients that don't want to waste time downloading the entire contents of the song queue if it hasn't changed. Arguments: None. Return value: A floating-point number that represents time as the number of seconds since the epoch. =item int B () Returns the number of items in the song queue. Arguments: None. Return value: The number of items in the song queue. =item array B () =item array B (array) Lists the song queue's contents. If a range is specified, only the items that fall within that range are listed. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is returned. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: An array of (base64-encoded) strings, representing the selected range from the song queue's contents. =item boolean B (array, int) Moves a range of items to a new position within the queue. Arguments: The first argument is an array of integers that represents a range of items to be moved. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. * The second argument, "destination", specifies the position in the queue where the items will be moved. Return value: Nothing meaningful. =item boolean B (array, int) Moves the items referenced by a list of positions to a new position. Arguments: The first argument is an array of integers that represents a list of the positions of the items to be moved. * The second argument, "destination", specifies the position in the queue where the items will be moved. Return value: Nothing meaningful. =item boolean B () =item boolean B (int) Stops the current song (if any), and jumps ahead to a song that is currently in the queue. The skipped songs are recorded in the history as if they had been played. When called without arguments, this behaves very much like the skip() method, except that it will have an effect even if nothing is currently playing. Arguments: A single integer that tells how far forward into the song queue to advance. A value of 1 will cause the first song in the queue to play, 2 will cause the second song in the queue to play, and so on. If no argument is given, a value of 1 is assumed. Return value: Nothing meaningful. =item boolean B () Does nothing, successfully. Arguments: None. Return value: Nothing meaningful. =item boolean B () Pauses the currently playing song. Arguments: None. Return value: Nothing meaningful. =item boolean B (array) Adds items to the beginning of the queue. Argument: An array of (base64-encoded) strings, representing the items to be added. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server has no idea what the client's current working directory is. Return value: Nothing meaningful. =item boolean B () =item boolean B (int) Stops the current song (if any), removes the most recently played song from the history, and puts these songs at the head of the queue. When loop mode is on, the songs at the tail of the song queue are used instead of the most recently played songs in the history. Arguments: A single integer that tells how far back in the history list to retreat. A value of 1 will cause the most recent song to play, 2 will cause the second most recent song to play, and so on. If no argument is given, a value of 1 is assumed. Return value: Nothing meaningful. =item boolean B () Places the currently playing song at the beginning of the queue. Arguments: None. Return value: Nothing meaningful. =item int B () Returns the number of items in the song queue. Arguments: None. Return value: The number of items in the song queue. =item boolean B () Tells the server to reread its player configuration file. Arguments: None. Return value: Nothing meaningful. =item boolean B (base64) =item boolean B (base64, array) Removes all items that match the given regular expression. Arguments: A regular expression that specifies which items to remove. * Optionally, an array of integers may be given as a second argument. This argument represents a range to which the removal will be limited. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. =item boolean B (array) Replaces the contents of the queue with the given items. This is equivalent to calling clear() and prepend() in succession, except that this operation is atomic. Argument: An array of (base64-encoded) strings, representing the items to be added. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server isn't aware of the client's current working directory. Return value: Nothing meaningful. =item boolean B () =item boolean B (array) Reverses the order of the items in the queue. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is affected. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. =item boolean B () Allows new songs to be played again after halt_queue() has been called. Arguments: None. Return value: Nothing meaningful. =item boolean B () Allows new songs to be played again after halt_queue() has been called. Arguments: None. Return value: Nothing meaningful. =item boolean B (int) Sets the limit on the size of the history list stored in memory. This will irrevocably discard history entries if the new limit is lower than the current size of the history list. Arguments: The new maximum number of history entries. If this value is negative, the history limit will be set to zero. Return value: Nothing meaningful. =item boolean B (boolean) Turns loop mode on or off. If loop mode is on, songs are returned to the end of the song queue after they finish playing. If loop mode is off, songs that have finished playing are not returned to the queue. Arguments: True if you want to turn loop mode on, False if you want to turn it off. Return value: Nothing meaningful. =item base64 B () Returns a textual description of the server's player configuration. Arguments: None. Return value: A (base64-encoded) string that shows which programs will be used to play the various file-types recognized by the Moosic server. =item boolean B () =item boolean B (array) Rearrange the contents of the queue into a random order. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is affected. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. =item boolean B () Skips the rest of the current song to play the next song in the queue. This only has an effect if there actually is a current song. Arguments: None. Return value: Nothing meaningful. =item boolean B () =item boolean B (array) Arranges the contents of the queue into sorted order. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is affected. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. =item boolean B () Stops playing the current song and stops new songs from playing. The current song is returned to the head of the song queue and is not recorded in the history list. If loop mode is on, the current song won't be placed at the end of the song queue when it is stopped. Arguments: None. Return value: Nothing meaningful. =item boolean B (base64, base64) =item boolean B (base64, base64, array) Performs a regular expression substitution on the items in the queue. Arguments: The first is a (base64-encoded) regular expression that specifies the text to be replaced. * The second argument is the (base64-encoded) string that will be used to replace the first occurrence of the regular expression within each queue item. Any backslash escapes in this string will be processed, including special character translation (e.g. "\n" to newline) and backreferences to groups within the match. * Optionally, an array of integers may be given as a third argument. This argument represents a range to which the substitution will be limited. This range is interpreted in the same way as the range argument in other Moosic methods. * If performing a replacement changes an item in the queue into the empty string, then it is removed from the queue. Return value: Nothing meaningful. =item boolean B (base64, base64) =item boolean B (base64, base64, array) Performs a global regular expression substitution on the items in the queue. Arguments: The first is a (base64-encoded) regular expression that specifies the text to be replaced. * The second argument is the (base64-encoded) string that will be used to replace all occurrences of the regular expression within each queue item. Any backslash escapes in this string will be processed, including special character translation (e.g. "\n" to newline) and backreferences to the substrings matched by individual groups in the pattern. * Optionally, an array of integers may be given as a third argument. This argument represents a range to which the substitution will be limited. This range is interpreted in the same way as the range argument in other Moosic methods. * If performing a replacement changes an item in the queue into the empty string, then it is removed from the queue. Return value: Nothing meaningful. =item boolean B (array, array) Swaps the items contained in one range with the items contained in the other range. Return value: Nothing meaningful. =item array B () Return an array of all available XML-RPC methods on this server. =item string B (string) Given the name of a method, return a help string. =item array B (string) Given the name of a method, return an array of legal signatures. Each signature is an array of strings. The first item of each signature is the return type, and any others items are parameter types. =item array B (array) Process an array of calls, and return an array of results. Calls should be structs of the form {'methodName': string, 'params': array}. Each result will either be a single-item array containg the result value, or a struct of the form {'faultCode': int, 'faultString': string}. This is useful when you need to make lots of small calls without lots of round trips. =item boolean B () Turns loop mode on if it is off, and turns it off if it is on. If loop mode is on, songs are returned to the end of the song queue after they finish playing. If loop mode is off, songs that have finished playing are not returned to the queue. Arguments: None. Return value: Nothing meaningful. =item boolean B () Pauses the current song if it is playing, and unpauses if it is paused. Arguments: None. Return value: Nothing meaningful. =item boolean B () Unpauses the current song. Arguments: None. Return value: Nothing meaningful. =item string B () Returns the Moosic server's version string. Arguments: None. Return value: The version string for the Moosic server. =back =head1 Section 4: Writing a Moosic Client in Python As demonstrated in section 0, communicating with a Moosic server is very easy to do with Python. In this section, I'll merely elaborate on the details that were omitted from section 0 for the sake of brevity. First of all, you should know that LocalMoosicProxy() can be called with a filename argument to specify the location of the Moosic server's socket file. This is useful if moosicd was started with the "-c" option. Refer to the moosic_factory.py module's documentation (moosic_factory.html). Next, note that many of the Moosic server's methods accept or return special types of objects from the xmlrpclib module, namely Boolean and Binary. These object types serve the purpose of bridging the small mismatch between the data-types supported by XML-RPC and Python's intrinsic data-types. Boolean objects present no unusual problem, since they evaluate to a correct truth value without any extra effort. However, you must take care when using the Moosic methods that accept or return Binary objects. Read the documentation for the xmlrpclib module for details on how to work with these objects. The basic technique boils down to wrapping up a string inside a Binary object before sending it to the server, and using the "data" attribute to access the string data within the Binary objects returned by the server. Regular strings can't be used because XML-RPC's normal string data-type can't handle multiple 8-bit strings within a single request if the strings use different encodings. =head1 Section 5: Writing a Moosic Client in Another Language If you are not using Python to write your Moosic client, the first issue to deal with is deciding upon an XML-RPC implementation. For most popular programming languages, there are multiple XML-RPC implementations available. Most of the possibilities are listed at L. Since XML-RPC is an open specification, you can create your own implementation if you don't like any of the ones that already exist. Once you've got an XML-RPC library that you like, the big hurdle to overcome is to make that library send its RPC calls over a Unix socket instead of an IP socket. I was able to do this pretty easily with Python's xmlrpclib since it is designed to allow pluggable transport methods: all I had to do was subclass my own Transport type and plug it back into the original library's classes. (If your language and/or library of choice makes this task difficult, then you may begin to understand why some Python programmers are so smug.) After you are capable of sending XML-RPC requests through a Unix socket, you can go ahead and start sending requests to a Moosic server. Refer to the end of section 2 for information on how to address a Moosic server. Refer to section 3 for a list of valid server requests. If you can't be bothered to find or hack together an XML-RPC library that works with Unix sockets, then you can still talk to a Moosic server that is listening on an IP socket, but this is less than ideal since listening on an IP socket is not default behavior for most Moosic servers. =head1 Section 6: API Version History (ChangeLog) =over =item * S<1.8> First implemented in moosicd 1.5.1. The following methods were added: swap =item * S<1.7> First implemented by moosicd 1.5.0. The following methods were added: skip, current_time The following methods had their behavior significantly changed: previous, next Specifically, the previous() method no longer activates queue advancement if it had been disabled before. This means that calling previous() no longer necessarily causes a song to start playing. The next() method was changed to more closely parallel the behavior of previous(): it takes a single optional integer argument to allow immediate advancement by more than one song at a time, and it has an effect even when no song is currently playing. The new skip() method implements the exact same behavior that was previously exhibited by next(). Last, and most importantly, the name of the default socket file for communicating with the server via unix sockets was changed from $CONFIG_DIR/socket-$HOSTNAME to $CONFIG_DIR/socket. If you are a Python programmer and you use the updated moosic_factory.py file from Moosic version 1.5.0, then you don't have to make any changes. Otherwise, you must change your client's code to connect to the file named "socket" instead of the file named "socket-something.example.com". If your client only talks to the Moosic server through TCP/IP, then you don't have to make any changes, of course. =item * S<1.6> First implemented by moosicd 1.4.10. The following methods were added: getconfig =item * S<1.5> First implemented by moosicd 1.4.6. The following methods were added: sub, sub_all, stop =item * S<1.4> First implemented by moosicd 1.4.5. The following methods were added: previous =item * S<1.3> First implemented by moosicd 1.4.4. The following methods were added: replace, replace_range, last_queue_update =item * S<1.2> First implemented by moosicd 1.4.2. The following methods were added: cut_list, crop_list =item * S<1.1> First implemented by moosicd 1.4.1. The following methods were added: is_looping, set_loop_mode, toggle_loop_mode =item * S<1.0> First implemented by moosicd 1.4.0. The following methods were included: api_version, append, clear, crop, current, cut, die, filter, get_history_limit, halt_queue, haltqueue, history, indexed_list, insert, is_paused, is_queue_running, length, list, move, move_list, next, no_op, pause, prepend, putback, queue_length, reconfigure, remove, reverse, run_queue, runqueue, set_history_limit, showconfig, shuffle, sort, system.listMethods, system.methodHelp, system.methodSignature, system.multicall, toggle_pause, unpause, version =back moosic-1.5.6/doc/Moosic_API.30000644000175000017500000014322610300321616016301 0ustar danieldaniel00000000000000.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.3 .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "Moosic_API 3" .TH Moosic_API 3 "2005-08-16" "Moosic 1.5.2" "" .SH "NAME" Moosic_API \- How to write your own Moosic client. .SH "Introduction" .IX Header "Introduction" \&\*(L"moosicd\*(R" is a program that implements the server portion of the Moosic jukebox system. This server provides services for manipulating a queue of songs to be played, as well as for controlling the playing of these songs. A Moosic client sends requests to the server, and receives data in response to these requests. The \*(L"moosic\*(R" command line utility is the canonical Moosic client, and provides a way to control a Moosic server from an interactive command shell or from a shell script. However, you might not be satisfied by the interface that is provided by the \*(L"moosic\*(R" command, so I have written this document to describe how to communicate with a Moosic server in your own programs. .PP [This document was written by Daniel Pearson , and has been placed into the public domain.] .SH "Section 0: Instructions for Impatient Developers" .IX Header "Section 0: Instructions for Impatient Developers" .IP "1." 4 Plan to write your program with Python and xmlrpclib. xmlrpclib is included with Python 2.2 or later, but if you need to use an earlier version of Python, xmlrpclib can be downloaded from . .IP "2." 4 If you can't or don't want to write your program with Python and xmlrpclib, you won't benefit from this section and will have to read section 2 of this document. .IP "3." 4 Create a proxy which communicates with moosicd: .Sp .Vb 2 \& >>> import moosic.client.factory \& >>> proxy = moosic.client.factory.LocalMoosicProxy() .Ve .IP "4." 4 Read section 3 for the documentation of all the methods supported by the proxy object. .IP "5." 4 Use the proxy to get information from the server and to send commands to it. For example: .Sp .Vb 27 \& >>> proxy.list() \& [] \& >>> proxy.is_queue_running() \& True \& >>> proxy.haltqueue() \& True \& >>> proxy.is_queue_running() \& False \& >>> proxy.append([xmlrpc.Binary(i) for i in \& ... ['/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3', \& ... '/home/daniel/music/Fiona_Apple/When_The_Pawn/04\-Love_Ridden.ogg', \& ... "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"]]) \& True \& >>> proxy.list() \& [, \& , \& ] \& >>> [i.data for i in proxy.list()] \& ['/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3', \& '/home/daniel/music/Fiona_Apple/When_The_Pawn/04\-Love_Ridden.ogg', \& "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"] \& >>> proxy.sort() \& True \& >>> [i.data for i in proxy.list()] \& ['/home/daniel/music/Fiona_Apple/When_The_Pawn/04\-Love_Ridden.ogg', \& '/home/daniel/music/Weird_Al/Pretty_Fly_for_a_Rabbi.mp3', \& "/home/daniel/music/Zelda/Great_Fairy's_Fountain.mid"] .Ve .IP "6." 4 If you wish to communicate with a moosicd that is listening for requests on an \s-1IP\s0 socket instead of a Unix domain socket, you should use InetMoosicProxy instead of LocalMoosicProxy. Here's an example: .Sp .Vb 2 \& >>> import moosic.client.factory \& >>> proxy = moosic.client.factory.InetMoosicProxy('example.com', 8080) .Ve .SH "Section 1: The Moosic Server's Data Model" .IX Header "Section 1: The Moosic Server's Data Model" This section describes, in precise terms, the data objects that can be accessed and manipulated by sending requests to the Moosic server. .IP "song queue" 4 .IX Item "song queue" This is the foremost data object maintained by the Moosic server. It is an ordered sequence of strings that represents the queue of songs that are waiting to be played. Each item in this list identifies a song that will be played by the Moosic server. Usually, these items are the names of files on disk that contain each song, but this does not have to be the case. For instance, an \s-1HTTP\s0 \&\s-1URL\s0 might be used to name a song if a program that can play songs from the Web is appropriately registered with the Moosic server (see \*(L"player configuration\*(R" later in this section). .IP "current song" 4 .IX Item "current song" This is a string that identifies the song that is currently being played by the Moosic server. If nothing is currently playing, then this will be the empty string. .IP "queue running flag" 4 .IX Item "queue running flag" This is a boolean value that indicates whether the Moosic server will start playing a new song as soon as the current song has finished and the song queue is not empty. .IP "pause flag" 4 .IX Item "pause flag" This is a boolean value that indicates whether the current song is paused or not. .IP "loop mode flag" 4 .IX Item "loop mode flag" This is a boolean value that sets \*(L"loop mode\*(R". When loop mode is on, songs are returned to the end of the song queue when they finish playing instead of being discarded. .IP "history" 4 .IX Item "history" This is a list of songs that the Moosic server has finished playing. Note that songs named in this list may have finished playing early at the request of a user (i.e. through use of the \*(L"next\*(R" command). Each entry in this list is actually a 3\-tuple of (song, start time, finish time). .IP "maximum history size" 4 .IX Item "maximum history size" This is the maximum number of songs that will be stored in the history list. Old entries are removed from the history to make room for newer entries when this limit is reached. .IP "player configuration" 4 .IX Item "player configuration" This is an ordered mapping that associates regular expressions (text patterns) to programs. For each regular expression, the associated program is expected to be able to play any queue items that match that regular expression. Each program is a list in which the name of the executable file that contains the program is the first element and the program's arguments are the rest of the elements. .IP "last queue update" 4 .IX Item "last queue update" This is the time at which the song queue was last modified. It a floating-point number that represents time as the number of seconds since the epoch. .IP "server version" 4 .IX Item "server version" This is a string that describes the version of the program that implements the Moosic server. It has no specific, well-defined semantics. .IP "\s-1API\s0 version" 4 .IX Item "API version" This is a pair of integers, one representing the \*(L"major\*(R" version, and the other representing the \*(L"minor\*(R" version. These numbers are meant to provide some useful compatibility information to Moosic clients. As this \s-1API\s0 changes, these numbers will change in the following ways. If the \s-1API\s0 has been changed in a backward-compatible way (e.g. a new method was added or an existing method was overloaded), then the minor version will increase and the major version will remain unchanged. However, if the \s-1API\s0 has been changed in such a way that existing code that uses the \s-1API\s0 might break (e.g. a method was removed or its return value or parameter types were changed), then the major version will increase and the minor version may be reset to any value (although it will usually be reset zero). .SH "Section 2: The Low-Level Details of Client-Server Communication" .IX Header "Section 2: The Low-Level Details of Client-Server Communication" The information in this section is generally only necessary to people who wish to write a Moosic client in a programming language other than Python. If you are using Python to write a Moosic client, then you can use the classes LocalMoosicProxy and InetMoosicProxy from the moosic_factory.py module, and blissfully ignore most of these gory details. However, Python programmers can also benefit from reading this section, as it will deepen their understanding of Moosic's inter-process communication model. .PP The first thing to know about writing your own Moosic client is that communication between the client and server is done through a BSD-style socket. Read the \*(L"socket\*(R" manual page (and related manual pages) on a Unix system if you are unfamiliar with \s-1BSD\s0 sockets. The socket used by Moosic belongs to the Unix-domain protocol family (\s-1PF_UNIX\s0 or \s-1PF_LOCAL\s0) and has a type of \s-1SOCK_STREAM\s0. This means that a Moosic client can only communicate with a Moosic server that is running on the same computer as the client. This limitation is a very purposeful part of Moosic's design. It has the advantage of vastly reducing the consequences of any security flaws that Moosic might have. .PP If you really, really think that you need the client and the server to run on separate hosts, then you can run moosicd with the \-t option, which tells it to listen on a \s-1TCP/IP\s0 socket instead of a Unix domain socket. I recommend firewalling such a port very carefully. .PP Regardless of which kind of socket is used by the server, XML-RPC is used as the data protocol for requests and responses. For an introduction to \s-1XML\-RPC\s0, see the XML-RPC homepage and the XML-RPC \s-1HOWTO\s0 . Python users should note that if the XML-RPC \s-1HOWTO\s0 tells you that you need to install a third-party library to use \s-1XML\-RPC\s0, it is assuming that you are using a Python version earlier than 2.2. Since version 2.2, Python has included the xmlrpclib module in its standard library. .PP In summary, all you need to do to talk to a Moosic server in your own program is to send XML-RPC requests to the appropriate address. By default, the appropriate address for contacting moosicd is the file named \*(L"socket\*(R" in a directory named \*(L".moosic\*(R" in the home directory of the user that started moosicd (i.e. \*(L"~/.moosic/socket\*(R"). If moosicd is started with the \-c option, then the directory that contains \*(L"socket\*(R" will be the argument provided to the \-c option instead of ~/.moosic. If moosicd is started with the \-t option, then clients will have to address it by using a (host, port) pair instead of a filename. .SH "Section 3: Valid Moosic Server Methods" .IX Header "Section 3: Valid Moosic Server Methods" moosicd's XML-RPC server implements the introspection \s-1API\s0 mentioned on , so the \s-1API\s0 presented by moosicd is essentially self\-documenting. Thus, the information in this section has been automatically generated by querying a running Moosic server. .PP The Moosic \s-1API\s0 contains the following methods: .IP "array \fBapi_version\fR ()" 4 .IX Item "array api_version ()" .Vb 1 \& Returns the version number for the API that the server implements. .Ve .Sp .Vb 4 \& Arguments: None. \& Return value: The version number, which is a 2\-element array of \& integers. The first element is the major version, and the second \& element is the minor version. .Ve .IP "boolean \fBappend\fR (array)" 4 .IX Item "boolean append (array)" .Vb 1 \& Adds items to the end of the queue. .Ve .Sp .Vb 6 \& Argument: An array of (base64\-encoded) strings, representing the items to be \& added. \& * When adding local filenames to the queue, only absolute pathnames should \& be used. Using relative pathnames would be foolish because the server \& has no idea what the client's current working directory is. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBclear\fR ()" 4 .IX Item "boolean clear ()" .Vb 1 \& Removes all items from the queue. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBcrop\fR (array)" 4 .IX Item "boolean crop (array)" .Vb 1 \& Remove all queued items that do not fall within the given range. .Ve .Sp .Vb 9 \& Arguments: An array of integers that represents a range. \& * If the range contains a single integer, it will represent all members \& of the queue whose index is greater than or equal to the value of the \& integer. \& * If the range contains two integers, it will represent all members of \& the queue whose index is greater than or equal to the value of the \& first integer and less than the value of the second integer. \& * If the range contains more than two integers, an error will occur. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBcrop_list\fR (array)" 4 .IX Item "boolean crop_list (array)" .Vb 1 \& Removes all items except for those referenced by a list of positions. .Ve .Sp .Vb 3 \& Arguments: An array of integers that represents a list of the positions of \& the items to be kept. \& Return value: Nothing meaningful. .Ve .IP "base64 \fBcurrent\fR ()" 4 .IX Item "base64 current ()" .Vb 1 \& Returns the name of the currently playing song. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: The name of the currently playing song. .Ve .IP "double \fBcurrent_time\fR ()" 4 .IX Item "double current_time ()" .Vb 1 \& Returns the amount of time that the current song has been playing. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: The number of seconds that the current song has been playing. .Ve .IP "boolean \fBcut\fR (array)" 4 .IX Item "boolean cut (array)" .Vb 1 \& Remove all queued items that fall within the given range. .Ve .Sp .Vb 9 \& Arguments: An array of integers that represents a range. \& * If the range contains a single integer, it will represent all members \& of the queue whose index is greater than or equal to the value of the \& integer. \& * If the range contains two integers, it will represent all members of \& the queue whose index is greater than or equal to the value of the \& first integer and less than the value of the second integer. \& * If the range contains more than two integers, an error will occur. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBcut_list\fR (array)" 4 .IX Item "boolean cut_list (array)" .Vb 1 \& Removes the items referenced by a list of positions within the queue. .Ve .Sp .Vb 3 \& Arguments: An array of integers that represents a list of the positions of \& the items to be removed. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBdie\fR ()" 4 .IX Item "boolean die ()" .Vb 1 \& Tells the server to terminate itself. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBfilter\fR (base64)" 4 .IX Item "boolean filter (base64)" .PD 0 .IP "boolean \fBfilter\fR (base64, array)" 4 .IX Item "boolean filter (base64, array)" .PD .Vb 1 \& Removes all items that don't match the given regular expression. .Ve .Sp .Vb 12 \& Arguments: A regular expression that specifies which items to keep. \& * Optionally, an array of integers may be given as a second argument. \& This argument represents a range to which the filtering will be \& limited. \& * If the range contains a single integer, it will represent all members \& of the queue whose index is greater than or equal to the value of the \& integer. \& * If the range contains two integers, it will represent all members of \& the queue whose index is greater than or equal to the value of the \& first integer and less than the value of the second integer. \& * If the range contains more than two integers, an error will occur. \& Return value: Nothing meaningful. .Ve .IP "int \fBget_history_limit\fR ()" 4 .IX Item "int get_history_limit ()" .Vb 1 \& Gets the limit on the size of the history list stored in memory. .Ve .Sp .Vb 3 \& Arguments: None. \& Return value: The maximum number of history entries that the server will \& remember. .Ve .IP "array \fBgetconfig\fR ()" 4 .IX Item "array getconfig ()" .Vb 1 \& Returns a list of the server's filetype\-player associations. .Ve .Sp .Vb 6 \& Arguments: None. \& Return value: An array of pairs. The first element of each pair is a \& (base64\-encoded) string that represents a regular expression pattern, \& and the second element is a (base64\-encoded) string that represents the \& system command that should be used to handle songs that match the \& corresponding pattern. .Ve .IP "boolean \fBhalt_queue\fR ()" 4 .IX Item "boolean halt_queue ()" .Vb 2 \& Stops any new songs from being played. Use run_queue() to reverse this \& state. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBhaltqueue\fR ()" 4 .IX Item "boolean haltqueue ()" .Vb 2 \& Stops any new songs from being played. Use run_queue() to reverse this \& state. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "array \fBhistory\fR ()" 4 .IX Item "array history ()" .PD 0 .IP "array \fBhistory\fR (int)" 4 .IX Item "array history (int)" .PD .Vb 1 \& Returns a list of the items that were recently played. .Ve .Sp .Vb 15 \& Arguments: If a positive integer argument is given, then no more than that \& number of entries will be returned. If a number is not specified, or if \& zero is given, then the entire history is returned. The result is \& undefined if a negative integer argument is given (but does not raise an \& exception). \& Return value: An array of triples, each representing a song that was played \& along with the times that it started and finished playing. \& * The first member of the pair is a (base64\-encoded) string which \& represents the song that was previously played. \& * The second member of the pair is a floating point number which \& represents the time that the song started playing in seconds since the \& epoch. \& * The third member of the pair is a floating point number which \& represents the time that the song finished playing in seconds since the \& epoch. .Ve .IP "struct \fBindexed_list\fR ()" 4 .IX Item "struct indexed_list ()" .PD 0 .IP "struct \fBindexed_list\fR (array)" 4 .IX Item "struct indexed_list (array)" .PD .Vb 2 \& Lists the song queue's contents. If a range is specified, only the \& items that fall within that range are listed. .Ve .Sp .Vb 4 \& This differs from list() only in its return value, and is useful when you \& want to know the starting position of your selected range within the song \& queue (which can be different than the starting index of the specified range \& if, for example, the starting index is a negative integer). .Ve .Sp .Vb 14 \& Arguments: Either none, or an array of integers that represents a range. \& * If no range is given, the whole list is returned. \& * If the range contains a single integer, it will represent all members \& of the queue whose index is greater than or equal to the value of the \& integer. \& * If the range contains two integers, it will represent all members of \& the queue whose index is greater than or equal to the value of the \& first integer and less than the value of the second integer. \& * If the range contains more than two integers, an error will occur. \& Return value: A struct with two elements. This first is "list", an array of \& (base64\-encoded) strings, representing the selected range from the song \& queue's contents. The second is "start", an integer index value that \& represents the position of the first item of the returned list in the \& song queue. .Ve .IP "boolean \fBinsert\fR (array, int)" 4 .IX Item "boolean insert (array, int)" .Vb 1 \& Inserts items at a given position in the queue. .Ve .Sp .Vb 8 \& Arguments: The first argument is an array of (base64\-encoded) strings, \& representing the items to be added. \& * The second argument specifies the position in the queue where the items \& will be inserted. \& * When adding local filenames to the queue, only absolute pathnames should \& be used. Using relative pathnames would be foolish because the server \& has no idea what the client's current working directory is. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBis_looping\fR ()" 4 .IX Item "boolean is_looping ()" .Vb 1 \& Tells you whether loop mode is on or not. .Ve .Sp .Vb 3 \& If loop mode is on, songs are returned to the end of the song queue after \& they finish playing. If loop mode is off, songs that have finished playing \& are not returned to the queue. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: True if loop mode is set, False if it is not. .Ve .IP "boolean \fBis_paused\fR ()" 4 .IX Item "boolean is_paused ()" .Vb 1 \& Tells you whether the current song is paused or not. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: True if the current song is paused, otherwise False. .Ve .IP "boolean \fBis_queue_running\fR ()" 4 .IX Item "boolean is_queue_running ()" .Vb 1 \& Tells you whether the queue consumption (advancement) is activated. .Ve .Sp .Vb 3 \& Arguments: None. \& Return value: True if new songs are going to be played from the queue after \& the current song is finished, otherwise False. .Ve .IP "double \fBlast_queue_update\fR ()" 4 .IX Item "double last_queue_update ()" .Vb 1 \& Returns the time at which the song queue was last modified. .Ve .Sp .Vb 2 \& This method is intended for use by GUI clients that don't want to waste time \& downloading the entire contents of the song queue if it hasn't changed. .Ve .Sp .Vb 3 \& Arguments: None. \& Return value: A floating\-point number that represents time as the number of \& seconds since the epoch. .Ve .IP "int \fBlength\fR ()" 4 .IX Item "int length ()" .Vb 1 \& Returns the number of items in the song queue. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: The number of items in the song queue. .Ve .IP "array \fBlist\fR ()" 4 .IX Item "array list ()" .PD 0 .IP "array \fBlist\fR (array)" 4 .IX Item "array list (array)" .PD .Vb 2 \& Lists the song queue's contents. If a range is specified, only the \& items that fall within that range are listed. .Ve .Sp .Vb 11 \& Arguments: Either none, or an array of integers that represents a range. \& * If no range is given, the whole list is returned. \& * If the range contains a single integer, it will represent all members \& of the queue whose index is greater than or equal to the value of the \& integer. \& * If the range contains two integers, it will represent all members of \& the queue whose index is greater than or equal to the value of the \& first integer and less than the value of the second integer. \& * If the range contains more than two integers, an error will occur. \& Return value: An array of (base64\-encoded) strings, representing the \& selected range from the song queue's contents. .Ve .IP "boolean \fBmove\fR (array, int)" 4 .IX Item "boolean move (array, int)" .Vb 1 \& Moves a range of items to a new position within the queue. .Ve .Sp .Vb 12 \& Arguments: The first argument is an array of integers that represents a \& range of items to be moved. \& * If the range contains a single integer, it will represent all members \& of the queue whose index is greater than or equal to the value of the \& integer. \& * If the range contains two integers, it will represent all members of \& the queue whose index is greater than or equal to the value of the \& first integer and less than the value of the second integer. \& * If the range contains more than two integers, an error will occur. \& * The second argument, "destination", specifies the position in the queue \& where the items will be moved. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBmove_list\fR (array, int)" 4 .IX Item "boolean move_list (array, int)" .Vb 1 \& Moves the items referenced by a list of positions to a new position. .Ve .Sp .Vb 5 \& Arguments: The first argument is an array of integers that represents a \& list of the positions of the items to be moved. \& * The second argument, "destination", specifies the position in the queue \& where the items will be moved. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBnext\fR ()" 4 .IX Item "boolean next ()" .PD 0 .IP "boolean \fBnext\fR (int)" 4 .IX Item "boolean next (int)" .PD .Vb 5 \& Stops the current song (if any), and jumps ahead to a song that is \& currently in the queue. The skipped songs are recorded in the history as if \& they had been played. When called without arguments, this behaves very \& much like the skip() method, except that it will have an effect even if \& nothing is currently playing. .Ve .Sp .Vb 5 \& Arguments: A single integer that tells how far forward into the song queue \& to advance. A value of 1 will cause the first song in the queue to play, \& 2 will cause the second song in the queue to play, and so on. If no \& argument is given, a value of 1 is assumed. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBno_op\fR ()" 4 .IX Item "boolean no_op ()" .Vb 1 \& Does nothing, successfully. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBpause\fR ()" 4 .IX Item "boolean pause ()" .Vb 1 \& Pauses the currently playing song. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBprepend\fR (array)" 4 .IX Item "boolean prepend (array)" .Vb 1 \& Adds items to the beginning of the queue. .Ve .Sp .Vb 6 \& Argument: An array of (base64\-encoded) strings, representing the items to be \& added. \& * When adding local filenames to the queue, only absolute pathnames should \& be used. Using relative pathnames would be foolish because the server \& has no idea what the client's current working directory is. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBprevious\fR ()" 4 .IX Item "boolean previous ()" .PD 0 .IP "boolean \fBprevious\fR (int)" 4 .IX Item "boolean previous (int)" .PD .Vb 4 \& Stops the current song (if any), removes the most recently played song \& from the history, and puts these songs at the head of the queue. When loop \& mode is on, the songs at the tail of the song queue are used instead of the \& most recently played songs in the history. .Ve .Sp .Vb 5 \& Arguments: A single integer that tells how far back in the history list to \& retreat. A value of 1 will cause the most recent song to play, 2 will \& cause the second most recent song to play, and so on. If no argument is \& given, a value of 1 is assumed. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBputback\fR ()" 4 .IX Item "boolean putback ()" .Vb 1 \& Places the currently playing song at the beginning of the queue. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "int \fBqueue_length\fR ()" 4 .IX Item "int queue_length ()" .Vb 1 \& Returns the number of items in the song queue. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: The number of items in the song queue. .Ve .IP "boolean \fBreconfigure\fR ()" 4 .IX Item "boolean reconfigure ()" .Vb 1 \& Tells the server to reread its player configuration file. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBremove\fR (base64)" 4 .IX Item "boolean remove (base64)" .PD 0 .IP "boolean \fBremove\fR (base64, array)" 4 .IX Item "boolean remove (base64, array)" .PD .Vb 1 \& Removes all items that match the given regular expression. .Ve .Sp .Vb 11 \& Arguments: A regular expression that specifies which items to remove. \& * Optionally, an array of integers may be given as a second argument. \& This argument represents a range to which the removal will be limited. \& * If the range contains a single integer, it will represent all members \& of the queue whose index is greater than or equal to the value of the \& integer. \& * If the range contains two integers, it will represent all members of \& the queue whose index is greater than or equal to the value of the \& first integer and less than the value of the second integer. \& * If the range contains more than two integers, an error will occur. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBreplace\fR (array)" 4 .IX Item "boolean replace (array)" .Vb 1 \& Replaces the contents of the queue with the given items. .Ve .Sp .Vb 2 \& This is equivalent to calling clear() and prepend() in succession, except that this \& operation is atomic. .Ve .Sp .Vb 6 \& Argument: An array of (base64\-encoded) strings, representing the items to be \& added. \& * When adding local filenames to the queue, only absolute pathnames \& should be used. Using relative pathnames would be foolish because \& the server isn't aware of the client's current working directory. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBreverse\fR ()" 4 .IX Item "boolean reverse ()" .PD 0 .IP "boolean \fBreverse\fR (array)" 4 .IX Item "boolean reverse (array)" .PD .Vb 1 \& Reverses the order of the items in the queue. .Ve .Sp .Vb 10 \& Arguments: Either none, or an array of integers that represents a range. \& * If no range is given, the whole list is affected. \& * If the range contains a single integer, it will represent all members \& of the queue whose index is greater than or equal to the value of the \& integer. \& * If the range contains two integers, it will represent all members of \& the queue whose index is greater than or equal to the value of the \& first integer and less than the value of the second integer. \& * If the range contains more than two integers, an error will occur. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBrun_queue\fR ()" 4 .IX Item "boolean run_queue ()" .Vb 1 \& Allows new songs to be played again after halt_queue() has been called. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBrunqueue\fR ()" 4 .IX Item "boolean runqueue ()" .Vb 1 \& Allows new songs to be played again after halt_queue() has been called. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBset_history_limit\fR (int)" 4 .IX Item "boolean set_history_limit (int)" .Vb 1 \& Sets the limit on the size of the history list stored in memory. .Ve .Sp .Vb 2 \& This will irrevocably discard history entries if the new limit is lower than \& the current size of the history list. .Ve .Sp .Vb 3 \& Arguments: The new maximum number of history entries. If this value is \& negative, the history limit will be set to zero. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBset_loop_mode\fR (boolean)" 4 .IX Item "boolean set_loop_mode (boolean)" .Vb 1 \& Turns loop mode on or off. .Ve .Sp .Vb 3 \& If loop mode is on, songs are returned to the end of the song queue after \& they finish playing. If loop mode is off, songs that have finished playing \& are not returned to the queue. .Ve .Sp .Vb 3 \& Arguments: True if you want to turn loop mode on, False if you want to turn \& it off. \& Return value: Nothing meaningful. .Ve .IP "base64 \fBshowconfig\fR ()" 4 .IX Item "base64 showconfig ()" .Vb 1 \& Returns a textual description of the server's player configuration. .Ve .Sp .Vb 3 \& Arguments: None. \& Return value: A (base64\-encoded) string that shows which programs will be \& used to play the various file\-types recognized by the Moosic server. .Ve .IP "boolean \fBshuffle\fR ()" 4 .IX Item "boolean shuffle ()" .PD 0 .IP "boolean \fBshuffle\fR (array)" 4 .IX Item "boolean shuffle (array)" .PD .Vb 1 \& Rearrange the contents of the queue into a random order. .Ve .Sp .Vb 10 \& Arguments: Either none, or an array of integers that represents a range. \& * If no range is given, the whole list is affected. \& * If the range contains a single integer, it will represent all members \& of the queue whose index is greater than or equal to the value of the \& integer. \& * If the range contains two integers, it will represent all members of \& the queue whose index is greater than or equal to the value of the \& first integer and less than the value of the second integer. \& * If the range contains more than two integers, an error will occur. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBskip\fR ()" 4 .IX Item "boolean skip ()" .Vb 2 \& Skips the rest of the current song to play the next song in the queue. \& This only has an effect if there actually is a current song. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBsort\fR ()" 4 .IX Item "boolean sort ()" .PD 0 .IP "boolean \fBsort\fR (array)" 4 .IX Item "boolean sort (array)" .PD .Vb 1 \& Arranges the contents of the queue into sorted order. .Ve .Sp .Vb 10 \& Arguments: Either none, or an array of integers that represents a range. \& * If no range is given, the whole list is affected. \& * If the range contains a single integer, it will represent all members \& of the queue whose index is greater than or equal to the value of the \& integer. \& * If the range contains two integers, it will represent all members of \& the queue whose index is greater than or equal to the value of the \& first integer and less than the value of the second integer. \& * If the range contains more than two integers, an error will occur. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBstop\fR ()" 4 .IX Item "boolean stop ()" .Vb 4 \& Stops playing the current song and stops new songs from playing. The \& current song is returned to the head of the song queue and is not recorded \& in the history list. If loop mode is on, the current song won't be placed at \& the end of the song queue when it is stopped. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBsub\fR (base64, base64)" 4 .IX Item "boolean sub (base64, base64)" .PD 0 .IP "boolean \fBsub\fR (base64, base64, array)" 4 .IX Item "boolean sub (base64, base64, array)" .PD .Vb 1 \& Performs a regular expression substitution on the items in the queue. .Ve .Sp .Vb 14 \& Arguments: The first is a (base64\-encoded) regular expression that specifies \& the text to be replaced. \& * The second argument is the (base64\-encoded) string that will be used to \& replace the first occurrence of the regular expression within each queue \& item. Any backslash escapes in this string will be processed, including \& special character translation (e.g. "\en" to newline) and backreferences \& to groups within the match. \& * Optionally, an array of integers may be given as a third argument. \& This argument represents a range to which the substitution will be \& limited. This range is interpreted in the same way as the range argument \& in other Moosic methods. \& * If performing a replacement changes an item in the queue into the empty \& string, then it is removed from the queue. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBsub_all\fR (base64, base64)" 4 .IX Item "boolean sub_all (base64, base64)" .PD 0 .IP "boolean \fBsub_all\fR (base64, base64, array)" 4 .IX Item "boolean sub_all (base64, base64, array)" .PD .Vb 1 \& Performs a global regular expression substitution on the items in the queue. .Ve .Sp .Vb 14 \& Arguments: The first is a (base64\-encoded) regular expression that specifies \& the text to be replaced. \& * The second argument is the (base64\-encoded) string that will be used to \& replace all occurrences of the regular expression within each queue \& item. Any backslash escapes in this string will be processed, including \& special character translation (e.g. "\en" to newline) and backreferences \& to the substrings matched by individual groups in the pattern. \& * Optionally, an array of integers may be given as a third argument. \& This argument represents a range to which the substitution will be \& limited. This range is interpreted in the same way as the range argument \& in other Moosic methods. \& * If performing a replacement changes an item in the queue into the empty \& string, then it is removed from the queue. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBswap\fR (array, array)" 4 .IX Item "boolean swap (array, array)" .Vb 2 \& Swaps the items contained in one range with the items contained in the \& other range. .Ve .Sp .Vb 1 \& Return value: Nothing meaningful. .Ve .IP "array \fBsystem.listMethods\fR ()" 4 .IX Item "array system.listMethods ()" .Vb 1 \& Return an array of all available XML\-RPC methods on this server. .Ve .IP "string \fBsystem.methodHelp\fR (string)" 4 .IX Item "string system.methodHelp (string)" .Vb 1 \& Given the name of a method, return a help string. .Ve .IP "array \fBsystem.methodSignature\fR (string)" 4 .IX Item "array system.methodSignature (string)" .Vb 3 \& Given the name of a method, return an array of legal signatures. Each \& signature is an array of strings. The first item of each signature is \& the return type, and any others items are parameter types. .Ve .IP "array \fBsystem.multicall\fR (array)" 4 .IX Item "array system.multicall (array)" .Vb 6 \& Process an array of calls, and return an array of results. Calls \& should be structs of the form {'methodName': string, 'params': array}. \& Each result will either be a single\-item array containg the result \& value, or a struct of the form {'faultCode': int, 'faultString': \& string}. This is useful when you need to make lots of small calls \& without lots of round trips. .Ve .IP "boolean \fBtoggle_loop_mode\fR ()" 4 .IX Item "boolean toggle_loop_mode ()" .Vb 1 \& Turns loop mode on if it is off, and turns it off if it is on. .Ve .Sp .Vb 3 \& If loop mode is on, songs are returned to the end of the song queue after \& they finish playing. If loop mode is off, songs that have finished playing \& are not returned to the queue. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBtoggle_pause\fR ()" 4 .IX Item "boolean toggle_pause ()" .Vb 1 \& Pauses the current song if it is playing, and unpauses if it is paused. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "boolean \fBunpause\fR ()" 4 .IX Item "boolean unpause ()" .Vb 1 \& Unpauses the current song. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: Nothing meaningful. .Ve .IP "string \fBversion\fR ()" 4 .IX Item "string version ()" .Vb 1 \& Returns the Moosic server's version string. .Ve .Sp .Vb 2 \& Arguments: None. \& Return value: The version string for the Moosic server. .Ve .SH "Section 4: Writing a Moosic Client in Python" .IX Header "Section 4: Writing a Moosic Client in Python" As demonstrated in section 0, communicating with a Moosic server is very easy to do with Python. In this section, I'll merely elaborate on the details that were omitted from section 0 for the sake of brevity. .PP First of all, you should know that \fILocalMoosicProxy()\fR can be called with a filename argument to specify the location of the Moosic server's socket file. This is useful if moosicd was started with the \*(L"\-c\*(R" option. Refer to the moosic_factory.py module's documentation (moosic_factory.html). .PP Next, note that many of the Moosic server's methods accept or return special types of objects from the xmlrpclib module, namely Boolean and Binary. These object types serve the purpose of bridging the small mismatch between the data-types supported by XML-RPC and Python's intrinsic data\-types. Boolean objects present no unusual problem, since they evaluate to a correct truth value without any extra effort. However, you must take care when using the Moosic methods that accept or return Binary objects. Read the documentation for the xmlrpclib module for details on how to work with these objects. The basic technique boils down to wrapping up a string inside a Binary object before sending it to the server, and using the \*(L"data\*(R" attribute to access the string data within the Binary objects returned by the server. Regular strings can't be used because \s-1XML\-RPC\s0's normal string data-type can't handle multiple 8\-bit strings within a single request if the strings use different encodings. .SH "Section 5: Writing a Moosic Client in Another Language" .IX Header "Section 5: Writing a Moosic Client in Another Language" If you are not using Python to write your Moosic client, the first issue to deal with is deciding upon an XML-RPC implementation. For most popular programming languages, there are multiple XML-RPC implementations available. Most of the possibilities are listed at . Since XML-RPC is an open specification, you can create your own implementation if you don't like any of the ones that already exist. .PP Once you've got an XML-RPC library that you like, the big hurdle to overcome is to make that library send its \s-1RPC\s0 calls over a Unix socket instead of an \s-1IP\s0 socket. I was able to do this pretty easily with Python's xmlrpclib since it is designed to allow pluggable transport methods: all I had to do was subclass my own Transport type and plug it back into the original library's classes. (If your language and/or library of choice makes this task difficult, then you may begin to understand why some Python programmers are so smug.) .PP After you are capable of sending XML-RPC requests through a Unix socket, you can go ahead and start sending requests to a Moosic server. Refer to the end of section 2 for information on how to address a Moosic server. Refer to section 3 for a list of valid server requests. .PP If you can't be bothered to find or hack together an XML-RPC library that works with Unix sockets, then you can still talk to a Moosic server that is listening on an \s-1IP\s0 socket, but this is less than ideal since listening on an \s-1IP\s0 socket is not default behavior for most Moosic servers. .SH "Section 6: API Version History (ChangeLog)" .IX Header "Section 6: API Version History (ChangeLog)" .IP "* 1.8" 4 .IX Item "1.8" First implemented in moosicd 1.5.1. The following methods were added: .Sp .Vb 1 \& swap .Ve .IP "* 1.7" 4 .IX Item "1.7" First implemented by moosicd 1.5.0. The following methods were added: .Sp .Vb 1 \& skip, current_time .Ve .Sp The following methods had their behavior significantly changed: .Sp .Vb 1 \& previous, next .Ve .Sp Specifically, the \fIprevious()\fR method no longer activates queue advancement if it had been disabled before. This means that calling \fIprevious()\fR no longer necessarily causes a song to start playing. The \fInext()\fR method was changed to more closely parallel the behavior of \fIprevious()\fR: it takes a single optional integer argument to allow immediate advancement by more than one song at a time, and it has an effect even when no song is currently playing. The new \fIskip()\fR method implements the exact same behavior that was previously exhibited by \&\fInext()\fR. .Sp Last, and most importantly, the name of the default socket file for communicating with the server via unix sockets was changed from \&\f(CW$CONFIG_DIR\fR/socket\-$HOSTNAME to \f(CW$CONFIG_DIR\fR/socket. If you are a Python programmer and you use the updated moosic_factory.py file from Moosic version 1.5.0, then you don't have to make any changes. Otherwise, you must change your client's code to connect to the file named \*(L"socket\*(R" instead of the file named \&\*(L"socket\-something.example.com\*(R". If your client only talks to the Moosic server through \s-1TCP/IP\s0, then you don't have to make any changes, of course. .IP "* 1.6" 4 .IX Item "1.6" First implemented by moosicd 1.4.10. The following methods were added: .Sp .Vb 1 \& getconfig .Ve .IP "* 1.5" 4 .IX Item "1.5" First implemented by moosicd 1.4.6. The following methods were added: .Sp .Vb 1 \& sub, sub_all, stop .Ve .IP "* 1.4" 4 .IX Item "1.4" First implemented by moosicd 1.4.5. The following methods were added: .Sp .Vb 1 \& previous .Ve .IP "* 1.3" 4 .IX Item "1.3" First implemented by moosicd 1.4.4. The following methods were added: .Sp .Vb 1 \& replace, replace_range, last_queue_update .Ve .IP "* 1.2" 4 .IX Item "1.2" First implemented by moosicd 1.4.2. The following methods were added: .Sp .Vb 1 \& cut_list, crop_list .Ve .IP "* 1.1" 4 .IX Item "1.1" First implemented by moosicd 1.4.1. The following methods were added: .Sp .Vb 1 \& is_looping, set_loop_mode, toggle_loop_mode .Ve .IP "* 1.0" 4 .IX Item "1.0" First implemented by moosicd 1.4.0. The following methods were included: .Sp .Vb 7 \& api_version, append, clear, crop, current, cut, die, filter, \& get_history_limit, halt_queue, haltqueue, history, indexed_list, insert, \& is_paused, is_queue_running, length, list, move, move_list, next, no_op, \& pause, prepend, putback, queue_length, reconfigure, remove, reverse, \& run_queue, runqueue, set_history_limit, showconfig, shuffle, sort, \& system.listMethods, system.methodHelp, system.methodSignature, \& system.multicall, toggle_pause, unpause, version .Ve moosic-1.5.6/doc/Moosic_API.sect6.pod0000644000175000017500000000532010066632727017755 0ustar danieldaniel00000000000000=head1 Section 6: API Version History (ChangeLog) =over =item * S<1.8> First implemented in moosicd 1.5.1. The following methods were added: swap =item * S<1.7> First implemented by moosicd 1.5.0. The following methods were added: skip, current_time The following methods had their behavior significantly changed: previous, next Specifically, the previous() method no longer activates queue advancement if it had been disabled before. This means that calling previous() no longer necessarily causes a song to start playing. The next() method was changed to more closely parallel the behavior of previous(): it takes a single optional integer argument to allow immediate advancement by more than one song at a time, and it has an effect even when no song is currently playing. The new skip() method implements the exact same behavior that was previously exhibited by next(). Last, and most importantly, the name of the default socket file for communicating with the server via unix sockets was changed from $CONFIG_DIR/socket-$HOSTNAME to $CONFIG_DIR/socket. If you are a Python programmer and you use the updated moosic_factory.py file from Moosic version 1.5.0, then you don't have to make any changes. Otherwise, you must change your client's code to connect to the file named "socket" instead of the file named "socket-something.example.com". If your client only talks to the Moosic server through TCP/IP, then you don't have to make any changes, of course. =item * S<1.6> First implemented by moosicd 1.4.10. The following methods were added: getconfig =item * S<1.5> First implemented by moosicd 1.4.6. The following methods were added: sub, sub_all, stop =item * S<1.4> First implemented by moosicd 1.4.5. The following methods were added: previous =item * S<1.3> First implemented by moosicd 1.4.4. The following methods were added: replace, replace_range, last_queue_update =item * S<1.2> First implemented by moosicd 1.4.2. The following methods were added: cut_list, crop_list =item * S<1.1> First implemented by moosicd 1.4.1. The following methods were added: is_looping, set_loop_mode, toggle_loop_mode =item * S<1.0> First implemented by moosicd 1.4.0. The following methods were included: api_version, append, clear, crop, current, cut, die, filter, get_history_limit, halt_queue, haltqueue, history, indexed_list, insert, is_paused, is_queue_running, length, list, move, move_list, next, no_op, pause, prepend, putback, queue_length, reconfigure, remove, reverse, run_queue, runqueue, set_history_limit, showconfig, shuffle, sort, system.listMethods, system.methodHelp, system.methodSignature, system.multicall, toggle_pause, unpause, version =back moosic-1.5.6/doc/moosicd.html0000644000175000017500000002510310037722705016622 0ustar danieldaniel00000000000000 moosicd - the server for the Moosic jukebox system.


NAME

moosicd - the server for the Moosic jukebox system.


SYNOPSIS

moosicd --help|-h|--version|-v

moosicd [--history-size|-s size] [--config|-c directory] [--quiet|-q|--debug|-d] [-S|--stdout] [-t|--tcp port] [-T|--tcp-also port] [-l|--local-only]


DESCRIPTION

moosicd is the server for the Moosic jukebox system. It sits around, waiting to respond to commands given by a Moosic client (such as the moosic manpage(1)). It also maintains a queue of items to be played, and if this queue is not empty, it pops off the first item from the head of the queue and executes a user-configurable command on that item. When this command terminates, moosicd goes on to the next item in its queue, assuming that the queue is not empty.

moosicd is not meant to be used as a system-wide daemon that serves all users on a system. Rather, every user that wishes to use Moosic should start a separate instance of moosicd, and one user cannot communicate with the Moosic server of another user without taking special measures (i.e. using the -c or -t options to moosic).


OPTIONS

moosicd is designed so that you normally don't need to use any of these options.

-h, --help
Prints help text and exits.

-v, --version
Prints version information and exits.

-s size, --history-size size
moosicd remembers the file names of previous songs that it played. This option sets the maximum size of this history list. The default value is 50.

-f, --foreground
By default, moosicd detaches itself from the current terminal and puts itself in the background (i.e. it becomes a daemon). This option disables such behavior.

-S, --stdout
By default, moosicd logs almost all of its printed output to a file. When this option is used, the output is instead printed to the standard output device. This also prevents the program from putting itself in the background and detaching from the current terminal.

-q, --quiet
This option suppresses almost all printed output from moosicd. Note that, by default, moosicd prints this output to a file, not the standard output device.

-d, --debug
This option causes moosicd to print lots and lots of messages about what it's doing. These messages are usually quite superfluous and bothersome. Note that unless the -S option is used these messages will appear in the log file instead of the standard output device.

-c directory, --config directory
Specifies the directory where moosicd should keep the various files that it uses. The default directory is ~/.moosic/. This option is useful only in extraordinary circumstances. If this option is used, any Moosic clients that wish to communicate with moosicd must be told to use the specified directory instead of the default.

-t port, --tcp port
This option directs the server to listen to the given TCP port number for client requests instead of using the normal communication method. Use of this option without the -l option is highly discouraged unless you know what you are doing because there is no guarantee that moosicd is secure against malicious input from a remote location. Note that an instance of moosicd which is started with this option will not accept requests from a client that is using the normal communication method.

-T port, --tcp-also port
This option directs the server to listen to the given TCP port number for client requests in addition to using the normal communication method. Use of this option without the -l option is highly discouraged unless you know what you are doing because there is no guarantee that moosicd is secure against malicious input from a remote location.

-l, --local-only
This directs the server to only listen for TCP connections that originate from the local computer, refusing connections from remote hosts. This only has an effect when --tcp or --tcp-also is used.


CONFIGURATION

moosicd figures out how to play items in its queue by consulting its configuration file, which associates string patterns (in the form of Perl-compatible regular expressions) with commands.

The format of this file is as follows: Every pair of lines forms a unit. The first line in a pair is a regular expression that will be matched against items in the queue. The second line in a pair is the command that will be used to play any items that match the regular expression. The name of the item to be played will be appended to the end of this command, unless the command line includes a special substitution string.

The simplest kind of substitution string is ``$item''. Every occurrence of ``$item'' in the command will be replaced with the name of the song to be played. The other kind of substitution is called ``matched group substitution'', and is used to refer to specific parts of the name of the song to be played.

The command will not be interpreted by a shell, so don't bother trying to use shell variables or globbing or I/O redirection, and be mindful of how you use quotes and parentheses. If you need any of these fancy features, wrap up the command in a real shell script (and remember to use an ``exec'' statement in your shell script to invoke the program that does the actual song playing, otherwise Moosic won't be able to do things like stop or pause the song).

Blank lines and lines starting with a '#' character are ignored. Regular expressions specified earlier in this file take precedence over those specified later.


FILES

moosicd makes use of several files, all of which are found in the .moosic/ directory in the home directory of the user who invoked the program (unless the -c or --config option is used).

config
This is the configuration file that moosicd uses to associate file-types to player commands, as explained above.

server_log
moosicd logs short notices of its activities to this file unless the -S option is used. It usually contains nothing more than a history of what songs have been played.

player_log
This file contains the output of the player commands which are spawned by moosicd.

socket
This is a socket file which is (normally) used to allow Moosic clients to contact the Moosic server. If moosicd isn't shut down properly, this file will need to be removed by hand. You should leave this file alone under other circumstances.


SEE ALSO

the moosic manpage(1), the standard command-line Moosic client.

The chapter entitled ``Regular Expression Syntax'' http://www.python.org/doc/current/lib/re-syntax.html from the section dealing with the re module in the Python Library Reference, for details on the syntax of a regular expression.


AUTHOR

Daniel Pearson <daniel@nanoo.org>

moosic-1.5.6/doc/moosic.10000644000175000017500000011255510625507222015660 0ustar danieldaniel00000000000000.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.14 .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "MOOSIC 1" .TH MOOSIC 1 "2007-05-25" "Moosic 1.5.2" "" .SH "NAME" moosic \- a command\-line client for the Moosic jukebox system. .SH "SYNOPSIS" .IX Header "SYNOPSIS" \&\fBmoosic\fR [\fIoptions\fR] \fIcommand\fR [\fIoptions\fR] [\fIcommand arguments\fR] .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \fBmoosic\fR program is the command-line interface to the Moosic jukebox system. It communicates with moosicd(1), the Moosic server, querying the server for information and telling the server what to do. \fBmoosic\fR will not be able to do very much unless \fBmoosicd\fR is running. When \fBmoosicd\fR isn't already running, \fBmoosic\fR will automatically start it for you, unless you specifically request otherwise (with the \-\-no\-startserver option). .SH "USAGE" .IX Header "USAGE" \&\fBmoosic\fR works by sending a command to the Moosic server and returning the response, if any. The first non-option argument given to \fBmoosic\fR is the name of the command to be performed. This command name is case\-insensitive, and all non-alphanumeric characters in it are ignored. You can use the \*(L"help\*(R" command to quickly and easily view the names of all the available commands and to get a brief description of individual commands. You can also use \f(CW\*(C`moosic \&\-\-showcommands\*(C'\fR to display the short descriptions of all the commands at once. The \*(L"\s-1COMMANDS\s0\*(R" section below lists the full details of each command. There are very many commands, so you should start by just learning a few commonly used commands, and only learning others as you feel the need. I recommend starting with the following short command vocabulary: add, list, stop, play, and shuffle. .PP For example, \f(CW\*(C`moosic add foo.mp3\*(C'\fR adds the file \fIfoo.mp3\fR (in the current directory) to the end of the song queue and returns you immediately back to your shell prompt without printing any output (unless an error occurs). Compare with \&\f(CW\*(C`moosic list\*(C'\fR, which will list the contents of the song queue. Note that if the song queue is empty, \f(CW\*(C`moosic list\*(C'\fR will not display anything. .SH "OPTIONS" .IX Header "OPTIONS" Most of the options for \fBmoosic\fR are only relevant if they are used with one of the commands that take a \fIfilelist\fR argument. See \*(L"\s-1COMMANDS\s0\*(R" for the definition of a \fIfilelist\fR. The only shuffling options that don't mutually exclude each other are \fB\-d\fR and \fB\-a\fR. Shuffling options that are named later on the command line take precedence over ones that occur earlier. All options must be named immediately before the \fIcommand\fR given to \fBmoosic\fR or immediately after the \fIcommand\fR; options placed within the list of the command's arguments will not be interpreted as options. .IP "\fB\-g\fR, \fB\-\-shuffle\-global\fR" 4 .IX Item "-g, --shuffle-global" This option causes \fBmoosic\fR to shuffle the entire \fIfilelist\fR after directory expansion has taken place, before sending the \fIfilelist\fR to the Moosic server. This is the default behavior. This option is only meaningful if used in conjunction with a command that accepts a \fIfilelist\fR. .IP "\fB\-d\fR, \fB\-\-shuffle\-dir\fR" 4 .IX Item "-d, --shuffle-dir" This option causes \fBmoosic\fR to shuffle the results of expanding the directories named in the \fIfilelist\fR. This option is only meaningful if used in conjunction with a command that accepts a \fIfilelist\fR. .IP "\fB\-a\fR, \fB\-\-shuffle\-args\fR" 4 .IX Item "-a, --shuffle-args" This option causes \fBmoosic\fR to shuffle the actual command line arguments that comprise the \fIfilelist\fR. This option is only meaningful if used in conjunction with a command that accepts a \fIfilelist\fR. .IP "\fB\-o\fR, \fB\-\-inorder\fR" 4 .IX Item "-o, --inorder" When this option is used, \fBmoosic\fR doesn't shuffle the \fIfilelist\fR named on the command line at all. Rather, the order specified on the command line is preserved. This option is only meaningful if used in conjunction with a command that accepts a \fIfilelist\fR. .IP "\fB\-s\fR, \fB\-\-sort\fR" 4 .IX Item "-s, --sort" When this option is used, \fBmoosic\fR sorts the \fIfilelist\fR lexicographically after it has been expanded (through directory recursion or auto-finding or the like). The order specified on the command line is ignored. This option is only meaningful if used in conjunction with a command that accepts a \fIfilelist\fR. .IP "\fB\-r\fR, \fB\-\-no\-recurse\fR" 4 .IX Item "-r, --no-recurse" Using this option prevents \fBmoosic\fR from replacing directories named in the \&\fIfilelist\fR with a recursive traversal of their contents. .IP "\fB\-n\fR, \fB\-\-no\-file\-munge\fR" 4 .IX Item "-n, --no-file-munge" Using this option prevents \fBmoosic\fR from modifying the names in the expanded \&\fIfilelist\fR. Normally, \fBmoosic\fR converts relative filenames into absolute filenames before sending the names to \fBmoosicd\fR, but this is generally not desirable behavior if you want to insert items that aren't local files into the queue (such as URLs). This option is only meaningful if used in conjunction with a command that accepts a \fIfilelist\fR. .IP "\fB\-i\fR, \fB\-\-ignore\-case\fR" 4 .IX Item "-i, --ignore-case" Treats any given regular expressions as if they were case\-insensitive. This option is only meaningful if used in conjunction with a command that accepts one or more regular expressions as arguments. This option is syntactic sugar, since the regular expressions supported by Moosic can also be made case-insensitive by including \*(L"(?i)\*(R" within the regular expression. .IP "\fB\-f\fR, \fB\-\-auto\-find\fR" 4 .IX Item "-f, --auto-find" This option causes each string in the \fIfilelist\fR with the results of performing a \*(L"fuzzy\*(R" search for music files. \*(L"Fuzzy\*(R" matching is done by simplifying all the candidate filenames (by lowering the case and removing all non-alphanumeric characters except slashes) and then testing to see if the search string (which has been similarly simplified) is contained in any of the filenames. The list of candidate filenames is obtained by recursively traversing the file hierarchy rooted at the directory specified by the \fB\-\-music\-dir\fR option (which has a default value of \fI~/music/\fR). .Sp For example, if you use \f(CW\*(C`moosic \-f add severedgoddess\*(C'\fR, and the file \&\fI~/music/Meat_Puppets/Severed_Goddess_Hand.mp3\fR exists, then this file will be included in the list of files to be added to the queue. Similarly, if you use \&\f(CW\*(C`moosic \-f pre nesad\*(C'\fR, and the directory \fI~/music/J/Jane's Addiction/\fR exists, then all the files in this directory (and its subdirectories) will be included in the list of files to be prepended to the queue. .Sp This option is only meaningful if used in conjunction with a command that accepts a \fIfilelist\fR. Beware that using this option can cause \fBmoosic\fR to take a long time to complete if the directory tree being searched contains a very large number of files. .IP "\fB\-F\fR, \fB\-\-auto\-grep\fR" 4 .IX Item "-F, --auto-grep" This option enables behavior very much like that of the \fB\-\-auto\-find\fR option, except that regular expression searching is used instead of the \*(L"fuzzy\*(R" search scheme. Specifically, each string in the \fIfilelist\fR is treated as a regular expression, and is replaced with all the filenames that match the expression. As with \fB\-\-auto\-find\fR, the filenames that are eligible for matching are obtained by traversing the directory named with the \fB\-\-music\-dir\fR option (defaulting to \fI~/music/\fR if \fB\-\-music\-dir\fR is not used). Essentially, \&\f(CW\*(C`moosic \-F prepend something\*(C'\fR is semantically equivalent to \&\f(CW\*(C`moosic prepend `find ~/music/ | grep something`\*(C'\fR, but is syntactically a lot sweeter. .Sp This option is only meaningful if used in conjunction with a command that accepts a \fIfilelist\fR. Beware that using this option can cause \fBmoosic\fR to take a long time to complete if the directory tree being searched contains a very large number of files. .IP "\fB\-m\fR \fIdirectory\fR, \fB\-\-music\-dir\fR \fIdirectory\fR" 4 .IX Item "-m directory, --music-dir directory" This option controls which directory is used for searching when the \*(L"auto\-find\*(R" or \*(L"auto\-grep\*(R" feature is enabled. These automatic searches are limited to the file hierarchy rooted at the directory specified by this option. When this option is not used, the \fI~/music/\fR directory is used as a default. This option is only meaningful if either \fB\-\-auto\-find\fR or \fB\-\-auto\-grep\fR is used. .IP "\fB\-S\fR, \fB\-\-showcommands\fR" 4 .IX Item "-S, --showcommands" Prints a list of the commands that may be used with \fBmoosic\fR and then exits. Note that this output is quite copious, so you will probably want to pipe it to a text pager, such as \fBless\fR. .IP "\fB\-h\fR, \fB\-\-help\fR" 4 .IX Item "-h, --help" Prints a short help message that explains the command line options and then exits. .IP "\fB\-v\fR, \fB\-\-version\fR" 4 .IX Item "-v, --version" Prints version information and then exits. .IP "\fB\-c\fR \fIdirectory\fR, \fB\-\-config\-dir\fR \fIdirectory\fR" 4 .IX Item "-c directory, --config-dir directory" This option is not needed under normal circumstances. It should only be used if you want \fBmoosic\fR to communicate with an instance of \fBmoosicd\fR which was invoked with the \fB\-c\fR/\fB\-\-config\fR option. Using this option tells \fBmoosic\fR to search the specified directory for the files which are usually found in \&\fI~/.moosic/\fR. .IP "\fB\-t\fR \fIhost\fR:\fIport\fR, \fB\-\-tcp\fR \fIhost\fR:\fIport\fR" 4 .IX Item "-t host:port, --tcp host:port" This option tells \fBmoosic\fR to communicate with a Moosic server that is listening to the specified \s-1TCP/IP\s0 port on the specified host. Running a Moosic server that accepts requests via \s-1TCP/IP\s0 is not recommended because it is a security risk. .IP "\fB\-N\fR, \fB\-\-no\-startserver\fR" 4 .IX Item "-N, --no-startserver" This option prevents \fBmoosic\fR from trying to automatically start \fBmoosicd\fR if it can't contact a Moosic server. .IP "\fB\-U\fR, \fB\-\-allow\-unplayable\fR" 4 .IX Item "-U, --allow-unplayable" This option allows songs that the server doesn't know how to play to be added into the song queue. .IP "\fB\-C\fR, \fB\-\-current\-in\-list\fR" 4 .IX Item "-C, --current-in-list" This option causes the currently playing song to be printed at the top of the output of the \*(L"list\*(R" and \*(L"plainlist\*(R" commands. It has no effect if an argument is given to these commands or if used with other commands. .SH "COMMANDS" .IX Header "COMMANDS" Any of these commands may be specified with any mixture of upper-case and lower-case letters, and non-alphabetic characters (such as '\-') may be omitted. .PP Many of these commands accept a \fIrange\fR argument. A \fIrange\fR is a pair of colon-separated numbers. Such a \fIrange\fR addresses all items whose index in the song queue is both greater than or equal to the first number and less than the second number. For example, \*(L"3:7\*(R" addresses items 3, 4, 5, and 6. If the first number in the pair is omitted, then the \fIrange\fR starts at the beginning of the song queue. If the second number in the pair is omitted, then the \fIrange\fR extends to include the last item in the song queue. A \fIrange\fR can also be a single number (with no colon), in which case it addresses the single item whose index is that of the given number. Negative numbers may be used to index items from the end of the list instead of the beginning. Thus, \-1 refers to the last item in the song queue, \-2 refers to the second-to-last item, etc. .PP Beware that a negative number that immediately follows a moosic \fIcommand\fR is liable to be incorrectly interpreted as an option, so option processing should be explicitly terminated with an argument of \*(L"\-\-\*(R" between the \fIcommand\fR and the number. This is illustrated by the following example, which removes the last item in the queue: \f(CW\*(C`moosic del \-\- \-1\*(C'\fR .PP Alternatively (and perhaps more conveniently), you can prevent negative numbers from being interpreted as options by preceding the range with a single character that can't be mistaken for a number or an option (i.e. any character that isn't a digit or a dash). Example: \f(CW\*(C`moosic list /\-15:\-9\*(C'\fR. You can also place such a character at the end of the range if you think it makes it look prettier. Example: \f(CW\*(C`moosic list /\-15:\-9/\*(C'\fR. The bracketing characters surrounding a range need not be the same: \f(CW\*(C`moosic shuffle '[\-13:8]'\*(C'\fR. Notice how the preceding example surrounded the range in quotes to prevent the shell from treating the \&\*(L"[\*(R" and \*(L"]\*(R" characters specially (since shells have a habit of doing things like that). .Sh "Querying for information" .IX Subsection "Querying for information" These commands print useful bits of information to standard output. .IP "\fBhelp\fR [\fIcommand\fR ...]" 4 .IX Item "help [command ...]" Prints a brief description of the moosic commands named as arguments. If no arguments are given, a list of all the available moosic commands is printed. .IP "\fBcurrent\fR" 4 .IX Item "current" Print the name of the song that is currently playing. .IP "\fBcurr\fR" 4 .IX Item "curr" An alias for \*(L"current\*(R". .IP "\fBcurrent-time\fR [\fIformat\fR]" 4 .IX Item "current-time [format]" Print the amount of time that the current song has been playing. By default, this time is printed in a format of \*(L"hours:minutes:seconds\*(R", but if a different format is desired, a string argument can be given to specify it. The format should be a string that is appropriate for passing to the \fIstrftime\fR\|(3) function. .IP "\fBlist\fR [\fIrange\fR]" 4 .IX Item "list [range]" Print the list of items in the current song queue. A whole number is printed before each item in the list, indicating its position in the queue. If a range is specified, only the items that fall within that range are listed. Remember that the song queue does not contain the currently playing song. .IP "\fBplainlist\fR [\fIrange\fR]" 4 .IX Item "plainlist [range]" Print the current song queue without numbering each line. If a range is specified, only the items that fall within that range are listed. This output is suitable for saving to a file which can be reloaded by the \*(L"pl\-append\*(R", \&\*(L"pl\-prepend\*(R", \*(L"pl\-insert\*(R", and \*(L"pl\-mixin\*(R" commands. .IP "\fBhistory\fR [\fInumber\fR]" 4 .IX Item "history [number]" Print a list of items that were recently played. The times mentioned in the output of this command represents the time that a song finished playing. If a number is specified, then no more than that number of entries will be printed. If a number is not specified, then the entire history is printed. Note that \&\fBmoosicd\fR limits the number of items stored in its history list. .IP "\fBhist\fR [\fInumber\fR]" 4 .IX Item "hist [number]" An alias for \*(L"history\*(R". .IP "\fBstate\fR" 4 .IX Item "state" Print the current state of the music daemon. .IP "\fBstatus\fR" 4 .IX Item "status" An alias for \*(L"state\*(R". .IP "\fBlength\fR" 4 .IX Item "length" Print the number of items in the queue. .IP "\fBlen\fR" 4 .IX Item "len" An alias for \*(L"length\*(R". .IP "\fBispaused\fR" 4 .IX Item "ispaused" Show whether the current song is paused or not. If the song is paused, \*(L"True\*(R" is printed and \fBmoosic\fR returns normally. If the song is not paused, \*(L"False\*(R" is printed and \fBmoosic\fR returns with a non-zero exit status (which happens to be 2 for no particular reason). .IP "\fBislooping\fR" 4 .IX Item "islooping" Show whether the server is in loop mode. If the server is in loop mode, \*(L"True\*(R" is printed and \fBmoosic\fR returns normally. If not, \*(L"False\*(R" is printed and \&\fBmoosic\fR returns with a non-zero exit status (which happens to be 2 for no particular reason). .IP "\fBisadvancing\fR" 4 .IX Item "isadvancing" Show whether the server is advancing through the song queue. If the server is advancing, \*(L"True\*(R" is printed and \fBmoosic\fR returns normally. If not, \*(L"False\*(R" is printed and \fBmoosic\fR returns with a non-zero exit status (which happens to be 2 for no particular reason). .IP "\fBversion\fR" 4 .IX Item "version" Print version information for both the client and the server, and then exit. .Sh "Adding to the song queue" .IX Subsection "Adding to the song queue" These commands will add to the queue of items to be played. Many of these commands accept a \fIfilelist\fR argument. A \fIfilelist\fR is a list of one or more files or directories. Any directories named in the list will be replaced by a list of files produced by recursively traversing the contents of the directory (unless the \fB\-\-no\-file\-munge\fR option or \fB\-\-no\-recurse\fR option is being used). Depending on the shuffling options specified when invoking \fBmoosic\fR, the list will be shuffled before being added to the Moosic server's queue. .IP "\fBappend\fR \fIfilelist\fR" 4 .IX Item "append filelist" Add the files to be played to the end of the song queue. .IP "\fBadd\fR \fIfilelist\fR" 4 .IX Item "add filelist" An alias for \*(L"append\*(R". .IP "\fBpl-append\fR \fIplaylist-file\fR ..." 4 .IX Item "pl-append playlist-file ..." Add the items listed in the given playlist files to the end of the song queue. If \*(L"\-\*(R" (a single dash) is given as the name of a playlist file, data will be read from from standard input instead of trying to read from a file named \*(L"\-\*(R". .IP "\fBpl-add\fR \fIplaylist-file\fR ..." 4 .IX Item "pl-add playlist-file ..." An alias for \*(L"pl\-append\*(R". .IP "\fBprepend\fR \fIfilelist\fR" 4 .IX Item "prepend filelist" Add the files to be played to the beginning of the song queue. .IP "\fBpre\fR \fIfilelist\fR" 4 .IX Item "pre filelist" An alias for \*(L"prepend\*(R". .IP "\fBpl-prepend\fR \fIplaylist-file\fR ..." 4 .IX Item "pl-prepend playlist-file ..." Add the items listed in the given playlist files to the beginning of the song queue. If \*(L"\-\*(R" (a single dash) is given as the name of a playlist file, data will be read from from standard input instead of trying to read from a file named \*(L"\-\*(R". .IP "\fBmixin\fR \fIfilelist\fR" 4 .IX Item "mixin filelist" Add the files to the song queue and reshuffle the entire song queue. .IP "\fBpl-mixin\fR \fIplaylist-file\fR ..." 4 .IX Item "pl-mixin playlist-file ..." Add the items listed in the given playlist files to the song queue and reshuffle the entire song queue. If \*(L"\-\*(R" (a single dash) is given as the name of a playlist file, data will be read from from standard input instead of trying to read from a file named \*(L"\-\*(R". .IP "\fBreplace\fR \fIfilelist\fR" 4 .IX Item "replace filelist" Replace the current contents of the song queue with the songs contained in the filelist. .IP "\fBpl-replace\fR \fIplaylist-file\fR ..." 4 .IX Item "pl-replace playlist-file ..." Replace the current contents of the song queue with the songs named in the given playlists. .IP "\fBinsert\fR \fIfilelist\fR \fIindex\fR" 4 .IX Item "insert filelist index" Insert the given items at a given point in the song queue. The items are inserted such that they will precede the item that previously occupied the specified index. .IP "\fBpl-insert\fR \fIplaylist-file\fR ... \fIindex\fR" 4 .IX Item "pl-insert playlist-file ... index" Insert the items specified in the given playlist files at a specified point in the song queue. If \*(L"\-\*(R" (a single dash) is given as the name of a playlist file, data will be read from from standard input instead of trying to read from a file named \*(L"\-\*(R". .IP "\fBputback\fR" 4 .IX Item "putback" Reinsert the current song at the start of the song queue. .IP "\fBstagger-add\fR \fIfilelist\fR" 4 .IX Item "stagger-add filelist" Adds the file list to the end of the song queue, but only after rearranging it into a \*(L"staggered\*(R" order. This staggered order is very similar the order created by the \fBstagger\fR command (described below). Each element of the file list (before replacing directories with their contents) specifies a category into which the expanded file list will be divided. The staggered order of the list being added is formed by taking the first item from each category in turn until all the categories are empty. This may be a bit difficult to understand without an example, so here is a typical case: .Sp Initially, the queue contains a few items. .Sp .Vb 3 \& [0] /music/a.ogg \& [1] /music/b.mp3 \& [2] /music/c.mid .Ve .Sp Additionally, there are two directories that each contain a few files: .Sp .Vb 3 \& $ ls /music/X/ /music/Y/ \& X: \& 1.ogg 2.ogg 3.ogg .Ve .Sp .Vb 2 \& Y: \& 1.ogg 2.ogg 3.ogg 4.ogg .Ve .Sp After executing \f(CW\*(C`moosic \-o stagger\-add /music/Y /music/X\*(C'\fR, the queue now contains: .Sp .Vb 10 \& [0] /music/a.ogg \& [1] /music/b.mp3 \& [2] /music/c.mid \& [3] /music/Y/1.ogg \& [4] /music/X/1.ogg \& [5] /music/Y/2.ogg \& [6] /music/X/2.ogg \& [7] /music/Y/3.ogg \& [8] /music/X/3.ogg \& [9] /music/Y/4.ogg .Ve .IP "\fBstagger-merge\fR \fIfilelist\fR" 4 .IX Item "stagger-merge filelist" Adds the given file list to the queue in an interleaved fashion. More specifically, the new song queue will consist of a list that alternates between the items from the given file list and the items from the existing song queu. For example, if the queue initially contains: .Sp .Vb 3 \& [0] /music/a.ogg \& [1] /music/b.mp3 \& [2] /music/c.mid .Ve .Sp And the \fI/music/Y/\fR directory contains: .Sp .Vb 1 \& 1.ogg 2.ogg 3.ogg 4.ogg .Ve .Sp Then, after executing \f(CW\*(C`moosic \-o stagger\-merge /music/Y\*(C'\fR, the queue will contain: .Sp .Vb 7 \& [0] /music/Y/1.ogg \& [1] /music/a.ogg \& [2] /music/Y/2.ogg \& [3] /music/b.mp3 \& [4] /music/Y/3.ogg \& [5] /music/c.mid \& [6] /music/Y/4.ogg .Ve .IP "\fBinterval-add\fR \fIinterval\fR \fIfilelist\fR" 4 .IX Item "interval-add interval filelist" Inserts the given songs into the current song queue with a regular frequency that is specified with the given \fIinterval\fR argument (which must be an integer). .Sp For example, if the queue initially contains: .Sp .Vb 7 \& [0] /music/a.mod \& [1] /music/b.mod \& [2] /music/c.mod \& [3] /music/d.mod \& [4] /music/e.mod \& [5] /music/f.mod \& [6] /music/g.mod .Ve .Sp And the \fI/music/Z\fR directory contains: .Sp .Vb 1 \& aleph.wav bet.wav gimmel.wav .Ve .Sp Then, after executing \f(CW\*(C`moosic \-o interval\-add 3 /music/Z\*(C'\fR, the queue will contain: .Sp .Vb 10 \& [0] aleph.wav \& [1] /music/a.mod \& [2] /music/b.mod \& [3] bet.wav \& [4] /music/c.mod \& [5] /music/d.mod \& [6] gimmel.wav \& [7] /music/e.mod \& [8] /music/f.mod \& [9] /music/g.mod .Ve .Sh "Removing from the song queue" .IX Subsection "Removing from the song queue" These commands will remove from the queue of items to be played. .IP "\fBcut\fR \fIrange\fR" 4 .IX Item "cut range" Removes all song queue items that fall within the given range. .IP "\fBdel\fR \fIrange\fR" 4 .IX Item "del range" An alias for \*(L"cut\*(R". .IP "\fBcrop\fR \fIrange\fR" 4 .IX Item "crop range" Removes all song queue items that do not fall within the given range. .IP "\fBremove\fR \fIregex\fR ..." 4 .IX Item "remove regex ..." Remove all song queue items that match the given regular expression. If multiple regular expressions are given, any song that matches any one of the expressions will be removed. .IP "\fBfilter\fR \fIregex\fR ..." 4 .IX Item "filter regex ..." Remove all song queue items that do not match the given regular expression. If multiple regular expressions are given, only those songs that match all the regular expressions will remain afterward. .IP "\fBclear\fR" 4 .IX Item "clear" Clear the song queue. .IP "\fBwipe\fR" 4 .IX Item "wipe" Clear the song queue and stop the current song. .Sh "Rearranging the song queue" .IX Subsection "Rearranging the song queue" These commands let you change the order of the items in the queue. .IP "\fBmove\fR \fIrange\fR \fIindex\fR" 4 .IX Item "move range index" Moves all items in the given range to a new position in the song queue. If you want to move items to the end of the queue, use \f(CW`moosic length`\fR as the final argument. For example, to move the first 10 songs to the end of the queue, use the following command: \f(CW\*(C`moosic move 0:10 `moosic length`\*(C'\fR .IP "\fBmove-pattern\fR \fIregex\fR \fIindex\fR" 4 .IX Item "move-pattern regex index" Moves all items that match the given regular expression to a new position in the song queue. .IP "\fBswap\fR \fIrange\fR \fIrange\fR" 4 .IX Item "swap range range" Causes the songs contained within the two specified ranges to trade places. .IP "\fBreshuffle\fR [\fIrange\fR]" 4 .IX Item "reshuffle [range]" Reshuffle the song queue. If a range is specified, only items that fall within that range will be shuffled. .IP "\fBshuffle\fR [\fIrange\fR]" 4 .IX Item "shuffle [range]" An alias for \*(L"reshuffle\*(R". .IP "\fBsort\fR [\fIrange\fR]" 4 .IX Item "sort [range]" Rearrange the song queue in sorted order. If a range is specified, only items that fall within that range will be sorted. .IP "\fBreverse\fR [\fIrange\fR]" 4 .IX Item "reverse [range]" Reverse the order of the song queue. If a range is specified, only items that fall within that range will be reversed. .IP "\fBpartial-sort\fR \fIregex\fR ..." 4 .IX Item "partial-sort regex ..." For each specified regular expression, the items in the song queue that match that expression are removed from the queue and gathered into their own list. All of these lists (plus the list of items that did not match any regular expression) are then stitched back together through simple concatenation. Finally, this unified list replaces the contents of the song queue. .Sp The items that match a particular regular expression will remain in the same order with respect to each other. Each group of matched items will appear in the reordered song queue in the order that the corresponding regular expressions were specified on the command line. .IP "\fBstagger\fR \fIregex\fR ..." 4 .IX Item "stagger regex ..." For each specified regular expression, the items in the song queue that match that expression are removed from the queue and gathered into their own list. All of these lists are then merged together in a staggered fashion. All the leftover items (i.e. the ones that weren't matched by any regex on the command line) are appended to this unified list, which then replaces the contents of the song queue. .Sp For example, if you use \f(CW\*(C`moosic stagger red blue green\*(C'\fR and the queue originally contains only names that either contain the string \*(L"red\*(R" or \*(L"blue\*(R" or \&\*(L"green\*(R", then the members of the reordered queue will alternate between \*(L"red\*(R" items, \*(L"blue\*(R" items, and \*(L"green\*(R" items. If the queue does contain items that are neither \*(L"red\*(R" nor \*(L"green\*(R" nor \*(L"blue\*(R", then these will be collected and placed at the end of the queue, after all the \*(L"red\*(R", \*(L"green\*(R", and \*(L"blue\*(R" items. .IP "\fBsub\fR \fIpattern\fR \fIreplacement\fR [\fIrange\fR]" 4 .IX Item "sub pattern replacement [range]" Perform a regular expression substitution on all items in the song queue. More precisely, this searches each queue item for the regular expression specified by the first argument, and replaces it with the text specified by the second argument. Any backslash escapes in the replacement text will be processed, including special character translation (e.g. \*(L"\en\*(R" to newline) and backreferences to groups within the match. If a range is given, then the substitution will only be applied to the items that fall within the range, instead of all items. Only the first matching occurrence of the pattern is replaced in each item. .IP "\fBsuball\fR \fIpattern\fR \fIreplacement\fR [\fIrange\fR]" 4 .IX Item "suball pattern replacement [range]" This is identical to the \*(L"sub\*(R" command, except that all occurrences of the pattern within each queue item are replaced instead of just the first occurrence. .Sh "General management" .IX Subsection "General management" These commands affect the state of the Moosic server in various ways. .IP "\fBnext\fR [\fInumber\fR]" 4 .IX Item "next [number]" Stops the current song (if any), and jumps ahead to a song that is currently in the queue. The argument specifies the number of songs to be skipped, including the currently playing song. Its default value is 1. The skipped songs are recorded in the history as if they had been played. If queue advancement is disabled, this command merely stops the current song and removes the appropriate number of songs from the queue, and does not cause a new song to be played. .IP "\fBprevious\fR [\fInumber\fR]" 4 .IX Item "previous [number]" Retreats to a previously played song (from the history list) and begins playing it if queue advancement is enabled. If a number is given as an argument, then the music daemon will retreat by that number of songs. If no argument is given, then the music daemon will retreat to the most recent song in the history. More precisely, this command stops the current song (without recording it in the song history) and returns the most recently played song or songs to the queue. This command removes songs from the history when it returns them to the queue, thus modifying the song history. .Sp When loop mode is on, this command retreats into the tail end of the queue instead of the song history. This produces wrap-around behavior that you would expect from loop mode, and does not modify the song history. .IP "\fBprev\fR" 4 .IX Item "prev" An alias for \*(L"previous\*(R". .IP "\fBgoto\fR \fIregex\fR" 4 .IX Item "goto regex" Jumps to the next song in the queue that matches the given regular expression. .IP "\fBgobackto\fR \fIregex\fR" 4 .IX Item "gobackto regex" Jumps back to the most recent previous song that matches the given regular expression. .IP "\fBnoadvance\fR" 4 .IX Item "noadvance" Tell the music daemon to stop playing any new songs, but without interrupting the current song. In other words, this halts queue advancement. .IP "\fBnoadv\fR" 4 .IX Item "noadv" An alias for \*(L"noadvance\*(R". .IP "\fBadvance\fR" 4 .IX Item "advance" Tell the music daemon to resume queue advancement (i.e. play new songs when the current one is finished). Obviously, this has no effect if queue advancement hasn't been disabled. .IP "\fBadv\fR" 4 .IX Item "adv" An alias for \*(L"advance\*(R". .IP "\fBtoggle-advance\fR" 4 .IX Item "toggle-advance" Halts queue advancement if it is enabled, and enables advancement if it is halted. .IP "\fBstop\fR" 4 .IX Item "stop" Tell the music daemon to stop playing the current song and stop processing the song queue. The current song is put back into the song queue and is not recorded in the song history. .IP "\fBpause\fR" 4 .IX Item "pause" Suspend the current song so that it can be resumed at the exact same point at a later time. Note: this often leaves the sound device locked. .IP "\fBunpause\fR" 4 .IX Item "unpause" Unpause the current song, if the current song is paused, otherwise do nothing. .IP "\fBplay\fR" 4 .IX Item "play" Tell the music daemon to resume playing. (Use after \*(L"stop\*(R", \*(L"noadv\*(R", or \&\*(L"pause\*(R".) .IP "\fBloop\fR" 4 .IX Item "loop" Turn loop mode on. When loop mode is on, songs are returned to the end of the queue when they finish playing instead of being thrown away. .IP "\fBnoloop\fR" 4 .IX Item "noloop" Turn loop mode off. .IP "\fBtoggle-loop\fR" 4 .IX Item "toggle-loop" Turn loop mode on if it is off, and turn it off if it is on. .IP "\fBreconfigure\fR" 4 .IX Item "reconfigure" Tell the music daemon to reload its configuration file. .IP "\fBreconfig\fR" 4 .IX Item "reconfig" An alias for \*(L"reconfigure\*(R". .IP "\fBshowconfig\fR" 4 .IX Item "showconfig" Query and print the music daemon's filetype associations. .IP "\fBstart-server\fR [\fIoptions\fR]" 4 .IX Item "start-server [options]" Start a new instance of the music daemon (also known as \fBmoosicd\fR). If option arguments are given, they will be used as the options for invoking \fBmoosicd\fR. The options that are accepted by \fBmoosicd\fR can be found in its own manual page, moosicd(1). .IP "\fBexit\fR" 4 .IX Item "exit" Tell the music daemon to quit. .IP "\fBquit\fR" 4 .IX Item "quit" An alias for \*(L"exit\*(R". .IP "\fBdie\fR" 4 .IX Item "die" An alias for \*(L"exit\*(R". .SH "AUDIO CD SUPPORT" .IX Header "AUDIO CD SUPPORT" If you have the takcd program installed, and you have an appropriate entry for it in the Moosic server's player configuration, then you can play audio \s-1CD\s0 tracks with Moosic. The following entry should be in \fI~/.moosic/config\fR: .PP .Vb 2 \& (?i)^cda://(\eS*) \& takcd \e1 .Ve .PP To put \s-1CD\s0 tracks into the song queue, you should name them with the prefix \&\*(L"cda://\*(R", followed immediately by the number of the track you wish to play. For example, \f(CW\*(C`moosic \-n add cda://3\*(C'\fR will add the third track on the \s-1CD\s0 to the end of the song queue. .PP The takcd program can be found at . .SH "FILES" .IX Header "FILES" .IP "\fIsocket\fR" 4 .IX Item "socket" This is a socket file which is used to allow Moosic clients to contact the Moosic server. It is generally located in the \fI~/.moosic/\fR directory, unless \&\fBmoosicd\fR was invoked with the \fB\-c\fR/\fB\-\-config\fR option. .SH "SEE ALSO" .IX Header "SEE ALSO" moosicd(1), for details on invoking the Moosic server by hand. .PP Various \fBmoosic\fR commands accept regular expressions arguments. The syntax used for these regular expressions is identical to the syntax used by Python's regular expression library. The details of this syntax are explained in the chapter entitled \*(L"Regular Expression Syntax\*(R" from the section dealing with the \fBre\fR module in the \fIPython Library Reference\fR. .SH "AUTHOR" .IX Header "AUTHOR" Daniel Pearson moosic-1.5.6/doc/moosic_hackers.txt0000644000175000017500000002553610041307046020033 0ustar danieldaniel00000000000000This is a guide to people who are interested in reading and understanding (and possibly modifying) the source code for Moosic. It is not a guide for developers writing new Moosic clients. See Moosic_API.{html,1,pod} for that. === Code Organization === The source code is organized into different files in the following way: Modules that are useful to any part of Moosic: moosic/utilities.py - generally useful classes and functions. Modules that implement the server/daemon (moosicd): moosic/server/main.py - the program's entry point. moosic/server/methods.py - the methods exported by the Moosic client API. moosic/server/support.py - various helper classes and functions for moosicd. moosic/server/xmlrpc_registry.py - a class for collecting the server methods, dispatching them, and providing introspection for them. moosic/server/daemonize.py - a function for turning a program into a daemon. Modules that are useful for any Moosic client: moosic/client/factory.py - functions which create Moosic server proxies. Modules that implement the command-line client (moosic): moosic/client/cli/main.py - the program's entry point. moosic/client/cli/dispatcher.py - the functions that implement the commands recognized by moosic. All files that do not end with the .py suffix are not program source code, but are either documentation or part of the installation system. The installation system is based upon distutils, which is a Python standard for module distribution and installation. === Adding New Commands or Methods === moosicd and moosic are programmed in a mostly procedural style, and so the basic unit of program modularity and extensibility is the procedure. Both programs each have a registration facility that is designed to allow the addition of new commands or server methods cleanly and easily, with all code for the new command or method unified in a single location in the source code. --------- Adding a New Command to "moosic" --------- The main steps of this task are: (1) Add a function with a specific signature to the moosic/client/cli/dispatcher.py module, (2) decorate this function with a few special attributes, and (3) insert the new function into the "dispatcher" dictionary object contained in moosic/client/cli/dispatcher.py. After this is done, the moosic program will automatically support a new command whose name is the same as the name of the function that was added (more or less). In more detail: (1) The function must take three arguments. The first argument, "moosic", is a proxy object that lets you execute any of the server's methods. This object is most useful for actually implementing the body of the function. The second argument, "arglist", is a list of the arguments that were specified on the command line by the user. arglist is similar to sys.argv, but it does not contain any command line options, nor the program name (argv[0]), nor the name of the moosic command. The third argument, "options", is a dictionary of information that varies from one invoacation of moosic to the next. This information includes such data as the shuffling mode and the location information for contacting the Moosic server. The return value of the function can be any sort of object. The only meaning that this return value has comes from the fact that this value is passed as the parameter to sys.exit(). Therefore, it affects the exit code of the program as a whole, but does nothing more. Refer to the documentation for sys.exit() to see how different object values affect a program's exit code. (2) After the new function has been written, it should have some auxilliary data attributes associated with it. First of all, the new function should have a doc-string that summarizes its syntax for a command-line user. This doc-string should start with the command's name, followed by a terse summary of the command's arguments (using meta-syntactic variables when appropriate), followed by a dash, and finally ending with a brief description of what the command will do. The whole doc string should be preferably a single line less than 80 characters long, and should be no longer than two lines (each 80 characters long). Next, the command's function object should have an attribute named "category" whose value is a string that is equal to one of the keys in the dictionary named "command_categories", which is defined in moosic/client/cli/dispatcher.py. An appropriate category from command_categories should be chosen. This value, like the above-mentioned doc-string, is only used when generating documentation for the commands and doesn't affect the behavior of the command being implemented. If the "category" attribute is not one of the keys of command_categories, then the new command will not be listed by the "help" command nor the "--showcommands" option. The new command's function object should also have an attribute named "num_args" whose value is a short string that describes how many arguments the user will be allowed to specify for the command. This value is used by the check_args() function (which is defined in moosic/client/cli/dispatcher.py) to verify that an acceptable number of arguments have been given to a particular command. The valid values for num_args are mentioned in the documentation for the check_args() function. If num_args doesn't have one of these valid values, then argument checking simply won't be done for the new command. (3) Finally, the new function can be added to the "dispatcher" dictionary. This should be done by creating a new dictionary entry whose key is the name of the command and whose value is the newly defined function. If you look at the existing moosic commands as examples, you'll notice that the key for each entry in the dispatcher dictionary is automatically extracted from the function object by feeding the function's "__name__" attribute to the unmangle() utility function (defined in moosic/client/cli/dispatcher.py). You are encouraged to follow this example, but it is hardly necessary. That's it. If you've done these steps, you've added a new command to moosic. --------- Adding a New Server Method to "moosicd" --------- On the surface, this task seems much simpler than adding a new command to moosic: simply add a new function to moosic/server/methods.py and feed it to the register() method of the moosicd_methods object (which is defined within moosic/server/methods.py). The new function can have any number of arguments, and doesn't need to have any unusual attributes set upon it. However, actually programming a new server method requires awareness of the stateful environment that exists outside of the local function that implements the method. By contrast, "moosic" is a relatively stateless program (since the state is primarily maintained by the server), and therefore each function that implements each moosic command doesn't have to worry about any objects that were not explicitly passed to the function as arguments. In order to keep the task of programming server methods relatively sane, all of the server's global data is contained within an object named "data" (originally defined in moosic/server/support.py, and imported by the other modules that comprise moosicd). This lets a programmer easily distinguish between a method's local variables and the server's state data. The public attributes of this "data" object are listed and described in detail in the Moosic API documentation (which is available in several formats, including HTML, manpage, and POD). The private attributes are documented with comments in moosic/server/support.py. Note that trying to add new attributes to the "data" object during the course of normal use will raise an exception. Adding new attributes to "data" must be done in its class definition. If the new server method is going to modify any of the server's state data, then the appropriate lock should be acquired first by calling "data.lock.acquire()". The code that modifies the state should follow immediately after the lock is acquired. This modification should be done within a "try" block that ends with a "finally" clause that contains a call to "data.lock.release()". The time spent holding the lock should be minimized as much as possible to avoid delaying any other threads that might wish to acquire the lock. The following code demonstrates how to perform such a modification with the proper locking: data.lock.acquire() try: # mutate one of the attributes of data finally: data.lock.release() Since server methods are exported to clients via the XML-RPC protocol, the types of the objects that the new server method takes as parameters and outputs through its return value should be limited to those types supported by XML-RPC. A discussion of these supported types and their relationship to the Python type system can be found in the documentation for the "xmlrpclib" module in the Python Library Reference. One corollary to the requirement to conform to XML-RPC types is that the new function should have an explicit return value. If it doesn't, then Python will automatically make the function return None, which isn't supported as a valid value for passing through XML-RPC (according to the more strict implementations of the XML-RPC standard). As mentioned very briefly above, after the function that implements the new server method has been defined in moosic/server/methods.py, it must be registered into the moosicd_methods object by calling that object's register() method. This register() method takes the function to be registered as its first argument, and a specification of that function's type signature as its second argument. A third argument can be given to specify that the function should be registered under a name other than the identifier used to reference the function in the Python source code (i.e. the function's name). However, this argument is commonly not needed. The second argument to register() warrants further elaboration. The information in this argument is used only for the purposes of documentation, which is quite important since the developer of a Moosic client will need to know how to call the new server method before she can use it. The form of this argument is a list of one or more lists. Each of these lists specifies a valid type signature for the method. (More than one signature is possible because XML-RPC supports function overloading.) The first element of each signature list specifies the type of the method's return value, and all subsequent elements specify the types of the method's parameters (in order). These list elements are symbolic constants that represent datatypes. These constants are imported from the xmlrpc_registry module which is a part of moosicd's source code. After adding the new function and registering it, the process of adding a new server method is complete. moosic-1.5.6/doc/moosicd.pod0000644000175000017500000001552610033172017016437 0ustar danieldaniel00000000000000=head1 NAME moosicd - the server for the Moosic jukebox system. =head1 SYNOPSIS B B<--help>|B<-h>|B<--version>|B<-v> B [B<--history-size>|B<-s> I] [B<--config>|B<-c> I] [B<--quiet>|B<-q>|B<--debug>|B<-d>] [B<-S>|B<--stdout>] [B<-t>|B<--tcp> I] [B<-T>|B<--tcp-also> I] [B<-l>|B<--local-only>] =head1 DESCRIPTION B is the server for the Moosic jukebox system. It sits around, waiting to respond to commands given by a Moosic client (such as L(1)). It also maintains a queue of items to be played, and if this queue is not empty, it pops off the first item from the head of the queue and executes a user-configurable command on that item. When this command terminates, B goes on to the next item in its queue, assuming that the queue is not empty. B is not meant to be used as a system-wide daemon that serves all users on a system. Rather, every user that wishes to use Moosic should start a separate instance of B, and one user cannot communicate with the Moosic server of another user without taking special measures (i.e. using the B<-c> or B<-t> options to B). =head1 OPTIONS B is designed so that you normally don't need to use any of these options. =over =item B<-h>, B<--help> Prints help text and exits. =item B<-v>, B<--version> Prints version information and exits. =item B<-s> I, B<--history-size> I B remembers the file names of previous songs that it played. This option sets the maximum size of this history list. The default value is 50. =item B<-f>, B<--foreground> By default, B detaches itself from the current terminal and puts itself in the background (i.e. it becomes a daemon). This option disables such behavior. =item B<-S>, B<--stdout> By default, B logs almost all of its printed output to a file. When this option is used, the output is instead printed to the standard output device. This also prevents the program from putting itself in the background and detaching from the current terminal. =item B<-q>, B<--quiet> This option suppresses almost all printed output from B. Note that, by default, B prints this output to a file, not the standard output device. =item B<-d>, B<--debug> This option causes B to print lots and lots of messages about what it's doing. These messages are usually quite superfluous and bothersome. Note that unless the B<-S> option is used these messages will appear in the log file instead of the standard output device. =item B<-c> I, B<--config> I Specifies the directory where moosicd should keep the various files that it uses. The default directory is F<~/.moosic/>. This option is useful only in extraordinary circumstances. If this option is used, any Moosic clients that wish to communicate with B must be told to use the specified directory instead of the default. =item B<-t> I, B<--tcp> I This option directs the server to listen to the given TCP port number for client requests instead of using the normal communication method. Use of this option without the B<-l> option is I unless you know what you are doing because there is no guarantee that B is secure against malicious input from a remote location. Note that an instance of B which is started with this option will not accept requests from a client that is using the normal communication method. =item B<-T> I, B<--tcp-also> I This option directs the server to listen to the given TCP port number for client requests in addition to using the normal communication method. Use of this option without the B<-l> option is I unless you know what you are doing because there is no guarantee that B is secure against malicious input from a remote location. =item B<-l>, B<--local-only> This directs the server to only listen for TCP connections that originate from the local computer, refusing connections from remote hosts. This only has an effect when B<--tcp> or B<--tcp-also> is used. =back =head1 CONFIGURATION B figures out how to play items in its queue by consulting its configuration file, which associates string patterns (in the form of Perl-compatible regular expressions) with commands. The format of this file is as follows: Every pair of lines forms a unit. The first line in a pair is a regular expression that will be matched against items in the queue. The second line in a pair is the command that will be used to play any items that match the regular expression. The name of the item to be played will be appended to the end of this command, unless the command line includes a special substitution string. The simplest kind of substitution string is "$item". Every occurrence of "$item" in the command will be replaced with the name of the song to be played. The other kind of substitution is called "matched group substitution", and is used to refer to specific parts of the name of the song to be played. The command will not be interpreted by a shell, so don't bother trying to use shell variables or globbing or I/O redirection, and be mindful of how you use quotes and parentheses. If you need any of these fancy features, wrap up the command in a real shell script (and remember to use an "exec" statement in your shell script to invoke the program that does the actual song playing, otherwise Moosic won't be able to do things like stop or pause the song). Blank lines and lines starting with a '#' character are ignored. Regular expressions specified earlier in this file take precedence over those specified later. =head1 FILES B makes use of several files, all of which are found in the F<.moosic/> directory in the home directory of the user who invoked the program (unless the -c or --config option is used). =over =item F This is the configuration file that B uses to associate file-types to player commands, as explained above. =item F B logs short notices of its activities to this file unless the B<-S> option is used. It usually contains nothing more than a history of what songs have been played. =item F This file contains the output of the player commands which are spawned by B. =item F This is a socket file which is (normally) used to allow Moosic clients to contact the Moosic server. If B isn't shut down properly, this file will need to be removed by hand. You should leave this file alone under other circumstances. =back =head1 SEE ALSO L(1), the standard command-line Moosic client. The chapter entitled "Regular Expression Syntax" L from the section dealing with the B module in the I, for details on the syntax of a regular expression. =head1 AUTHOR Daniel Pearson moosic-1.5.6/doc/Todo0000644000175000017500000000424210041304776015127 0ustar danieldaniel00000000000000New features specific to moosic (the command-line client): - Add support for a ".moosicrc" configuration file to store default options that would otherwise have to be set on the command-line. - Find a way to let users specify queue ranges that start with a negative number without requiring a '--' to separate the range argument from the option parser. Possibilities: '[-9:-3]', '{-9:-3}', '(-9:-3)', '/-9:-3/', '/-9:-3', '=-9:-3', ',-9:-3', '.-9:-3', ':-9:-3:'. Surrounding with slashes seems the best, since it doesn't conflict with shell meta-characters, and it's easy to type, and it's one of the least visually unappealing choices. New features specific to moosicd: ====== Short term goals ====== - Implement server methods that allow remote clients to inspect (a possibly restricted portion of) the filesystem of the computer on which the server is running. (Possibly in a separate daemon.) - Implement server methods that allow remote clients to change the sound volume. (Possibly in a separate daemon.) ====== Long term goals ====== - Provide a hook for providing notification of when a song is started or stopped. - Devise a plugin-based system for handling individual songs. This would require a small revision to the config file format, but the impact of this revision could be minimized. - Measure the current song's total length in time. - Measure the amount of time until the current song will finish. - Support methods for seeking within a song. - Implement skipping to the next playlist in a list. Web site: - Create a page listing and thanking contributors to the project. - Create a page with a list of related projects. Invisible implementation details: - Explicitly define and document the semantics of inserting the empty string into the song queue. Alternative clients: - Write a GTK+ GUI client (gmoosic). - Write a GNUstep/Cocoa GUI client (MoosicStep/MoosicX). - A curses client (cmoosic), a PHP-based web client (php-moosic), and an interactive command interpreter client (moosicsh) are left as exercises to the reader (or me, if I get really bored). - Internationalize everything. moosic-1.5.6/doc/Moosic_API.sect2.pod0000644000175000017500000000612510131342043017733 0ustar danieldaniel00000000000000=head1 Section 2: The Low-Level Details of Client-Server Communication The information in this section is generally only necessary to people who wish to write a Moosic client in a programming language other than Python. If you are using Python to write a Moosic client, then you can use the classes LocalMoosicProxy and InetMoosicProxy from the moosic_factory.py module, and blissfully ignore most of these gory details. However, Python programmers can also benefit from reading this section, as it will deepen their understanding of Moosic's inter-process communication model. The first thing to know about writing your own Moosic client is that communication between the client and server is done through a BSD-style socket. Read the "socket" manual page (and related manual pages) on a Unix system if you are unfamiliar with BSD sockets. The socket used by Moosic belongs to the Unix-domain protocol family (PF_UNIX or PF_LOCAL) and has a type of SOCK_STREAM. This means that a Moosic client can only communicate with a Moosic server that is running on the same computer as the client. This limitation is a very purposeful part of Moosic's design. It has the advantage of vastly reducing the consequences of any security flaws that Moosic might have. If you really, really think that you need the client and the server to run on separate hosts, then you can run moosicd with the -t option, which tells it to listen on a TCP/IP socket instead of a Unix domain socket. I recommend firewalling such a port very carefully. Regardless of which kind of socket is used by the server, XML-RPC is used as the data protocol for requests and responses. For an introduction to XML-RPC, see the XML-RPC homepage L and the XML-RPC HOWTO L. Python users should note that if the XML-RPC HOWTO tells you that you need to install a third-party library to use XML-RPC, it is assuming that you are using a Python version earlier than 2.2. Since version 2.2, Python has included the xmlrpclib module in its standard library. In summary, all you need to do to talk to a Moosic server in your own program is to send XML-RPC requests to the appropriate address. By default, the appropriate address for contacting moosicd is the file named "socket" in a directory named ".moosic" in the home directory of the user that started moosicd (i.e. "~/.moosic/socket"). If moosicd is started with the -c option, then the directory that contains "socket" will be the argument provided to the -c option instead of ~/.moosic. If moosicd is started with the -t option, then clients will have to address it by using a (host, port) pair instead of a filename. =head1 Section 3: Valid Moosic Server Methods moosicd's XML-RPC server implements the introspection API mentioned on L, so the API presented by moosicd is essentially self-documenting. Thus, the information in this section has been automatically generated by querying a running Moosic server. The Moosic API contains the following methods: moosic-1.5.6/doc/moosic.html0000644000175000017500000012500010625507223016452 0ustar danieldaniel00000000000000 moosic - a command-line client for the Moosic jukebox system.


NAME

moosic - a command-line client for the Moosic jukebox system.


SYNOPSIS

moosic [options] command [options] [command arguments]


DESCRIPTION

The moosic program is the command-line interface to the Moosic jukebox system. It communicates with the moosicd manpage(1), the Moosic server, querying the server for information and telling the server what to do. moosic will not be able to do very much unless moosicd is running. When moosicd isn't already running, moosic will automatically start it for you, unless you specifically request otherwise (with the --no-startserver option).


USAGE

moosic works by sending a command to the Moosic server and returning the response, if any. The first non-option argument given to moosic is the name of the command to be performed. This command name is case-insensitive, and all non-alphanumeric characters in it are ignored. You can use the ``help'' command to quickly and easily view the names of all the available commands and to get a brief description of individual commands. You can also use moosic --showcommands to display the short descriptions of all the commands at once. The COMMANDS section below lists the full details of each command. There are very many commands, so you should start by just learning a few commonly used commands, and only learning others as you feel the need. I recommend starting with the following short command vocabulary: add, list, stop, play, and shuffle.

For example, moosic add foo.mp3 adds the file foo.mp3 (in the current directory) to the end of the song queue and returns you immediately back to your shell prompt without printing any output (unless an error occurs). Compare with moosic list, which will list the contents of the song queue. Note that if the song queue is empty, moosic list will not display anything.


OPTIONS

Most of the options for moosic are only relevant if they are used with one of the commands that take a filelist argument. See COMMANDS for the definition of a filelist. The only shuffling options that don't mutually exclude each other are -d and -a. Shuffling options that are named later on the command line take precedence over ones that occur earlier. All options must be named immediately before the command given to moosic or immediately after the command; options placed within the list of the command's arguments will not be interpreted as options.

-g, --shuffle-global
This option causes moosic to shuffle the entire filelist after directory expansion has taken place, before sending the filelist to the Moosic server. This is the default behavior. This option is only meaningful if used in conjunction with a command that accepts a filelist.

-d, --shuffle-dir
This option causes moosic to shuffle the results of expanding the directories named in the filelist. This option is only meaningful if used in conjunction with a command that accepts a filelist.

-a, --shuffle-args
This option causes moosic to shuffle the actual command line arguments that comprise the filelist. This option is only meaningful if used in conjunction with a command that accepts a filelist.

-o, --inorder
When this option is used, moosic doesn't shuffle the filelist named on the command line at all. Rather, the order specified on the command line is preserved. This option is only meaningful if used in conjunction with a command that accepts a filelist.

-s, --sort
When this option is used, moosic sorts the filelist lexicographically after it has been expanded (through directory recursion or auto-finding or the like). The order specified on the command line is ignored. This option is only meaningful if used in conjunction with a command that accepts a filelist.

-r, --no-recurse
Using this option prevents moosic from replacing directories named in the filelist with a recursive traversal of their contents.

-n, --no-file-munge
Using this option prevents moosic from modifying the names in the expanded filelist. Normally, moosic converts relative filenames into absolute filenames before sending the names to moosicd, but this is generally not desirable behavior if you want to insert items that aren't local files into the queue (such as URLs). This option is only meaningful if used in conjunction with a command that accepts a filelist.

-i, --ignore-case
Treats any given regular expressions as if they were case-insensitive. This option is only meaningful if used in conjunction with a command that accepts one or more regular expressions as arguments. This option is syntactic sugar, since the regular expressions supported by Moosic can also be made case-insensitive by including ``(?i)'' within the regular expression.

-f, --auto-find
This option causes each string in the filelist with the results of performing a ``fuzzy'' search for music files. ``Fuzzy'' matching is done by simplifying all the candidate filenames (by lowering the case and removing all non-alphanumeric characters except slashes) and then testing to see if the search string (which has been similarly simplified) is contained in any of the filenames. The list of candidate filenames is obtained by recursively traversing the file hierarchy rooted at the directory specified by the --music-dir option (which has a default value of ~/music/).

For example, if you use moosic -f add severedgoddess, and the file ~/music/Meat_Puppets/Severed_Goddess_Hand.mp3 exists, then this file will be included in the list of files to be added to the queue. Similarly, if you use moosic -f pre nesad, and the directory ~/music/J/Jane's Addiction/ exists, then all the files in this directory (and its subdirectories) will be included in the list of files to be prepended to the queue.

This option is only meaningful if used in conjunction with a command that accepts a filelist. Beware that using this option can cause moosic to take a long time to complete if the directory tree being searched contains a very large number of files.

-F, --auto-grep
This option enables behavior very much like that of the --auto-find option, except that regular expression searching is used instead of the ``fuzzy'' search scheme. Specifically, each string in the filelist is treated as a regular expression, and is replaced with all the filenames that match the expression. As with --auto-find, the filenames that are eligible for matching are obtained by traversing the directory named with the --music-dir option (defaulting to ~/music/ if --music-dir is not used). Essentially, moosic -F prepend something is semantically equivalent to moosic prepend `find ~/music/ | grep something`, but is syntactically a lot sweeter.

This option is only meaningful if used in conjunction with a command that accepts a filelist. Beware that using this option can cause moosic to take a long time to complete if the directory tree being searched contains a very large number of files.

-m directory, --music-dir directory
This option controls which directory is used for searching when the ``auto-find'' or ``auto-grep'' feature is enabled. These automatic searches are limited to the file hierarchy rooted at the directory specified by this option. When this option is not used, the ~/music/ directory is used as a default. This option is only meaningful if either --auto-find or --auto-grep is used.

-S, --showcommands
Prints a list of the commands that may be used with moosic and then exits. Note that this output is quite copious, so you will probably want to pipe it to a text pager, such as less.

-h, --help
Prints a short help message that explains the command line options and then exits.

-v, --version
Prints version information and then exits.

-c directory, --config-dir directory
This option is not needed under normal circumstances. It should only be used if you want moosic to communicate with an instance of moosicd which was invoked with the -c/--config option. Using this option tells moosic to search the specified directory for the files which are usually found in ~/.moosic/.

-t host:port, --tcp host:port
This option tells moosic to communicate with a Moosic server that is listening to the specified TCP/IP port on the specified host. Running a Moosic server that accepts requests via TCP/IP is not recommended because it is a security risk.

-N, --no-startserver
This option prevents moosic from trying to automatically start moosicd if it can't contact a Moosic server.

-U, --allow-unplayable
This option allows songs that the server doesn't know how to play to be added into the song queue.

-C, --current-in-list
This option causes the currently playing song to be printed at the top of the output of the ``list'' and ``plainlist'' commands. It has no effect if an argument is given to these commands or if used with other commands.


COMMANDS

Any of these commands may be specified with any mixture of upper-case and lower-case letters, and non-alphabetic characters (such as '-') may be omitted.

Many of these commands accept a range argument. A range is a pair of colon-separated numbers. Such a range addresses all items whose index in the song queue is both greater than or equal to the first number and less than the second number. For example, ``3:7'' addresses items 3, 4, 5, and 6. If the first number in the pair is omitted, then the range starts at the beginning of the song queue. If the second number in the pair is omitted, then the range extends to include the last item in the song queue. A range can also be a single number (with no colon), in which case it addresses the single item whose index is that of the given number. Negative numbers may be used to index items from the end of the list instead of the beginning. Thus, -1 refers to the last item in the song queue, -2 refers to the second-to-last item, etc.

Beware that a negative number that immediately follows a moosic command is liable to be incorrectly interpreted as an option, so option processing should be explicitly terminated with an argument of ``--'' between the command and the number. This is illustrated by the following example, which removes the last item in the queue: moosic del -- -1

Alternatively (and perhaps more conveniently), you can prevent negative numbers from being interpreted as options by preceding the range with a single character that can't be mistaken for a number or an option (i.e. any character that isn't a digit or a dash). Example: moosic list /-15:-9. You can also place such a character at the end of the range if you think it makes it look prettier. Example: moosic list /-15:-9/. The bracketing characters surrounding a range need not be the same: moosic shuffle '[-13:8]'. Notice how the preceding example surrounded the range in quotes to prevent the shell from treating the ``['' and ``]'' characters specially (since shells have a habit of doing things like that).

Querying for information

These commands print useful bits of information to standard output.

help [command ...]
Prints a brief description of the moosic commands named as arguments. If no arguments are given, a list of all the available moosic commands is printed.

current
Print the name of the song that is currently playing.

curr
An alias for ``current''.

current-time [format]
Print the amount of time that the current song has been playing. By default, this time is printed in a format of ``hours:minutes:seconds'', but if a different format is desired, a string argument can be given to specify it. The format should be a string that is appropriate for passing to the strftime(3) function.

list [range]
Print the list of items in the current song queue. A whole number is printed before each item in the list, indicating its position in the queue. If a range is specified, only the items that fall within that range are listed. Remember that the song queue does not contain the currently playing song.

plainlist [range]
Print the current song queue without numbering each line. If a range is specified, only the items that fall within that range are listed. This output is suitable for saving to a file which can be reloaded by the ``pl-append'', ``pl-prepend'', ``pl-insert'', and ``pl-mixin'' commands.

history [number]
Print a list of items that were recently played. The times mentioned in the output of this command represents the time that a song finished playing. If a number is specified, then no more than that number of entries will be printed. If a number is not specified, then the entire history is printed. Note that moosicd limits the number of items stored in its history list.

hist [number]
An alias for ``history''.

state
Print the current state of the music daemon.

status
An alias for ``state''.

length
Print the number of items in the queue.

len
An alias for ``length''.

ispaused
Show whether the current song is paused or not. If the song is paused, ``True'' is printed and moosic returns normally. If the song is not paused, ``False'' is printed and moosic returns with a non-zero exit status (which happens to be 2 for no particular reason).

islooping
Show whether the server is in loop mode. If the server is in loop mode, ``True'' is printed and moosic returns normally. If not, ``False'' is printed and moosic returns with a non-zero exit status (which happens to be 2 for no particular reason).

isadvancing
Show whether the server is advancing through the song queue. If the server is advancing, ``True'' is printed and moosic returns normally. If not, ``False'' is printed and moosic returns with a non-zero exit status (which happens to be 2 for no particular reason).

version
Print version information for both the client and the server, and then exit.

Adding to the song queue

These commands will add to the queue of items to be played. Many of these commands accept a filelist argument. A filelist is a list of one or more files or directories. Any directories named in the list will be replaced by a list of files produced by recursively traversing the contents of the directory (unless the --no-file-munge option or --no-recurse option is being used). Depending on the shuffling options specified when invoking moosic, the list will be shuffled before being added to the Moosic server's queue.

append filelist
Add the files to be played to the end of the song queue.

add filelist
An alias for ``append''.

pl-append playlist-file ...
Add the items listed in the given playlist files to the end of the song queue. If ``-'' (a single dash) is given as the name of a playlist file, data will be read from from standard input instead of trying to read from a file named ``-''.

pl-add playlist-file ...
An alias for ``pl-append''.

prepend filelist
Add the files to be played to the beginning of the song queue.

pre filelist
An alias for ``prepend''.

pl-prepend playlist-file ...
Add the items listed in the given playlist files to the beginning of the song queue. If ``-'' (a single dash) is given as the name of a playlist file, data will be read from from standard input instead of trying to read from a file named ``-''.

mixin filelist
Add the files to the song queue and reshuffle the entire song queue.

pl-mixin playlist-file ...
Add the items listed in the given playlist files to the song queue and reshuffle the entire song queue. If ``-'' (a single dash) is given as the name of a playlist file, data will be read from from standard input instead of trying to read from a file named ``-''.

replace filelist
Replace the current contents of the song queue with the songs contained in the filelist.

pl-replace playlist-file ...
Replace the current contents of the song queue with the songs named in the given playlists.

insert filelist index
Insert the given items at a given point in the song queue. The items are inserted such that they will precede the item that previously occupied the specified index.

pl-insert playlist-file ... index
Insert the items specified in the given playlist files at a specified point in the song queue. If ``-'' (a single dash) is given as the name of a playlist file, data will be read from from standard input instead of trying to read from a file named ``-''.

putback
Reinsert the current song at the start of the song queue.

stagger-add filelist
Adds the file list to the end of the song queue, but only after rearranging it into a ``staggered'' order. This staggered order is very similar the order created by the stagger command (described below). Each element of the file list (before replacing directories with their contents) specifies a category into which the expanded file list will be divided. The staggered order of the list being added is formed by taking the first item from each category in turn until all the categories are empty. This may be a bit difficult to understand without an example, so here is a typical case:

Initially, the queue contains a few items.

    [0] /music/a.ogg
    [1] /music/b.mp3
    [2] /music/c.mid

Additionally, there are two directories that each contain a few files:

    $ ls /music/X/ /music/Y/
    X:
    1.ogg  2.ogg  3.ogg
    Y:
    1.ogg  2.ogg  3.ogg  4.ogg

After executing moosic -o stagger-add /music/Y /music/X, the queue now contains:

    [0] /music/a.ogg
    [1] /music/b.mp3
    [2] /music/c.mid
    [3] /music/Y/1.ogg
    [4] /music/X/1.ogg
    [5] /music/Y/2.ogg
    [6] /music/X/2.ogg
    [7] /music/Y/3.ogg
    [8] /music/X/3.ogg
    [9] /music/Y/4.ogg

stagger-merge filelist
Adds the given file list to the queue in an interleaved fashion. More specifically, the new song queue will consist of a list that alternates between the items from the given file list and the items from the existing song queu. For example, if the queue initially contains:
    [0] /music/a.ogg
    [1] /music/b.mp3
    [2] /music/c.mid

And the /music/Y/ directory contains:

    1.ogg  2.ogg  3.ogg  4.ogg

Then, after executing moosic -o stagger-merge /music/Y, the queue will contain:

    [0] /music/Y/1.ogg
    [1] /music/a.ogg
    [2] /music/Y/2.ogg
    [3] /music/b.mp3
    [4] /music/Y/3.ogg
    [5] /music/c.mid
    [6] /music/Y/4.ogg

interval-add interval filelist
Inserts the given songs into the current song queue with a regular frequency that is specified with the given interval argument (which must be an integer).

For example, if the queue initially contains:

    [0] /music/a.mod
    [1] /music/b.mod
    [2] /music/c.mod
    [3] /music/d.mod
    [4] /music/e.mod
    [5] /music/f.mod
    [6] /music/g.mod

And the /music/Z directory contains:

    aleph.wav  bet.wav  gimmel.wav

Then, after executing moosic -o interval-add 3 /music/Z, the queue will contain:

    [0] aleph.wav
    [1] /music/a.mod
    [2] /music/b.mod
    [3] bet.wav
    [4] /music/c.mod
    [5] /music/d.mod
    [6] gimmel.wav
    [7] /music/e.mod
    [8] /music/f.mod
    [9] /music/g.mod

Removing from the song queue

These commands will remove from the queue of items to be played.

cut range
Removes all song queue items that fall within the given range.

del range
An alias for ``cut''.

crop range
Removes all song queue items that do not fall within the given range.

remove regex ...
Remove all song queue items that match the given regular expression. If multiple regular expressions are given, any song that matches any one of the expressions will be removed.

filter regex ...
Remove all song queue items that do not match the given regular expression. If multiple regular expressions are given, only those songs that match all the regular expressions will remain afterward.

clear
Clear the song queue.

wipe
Clear the song queue and stop the current song.

Rearranging the song queue

These commands let you change the order of the items in the queue.

move range index
Moves all items in the given range to a new position in the song queue. If you want to move items to the end of the queue, use `moosic length` as the final argument. For example, to move the first 10 songs to the end of the queue, use the following command: moosic move 0:10 `moosic length`

move-pattern regex index
Moves all items that match the given regular expression to a new position in the song queue.

swap range range
Causes the songs contained within the two specified ranges to trade places.

reshuffle [range]
Reshuffle the song queue. If a range is specified, only items that fall within that range will be shuffled.

shuffle [range]
An alias for ``reshuffle''.

sort [range]
Rearrange the song queue in sorted order. If a range is specified, only items that fall within that range will be sorted.

reverse [range]
Reverse the order of the song queue. If a range is specified, only items that fall within that range will be reversed.

partial-sort regex ...
For each specified regular expression, the items in the song queue that match that expression are removed from the queue and gathered into their own list. All of these lists (plus the list of items that did not match any regular expression) are then stitched back together through simple concatenation. Finally, this unified list replaces the contents of the song queue.

The items that match a particular regular expression will remain in the same order with respect to each other. Each group of matched items will appear in the reordered song queue in the order that the corresponding regular expressions were specified on the command line.

stagger regex ...
For each specified regular expression, the items in the song queue that match that expression are removed from the queue and gathered into their own list. All of these lists are then merged together in a staggered fashion. All the leftover items (i.e. the ones that weren't matched by any regex on the command line) are appended to this unified list, which then replaces the contents of the song queue.

For example, if you use moosic stagger red blue green and the queue originally contains only names that either contain the string ``red'' or ``blue'' or ``green'', then the members of the reordered queue will alternate between ``red'' items, ``blue'' items, and ``green'' items. If the queue does contain items that are neither ``red'' nor ``green'' nor ``blue'', then these will be collected and placed at the end of the queue, after all the ``red'', ``green'', and ``blue'' items.

sub pattern replacement [range]
Perform a regular expression substitution on all items in the song queue. More precisely, this searches each queue item for the regular expression specified by the first argument, and replaces it with the text specified by the second argument. Any backslash escapes in the replacement text will be processed, including special character translation (e.g. ``\n'' to newline) and backreferences to groups within the match. If a range is given, then the substitution will only be applied to the items that fall within the range, instead of all items. Only the first matching occurrence of the pattern is replaced in each item.

suball pattern replacement [range]
This is identical to the ``sub'' command, except that all occurrences of the pattern within each queue item are replaced instead of just the first occurrence.

General management

These commands affect the state of the Moosic server in various ways.

next [number]
Stops the current song (if any), and jumps ahead to a song that is currently in the queue. The argument specifies the number of songs to be skipped, including the currently playing song. Its default value is 1. The skipped songs are recorded in the history as if they had been played. If queue advancement is disabled, this command merely stops the current song and removes the appropriate number of songs from the queue, and does not cause a new song to be played.

previous [number]
Retreats to a previously played song (from the history list) and begins playing it if queue advancement is enabled. If a number is given as an argument, then the music daemon will retreat by that number of songs. If no argument is given, then the music daemon will retreat to the most recent song in the history. More precisely, this command stops the current song (without recording it in the song history) and returns the most recently played song or songs to the queue. This command removes songs from the history when it returns them to the queue, thus modifying the song history.

When loop mode is on, this command retreats into the tail end of the queue instead of the song history. This produces wrap-around behavior that you would expect from loop mode, and does not modify the song history.

prev
An alias for ``previous''.

goto regex
Jumps to the next song in the queue that matches the given regular expression.

gobackto regex
Jumps back to the most recent previous song that matches the given regular expression.

noadvance
Tell the music daemon to stop playing any new songs, but without interrupting the current song. In other words, this halts queue advancement.

noadv
An alias for ``noadvance''.

advance
Tell the music daemon to resume queue advancement (i.e. play new songs when the current one is finished). Obviously, this has no effect if queue advancement hasn't been disabled.

adv
An alias for ``advance''.

toggle-advance
Halts queue advancement if it is enabled, and enables advancement if it is halted.

stop
Tell the music daemon to stop playing the current song and stop processing the song queue. The current song is put back into the song queue and is not recorded in the song history.

pause
Suspend the current song so that it can be resumed at the exact same point at a later time. Note: this often leaves the sound device locked.

unpause
Unpause the current song, if the current song is paused, otherwise do nothing.

play
Tell the music daemon to resume playing. (Use after ``stop'', ``noadv'', or ``pause''.)

loop
Turn loop mode on. When loop mode is on, songs are returned to the end of the queue when they finish playing instead of being thrown away.

noloop
Turn loop mode off.

toggle-loop
Turn loop mode on if it is off, and turn it off if it is on.

reconfigure
Tell the music daemon to reload its configuration file.

reconfig
An alias for ``reconfigure''.

showconfig
Query and print the music daemon's filetype associations.

start-server [options]
Start a new instance of the music daemon (also known as moosicd). If option arguments are given, they will be used as the options for invoking moosicd. The options that are accepted by moosicd can be found in its own manual page, the moosicd manpage(1).

exit
Tell the music daemon to quit.

quit
An alias for ``exit''.

die
An alias for ``exit''.


AUDIO CD SUPPORT

If you have the takcd program installed, and you have an appropriate entry for it in the Moosic server's player configuration, then you can play audio CD tracks with Moosic. The following entry should be in ~/.moosic/config:

    (?i)^cda://(\S*)
    takcd \1

To put CD tracks into the song queue, you should name them with the prefix ``cda://'', followed immediately by the number of the track you wish to play. For example, moosic -n add cda://3 will add the third track on the CD to the end of the song queue.

The takcd program can be found at http://bard.sytes.net/takcd/.


FILES

socket
This is a socket file which is used to allow Moosic clients to contact the Moosic server. It is generally located in the ~/.moosic/ directory, unless moosicd was invoked with the -c/--config option.


SEE ALSO

the moosicd manpage(1), for details on invoking the Moosic server by hand.

Various moosic commands accept regular expressions arguments. The syntax used for these regular expressions is identical to the syntax used by Python's regular expression library. The details of this syntax are explained in the chapter entitled ``Regular Expression Syntax'' http://www.python.org/doc/current/lib/re-syntax.html from the section dealing with the re module in the Python Library Reference.


AUTHOR

Daniel Pearson <daniel@nanoo.org>

moosic-1.5.6/moosic/0000755000175000017500000000000011655460503015024 5ustar danieldaniel00000000000000moosic-1.5.6/moosic/server/0000755000175000017500000000000011655460503016332 5ustar danieldaniel00000000000000moosic-1.5.6/moosic/server/support.py0000644000175000017500000005223111655333607020427 0ustar danieldaniel00000000000000# moosic.server.support.py - classes and functions that support moosicd # # This is free and unencumbered software released into the public domain. # # Anyone is free to copy, modify, publish, use, compile, sell, or # distribute this software, either in source code form or as a compiled # binary, for any purpose, commercial or non-commercial, and by any # means. # # In jurisdictions that recognize copyright laws, the author or authors # of this software dedicate any and all copyright interest in the # software to the public domain. We make this dedication for the benefit # of the public at large and to the detriment of our heirs and # successors. We intend this dedication to be an overt act of # relinquishment in perpetuity of all present and future rights to this # software under copyright law. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # # For more information, please refer to import sys, os, os.path, string, threading, time, socket, traceback, errno import SocketServer, SimpleXMLRPCServer # Define the True and False constants if they don't already exist. try: True except NameError: True = 1 try: False except NameError: False = 0 __all__ = ('data', 'readConfig', 'strConfig', 'getConfigFile', 'split_range', 'Log', 'UnixMoosicRequestHandler', 'TcpMoosicRequestHandler', 'UnixMoosicServer', 'TcpMoosicServer') class DataStore: """A convenient place to store the data maintained by the Moosic server. """ def __init__(self): self.__dict__['doing_init'] = True #------ data that should be remembered if moosicd is restarted ------# # 'song_queue' is a list of all the songs that are waiting to be played. self.song_queue = [] # 'qrunning' is used for controlling the state of the thread that is in # charge of advancing through the playlist, a.k.a. the queue consumer. # If it is true, then the queue consumer will continue advancing through # the playlist, otherwise the queue consumer will be idle. self.qrunning = True # 'loop_mode' is a flag which, if true, causes the queue consumer to # place items at the end of the queue when they finish playing instead # of throwing them away. self.loop_mode = False # 'history' is a list of all the songs that have been played, along with # timestamps indicating when each song started and finished playing. self.history = [] # 'max_hist_size' sets the limit on the size of the history list stored # in memory. self.max_hist_size = 50 #------ data that should not be remembered if moosicd is restarted ------# # 'moosic_server' is a the SocketServer object that is used to listen # for and handle requests from Moosic clients. self.moosic_server = None # 'current_song' is the song that is currently playing. If no song is # currently playing, this will be the empty string. self.current_song = '' # 'player_pid' is the process ID of the process that is playing the # current song. self.player_pid = None # 'paused' is a flag that keeps track of whether the song player has # been paused. self.paused = False # 'lock' is used to synchronize write-access to the other global # variables. self.lock = threading.RLock() # 'quitFlag' is used to let the different parts of moosicd tell each # other that it is time to shut down. When the queue consumer sees that # this flag is true, it will terminate itself. self.quitFlag = False # 'config' is a list of associations between filename patterns and # player programs. self.config = [] # 'confdir' is the directory where moosicd stores its log files, # configuration files, and socket files. The default configuration # directory is set here. self.confdir = os.path.join(os.getenv('HOME', '/tmp'), '.moosic') # 'conffile' is the file which contains a textual description of the # associations stored in the 'config' variable. self.conffile = None # 'log' is a callable that is used to record events that occur in # moosicd that might be of interest to the outside world. It takes two # arguments: (1) an integer that indicates the priority of the event # that occurred, and (2) a string that describes the event. self.log = lambda priority, message: None # <-- a dummy logger. # 'start_stop_times' is a list of times at which either a "song pause" # event or a "song play/continue" event occurred. An item in this list # with an even index represent the time (measured in the number of # seconds since the epoch) at which a "play" event occurred, while one # with an odd index are the time at which a "pause" event occurred. # These values can be used to compute the amount of time that the # current song has been playing. self.start_stop_times = [] # 'song_start_event' is the point in time (represented by a number of # seconds since the epoch) that the current song started playing. self.song_start_event = 0 # 'last_pause_event' is the point in time (in seconds since the epoch) # at which the current song was most recently paused. self.last_pause_event = 0 # 'current_paused_time' is the length of the interval of time (measured # in number of seconds) that the current song has spent in the paused # state. self.current_paused_time = 0 # 'last_queue_update' is the time at which the song queue was last # modified. It a floating-point number that represents time as the # number of seconds since the epoch. self.last_queue_update = time.time() # 'ignore_song_finish' is a flag that is used to indicate to the queue # consumer that the current song should not be put in the history when # the song finishes playing. self.ignore_song_finish = False # 'extra_moosic_server' is a SocketServer object that is used only when # moosicd is listening for requests through both a Unix socket and an IP # socket. self.extra_moosic_server = None # 'music_root' is the name of the directory in which the file listing # API functions are permitted. If this is the empty string, then file # listing will not be permitted at all. self.music_root = os.path.realpath('/data/music') # DEBUG #self.music_root = None del self.doing_init def __setattr__(self, name, value): # Override __setattr__ to prevent adding new attributes that weren't # created in the constructor. if not hasattr(self, name) and not hasattr(self, 'doing_init'): raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name)) else: self.__dict__[name] = value def getstate(self): # Only save certain attributes. saved_state = {} attrs_to_save = ( 'song_queue', 'qrunning', 'loop_mode', 'history', 'max_hist_size', ) for attr in attrs_to_save: saved_state[attr] = getattr(self, attr) # Normalize boolean objects into the standard boolean type. # (Avoid saving unusual boolean objects like xmlrpclib.Boolean.) saved_state['qrunning'] = bool(saved_state['qrunning']) saved_state['loop_mode'] = bool(saved_state['loop_mode']) # The current song will be put back at the head of the queue if and only # if the queue consumer is active. if self.qrunning and self.current_song: saved_state["song_queue"] = [self.current_song] + saved_state["song_queue"] return saved_state def setstate(self, saved_state): # Merge the saved attributes in with the existing ones. self.__dict__.update(saved_state) data = DataStore() def readConfig(filename): """Parses a moosicd configuration file and returns the data within. The "filename" argument specifies the name of the file from which to read the configuration. This function returns a list of 2-tuples which associate regular expression objects to the commands that will be used to play files whose names are matched by the regexps. """ import re, fileinput config = [] expecting_regex = True regex = None command = None for line in fileinput.input(filename): # skip empty lines if re.search(r'^\s*$', line): continue # skip lines that begin with a '#' character if re.search('^#', line): continue # chomp off trailing newline if line[-1] == '\n': line = line[:-1] # the first line in each pair is interpreted as a regular expression # note that case is ignored. it would be nice if there was an easy way # for the user to choose whether or not case should be ignored. if expecting_regex: regex = re.compile(line) expecting_regex = False # the second line in each pair is interpreted as a command else: command = string.split(line) config.append((regex, command)) expecting_regex = True return config def strConfig(config): """Stringifies a list of moosicd filetype-player associations. This function converts the list used to store filetype-to-player associations to a string. The "config" parameter is the list to be converted. The return value is a good-looking string that represents the contents of the list of associations. """ s = '' for regex, command in config: s = s + regex.pattern + '\n\t' + string.join(command) + '\n' return s def getConfigFile(confdir): '''A procedure which prepares the moosicd configuration file. The name of the configuration file is returned. The "confdir" parameter is the name of the configuration directory. The configuration directory and configuration file will be created if they don't already exist. ''' conffile = os.path.join(confdir, 'config') if not os.path.exists(confdir): try: os.makedirs(confdir, 0700) except IOError, e: sys.exit('Error creating directory "%s": %s' % (confdir, e.strerror)) if not os.path.exists(conffile): try: f = open(conffile, 'w') f.write('''\ # %s # This file associates filetypes with commands which play them. # # The format of this file is as follows: Every pair of lines forms a unit. # The first line in a pair is a regular expression that will be matched against # items in the play list. The second line in a pair is the command that will # be used to play any items that match the regular expression. The name of the # item to be played will be appended to the end of this command line. # # The command will not be interpreted by a shell, so don't bother trying to use # shell variables or globbing or I/O redirection, and be mindful of how you use # quotes and parentheses. If you need any of these fancy features, wrap up the # command in a real shell script (and remember to use an "exec" statement to # invoke the program that does the actual song playing, otherwise Moosic won't # be able to do things like stop or pause the song). # # Blank lines and lines starting with a '#' character are ignored. Regular # expressions specified earlier in this file take precedence over those # specified later. (?i)\.mp3$ mpg123 -q (?i)\.midi?$ timidity -idq (?i)\.(mod|xm|s3m|stm|it|mtm|669|amf)$ mikmod -q (?i)\.(wav|8svx|aiff|aifc|aif|au|cdr|maud|sf|snd|voc)$ sox $item -t ossdsp /dev/dsp (?i)\.ogg$ ogg123 -q (?i)\.m3u$ moosic -o pl-add (?i)^cda://(\S+) takcd \1''' % conffile) f.close() except IOError, e: sys.exit('Error creating configuration file "%s": %s' % (conffile, e)) if not os.path.isfile(conffile): sys.exit('Error: "%s" exists, but is not a regular file.\n' "I can't run without a proper configuration file." % (conffile)) return conffile def split_range(range): '''A helper function that handles the ranges used by several Moosic methods. ''' if len(range) == 0: start, end = 0, len(data.song_queue) elif len(range) == 1: start, end = range[0], len(data.song_queue) elif len(range) == 2: start, end = range[0], range[1] else: raise TypeError("Invalid range argument: %s" % range) return start, end class Log: """A very simple logging facility. All messages logged with this facility have a priority associated with them. The valid priorities are defined by the following class constants (listed in order of increasing priority): DEBUG, NOTICE, WARNING, and ERROR. The "priorityNames" dictionary is meant for mapping these constants into strings that contain their respective names, and should not be modified. """ # The following class attributes are constants used to specify the priority # of a log message. DEBUG = 3 NOTICE = 2 WARNING = 1 ERROR = 0 priorityNames = { DEBUG:"DEBUG", NOTICE:"NOTICE", WARNING:"WARNING", ERROR:"ERROR" } def __init__(self, file, loglevel=WARNING): """Creates a Log object. "file" is the file-like object to which log messages will be written. "loglevel" is the minimum priority level needed for a message to be logged. Messages with a priority lower than this will be ignored. One of the constants defined in this class (i.e. DEBUG, NOTICE, WARNING, or ERROR) should be used as the value of this parameter. """ if not hasattr(file, 'write') or not callable(file.write): raise TypeError("argument 1: expected a file object") self.logfile = file self.loglevel = loglevel def __call__(self, priority, message): """Sends a message to be logged by the Log object. "priority" is a measure of the message's urgency. One of the constants defined in this class (i.e. DEBUG, NOTICE, WARNING, or ERROR) should be used as the value of this parameter. If the priority of the message is lower than the threshold associated with the Log object, then the message will not be logged. "message" is a string that contains the text of the message. The return value is the object passed as the "message" parameter. """ orig_message = message if priority <= self.loglevel: # Portability note: Unix-style newlines are assumed. message = string.replace(message, "\n", "\n\t") now = time.strftime('%I:%M:%S%p', time.localtime(time.time())) message = "%s [%s] %s\n" % (now, self.priorityNames[priority], message) self.logfile.write(message) return orig_message class UnixMoosicRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): """An adaptation of SimpleXMLRPCRequestHandler for use with Unix sockets. """ # The Nagle algorithm issue doesn't apply to non-TCP sockets. disable_nagle_algorithm = False def address_string(self): """Returns the client address in a format appropriate for logging. BaseHTTPServer.BaseHTTPRequestHandler (which is a base class of SimpleXMLRPCServer.SimpleXMLRPCRequestHandler) assumes that its client_address attribute is a (host, port) tuple, which is a false assumption if we are using a socket that doesn't use the INET address family. So, this method has been overridden. """ return self.client_address class TcpMoosicRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): """An adaptation of SimpleXMLRPCRequestHandler for use with TCP sockets. """ # Actually, no adaptation needs to be done. pass class UnixMoosicServer(SocketServer.UnixStreamServer, SimpleXMLRPCServer.SimpleXMLRPCServer): """A server that responds to Moosic requests via a Unix (local) socket. """ # Portability note: By default, Unix domain sockets are used to implement # interprocess communication between the client and the server. This prevents # this program from working on (most, if not all) non-Unix systems. def __init__(self, addr, logRequests=False): SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, addr, requestHandler=UnixMoosicRequestHandler, logRequests=logRequests) def handle_error(self, request, client_address): """Handle an error gracefully.""" log_exception(request, client_address) class TcpMoosicServer(SimpleXMLRPCServer.SimpleXMLRPCServer): """A server that responds to Moosic requests via TCP/IP. """ def __init__(self, addr, logRequests=False): SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, addr, requestHandler=TcpMoosicRequestHandler, logRequests=logRequests) def handle_error(self, request, client_address): """Handle an error gracefully.""" log_exception(request, client_address) class ThreadedUnixMoosicServer(SocketServer.ThreadingMixIn, SocketServer.UnixStreamServer, SimpleXMLRPCServer.SimpleXMLRPCServer): """A server that responds to Moosic requests via a Unix (local) socket. """ # Portability note: By default, Unix domain sockets are used to implement # interprocess communication between the client and the server. This prevents # this program from working on (most, if not all) non-Unix systems. def __init__(self, addr, logRequests=False): SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, addr, requestHandler=UnixMoosicRequestHandler, logRequests=logRequests) def process_request(self, request, client_address): """Start a new thread to process the request. The new thread is specifically set as a non-daemon thread to ensure that the request is properly completed, even if the rest of the server is shutting down. """ # Apparently, some platforms (such as various forms of BSD) won't start # a new thread until the previous thread has performed a blocking # operation (such as a trivial call to sleep()): # # [scroll down to: "None of my threads seem to run: why?"] time.sleep(0.001) t = threading.Thread(target=self.process_request_thread, args=(request, client_address)) t.setDaemon(False) t.start() def handle_error(self, request, client_address): """Handle an error gracefully.""" log_exception(request, client_address) class ThreadedTcpMoosicServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer): """A server that responds to Moosic requests via TCP/IP. """ def __init__(self, addr, logRequests=False): SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, addr, requestHandler=TcpMoosicRequestHandler, logRequests=logRequests) def process_request(self, request, client_address): """Start a new thread to process the request. The new thread is specifically set as a non-daemon thread to ensure that the request is properly completed, even if the rest of the server is shutting down. """ # A trivial call to sleep() is made here for the same reason as it is # done in the analogous method of UnixMoosicServer. time.sleep(0.001) t = threading.Thread(target=self.process_request_thread, args=(request, client_address)) t.setDaemon(False) t.start() def handle_error(self, request, client_address): """Handle an error gracefully.""" log_exception(request, client_address) def log_exception(request, client_address): """Makes note of an exception in the error log. This logs a traceback of the error (with "ERROR" priority) and continues. """ exception = sys.exc_info()[1] # Ignore "broken pipe" errors. if isinstance(exception, IOError) and exception[0] == errno.EPIPE: return data.log(Log.ERROR, '-'*40 + '\n' + \ 'Exception happened during processing of request from ' + \ str(client_address) + '\n' + \ ''.join(traceback.format_exception(*sys.exc_info())) + \ '-'*40 ) moosic-1.5.6/moosic/server/methods.py0000644000175000017500000013517211655333656020370 0ustar danieldaniel00000000000000# moosic.server.methods.py - defines and registers the Moosic server methods # # This is free and unencumbered software released into the public domain. # # Anyone is free to copy, modify, publish, use, compile, sell, or # distribute this software, either in source code form or as a compiled # binary, for any purpose, commercial or non-commercial, and by any # means. # # In jurisdictions that recognize copyright laws, the author or authors # of this software dedicate any and all copyright interest in the # software to the public domain. We make this dedication for the benefit # of the public at large and to the detriment of our heirs and # successors. We intend this dedication to be an overt act of # relinquishment in perpetuity of all present and future rights to this # software under copyright law. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # # For more information, please refer to API_MAJOR_VERSION = 1 API_MINOR_VERSION = 8 # Import from the standard library. import xmlrpclib, time, os, signal, random, re, errno, operator from xmlrpclib import Boolean, Binary, True, False # Define the True and False constants if they don't already exist. try: True except NameError: True = 1 try: False except NameError: False = 0 # Import from my own modules. import xmlrpc_registry from xmlrpc_registry import INT, BOOLEAN, DOUBLE, STRING, ARRAY, STRUCT, BASE64 import moosic.utilities from moosic.utilities import grep, antigrep, splitpath, is_overlapping import moosic.server.support from moosic.server.support import data, Log, split_range from moosic import VERSION moosicd_methods = xmlrpc_registry.Registry() def insert(items, position): '''Inserts items at a given position in the queue. Arguments: The first argument is an array of (base64-encoded) strings, representing the items to be added. * The second argument specifies the position in the queue where the items will be inserted. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server has no idea what the client's current working directory is. Return value: Nothing meaningful. ''' for i in items: if not hasattr(i, 'data'): raise TypeError("Objects of type '%s' cannot be inserted." % \ i.__class__.__name__) p = position # Make a shorter alias so that we can fit into 80 colums. data.lock.acquire() try: data.song_queue[p:p] = filter(None, [str(i.data) for i in items]) finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(insert, [[BOOLEAN, ARRAY, INT]]) def replace(items): '''Replaces the contents of the queue with the given items. This is equivalent to calling clear() and prepend() in succession, except that this operation is atomic. Argument: An array of (base64-encoded) strings, representing the items to be added. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server isn't aware of the client's current working directory. Return value: Nothing meaningful. ''' for i in items: if not hasattr(i, 'data'): raise TypeError("Objects of type '%s' cannot be inserted." % \ i.__class__.__name__) data.lock.acquire() try: data.song_queue = filter(None, [str(i.data) for i in items]) finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(replace, [[BOOLEAN, ARRAY]]) def replace_range(range, items): '''Replaces a slice of the contents of the queue with the given items. This is equivalent to calling cut() and prepend() in succession, except that this operation is atomic. Argument: The first is an array of integers that represents a range. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. * The second argument is an array of (base64-encoded) strings, representing the items to be added. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server isn't aware of the client's current working directory. Return value: Nothing meaningful. ''' start, end = split_range(range) for i in items: if not hasattr(i, 'data'): raise TypeError("Objects of type '%s' cannot be inserted." % \ i.__class__.__name__) data.lock.acquire() try: data.song_queue[start:end] = filter(None, [str(i.data) for i in items]) finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(replace, [[BOOLEAN, ARRAY]]) def append(items): '''Adds items to the end of the queue. Argument: An array of (base64-encoded) strings, representing the items to be added. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server has no idea what the client's current working directory is. Return value: Nothing meaningful. ''' end = len(data.song_queue) return insert(items, end) moosicd_methods.register(append, [[BOOLEAN, ARRAY]]) def prepend(items): '''Adds items to the beginning of the queue. Argument: An array of (base64-encoded) strings, representing the items to be added. * When adding local filenames to the queue, only absolute pathnames should be used. Using relative pathnames would be foolish because the server has no idea what the client's current working directory is. Return value: Nothing meaningful. ''' return insert(items, 0) moosicd_methods.register(prepend, [[BOOLEAN, ARRAY]]) def clear(): '''Removes all items from the queue. Arguments: None. Return value: Nothing meaningful. ''' data.lock.acquire() try: data.song_queue = [] finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(clear, [[BOOLEAN]]) def sub_all(pattern, replace, range=()): '''Performs a global regular expression substitution on the items in the queue. Arguments: The first is a (base64-encoded) regular expression that specifies the text to be replaced. * The second argument is the (base64-encoded) string that will be used to replace all occurrences of the regular expression within each queue item. Any backslash escapes in this string will be processed, including special character translation (e.g. "\\n" to newline) and backreferences to the substrings matched by individual groups in the pattern. * Optionally, an array of integers may be given as a third argument. This argument represents a range to which the substitution will be limited. This range is interpreted in the same way as the range argument in other Moosic methods. * If performing a replacement changes an item in the queue into the empty string, then it is removed from the queue. Return value: Nothing meaningful. ''' start, end = split_range(range) if hasattr(pattern, 'data'): pattern = str(pattern.data) if hasattr(replace, 'data'): replace = str(replace.data) pattern = re.compile(pattern) data.lock.acquire() try: data.song_queue[start:end] = filter(None, [pattern.sub(replace, item) for item in data.song_queue[start:end]]) finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(sub_all, [[BOOLEAN, BASE64, BASE64], [BOOLEAN, BASE64, BASE64, ARRAY]]) def sub(pattern, replace, range=()): '''Performs a regular expression substitution on the items in the queue. Arguments: The first is a (base64-encoded) regular expression that specifies the text to be replaced. * The second argument is the (base64-encoded) string that will be used to replace the first occurrence of the regular expression within each queue item. Any backslash escapes in this string will be processed, including special character translation (e.g. "\\n" to newline) and backreferences to groups within the match. * Optionally, an array of integers may be given as a third argument. This argument represents a range to which the substitution will be limited. This range is interpreted in the same way as the range argument in other Moosic methods. * If performing a replacement changes an item in the queue into the empty string, then it is removed from the queue. Return value: Nothing meaningful. ''' start, end = split_range(range) if hasattr(pattern, 'data'): pattern = str(pattern.data) if hasattr(replace, 'data'): replace = str(replace.data) pattern = re.compile(pattern) data.lock.acquire() try: data.song_queue[start:end] = filter(None, [pattern.sub(replace, item, 1) for item in data.song_queue[start:end]]) finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(sub, [[BOOLEAN, BASE64, BASE64], [BOOLEAN, BASE64, BASE64, ARRAY]]) def shuffle(range=()): '''Rearrange the contents of the queue into a random order. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is affected. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. ''' start, end = split_range(range) altered_slice = data.song_queue[start:end] random.shuffle(altered_slice) data.lock.acquire() try: data.song_queue[start:end] = altered_slice finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(shuffle, [[BOOLEAN], [BOOLEAN, ARRAY]]) def sort(range=()): '''Arranges the contents of the queue into sorted order. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is affected. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. ''' start, end = split_range(range) altered_slice = data.song_queue[start:end] altered_slice.sort() data.lock.acquire() try: data.song_queue[start:end] = altered_slice finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(sort, [[BOOLEAN], [BOOLEAN, ARRAY]]) def reverse(range=()): '''Reverses the order of the items in the queue. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is affected. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. ''' start, end = split_range(range) altered_slice = data.song_queue[start:end] altered_slice.reverse() data.lock.acquire() try: data.song_queue[start:end] = altered_slice finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(reverse, [[BOOLEAN], [BOOLEAN, ARRAY]]) def putback(): '''Places the currently playing song at the beginning of the queue. Arguments: None. Return value: Nothing meaningful. ''' if data.current_song: data.lock.acquire() try: data.song_queue.insert(0, data.current_song) finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(putback, [[BOOLEAN]]) def current(): '''Returns the name of the currently playing song. Arguments: None. Return value: The name of the currently playing song. ''' return Binary(data.current_song) moosicd_methods.register(current, [[BASE64]]) def list(range=()): '''Lists the song queue's contents. If a range is specified, only the items that fall within that range are listed. Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is returned. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: An array of (base64-encoded) strings, representing the selected range from the song queue's contents. ''' start, end = split_range(range) return [Binary(i) for i in data.song_queue[start:end]] moosicd_methods.register(list, [[ARRAY], [ARRAY, ARRAY]]) def indexed_list(range=()): '''Lists the song queue's contents. If a range is specified, only the items that fall within that range are listed. This differs from list() only in its return value, and is useful when you want to know the starting position of your selected range within the song queue (which can be different than the starting index of the specified range if, for example, the starting index is a negative integer). Arguments: Either none, or an array of integers that represents a range. * If no range is given, the whole list is returned. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: A struct with two elements. This first is "list", an array of (base64-encoded) strings, representing the selected range from the song queue's contents. The second is "start", an integer index value that represents the position of the first item of the returned list in the song queue. ''' start, end = split_range(range) list = [Binary(i) for i in data.song_queue[start:end]] start_index = start if start_index < 0: start_index = len(data.song_queue) + start_index if start_index < 0: start_index = 0 return {'start':start_index, 'list':list} moosicd_methods.register(indexed_list, [[STRUCT], [STRUCT, ARRAY]]) #def packed_list(range=()): # '''Lists the song queue's contents. If a range is specified, only the # items that fall within that range are listed. # # This differs from list() only in its return value, and is useful since it is # much faster when the song queue is very large. # # Arguments: Either none, or an array of integers that represents a range. # * If no range is given, the whole list is returned. # * If the range contains a single integer, it will represent all members # of the queue whose index is greater than or equal to the value of the # integer. # * If the range contains two integers, it will represent all members of # the queue whose index is greater than or equal to the value of the # first integer and less than the value of the second integer. # * If the range contains more than two integers, an error will occur. # Return value: A single (base64-encoded) string which contains the selected # range from the song queue's contents in a "packed" form. That is, if # you split the return value (after base64-decoding it), using '\\0' as # the delimiter, a list of the selected queue items will result. ('\\0' # denotes the character whose ASCII ordinal is 0.) # ''' # start, end = split_range(range) # return Binary('\0'.join(data.song_queue[start:end])) #moosicd_methods.register(packed_list, [[BASE64], [BASE64, ARRAY]]) def halt_queue(): '''Stops any new songs from being played. Use run_queue() to reverse this state. Arguments: None. Return value: Nothing meaningful. ''' data.lock.acquire() try: data.qrunning = False finally: data.lock.release() return True moosicd_methods.register(halt_queue, [[BOOLEAN]]) haltqueue = halt_queue moosicd_methods.register(halt_queue, [[BOOLEAN]], "haltqueue") def run_queue(): '''Allows new songs to be played again after halt_queue() has been called. Arguments: None. Return value: Nothing meaningful. ''' data.lock.acquire() try: data.qrunning = True finally: data.lock.release() return True moosicd_methods.register(run_queue, [[BOOLEAN]]) runqueue = run_queue moosicd_methods.register(run_queue, [[BOOLEAN]], "runqueue") def pause(): '''Pauses the currently playing song. Arguments: None. Return value: Nothing meaningful. ''' data.lock.acquire() try: if data.current_song and data.player_pid: try: os.kill(data.player_pid, signal.SIGTSTP) time.sleep(0.10) os.kill(data.player_pid, signal.SIGSTOP) except OSError, e: e.strerror += ' (in method "pause")' raise e if data.paused == False: data.last_pause_event = time.time() #data.start_stop_times.append( time.time() ) # old algorithm data.paused = True finally: data.lock.release() return True moosicd_methods.register(pause, [[BOOLEAN]]) def unpause(): '''Unpauses the current song. Arguments: None. Return value: Nothing meaningful. ''' data.lock.acquire() try: if data.current_song and data.player_pid: try: os.kill(data.player_pid, signal.SIGCONT) except OSError, e: if e.errno == errno.ESRCH: data.player_pid = None else: e.strerror += ' (in method "unpause")' raise e if data.paused == True: data.current_paused_time += (time.time() - data.last_pause_event) #data.start_stop_times.append( -time.time() ) # old algorithm data.paused = False finally: data.lock.release() return True moosicd_methods.register(unpause, [[BOOLEAN]]) def toggle_pause(): '''Pauses the current song if it is playing, and unpauses if it is paused. Arguments: None. Return value: Nothing meaningful. ''' data.lock.acquire() try: if data.current_song and data.player_pid: if data.paused: unpause() else: pause() finally: data.lock.release() return True moosicd_methods.register(toggle_pause, [[BOOLEAN]]) def is_looping(): '''Tells you whether loop mode is on or not. If loop mode is on, songs are returned to the end of the song queue after they finish playing. If loop mode is off, songs that have finished playing are not returned to the queue. Arguments: None. Return value: True if loop mode is set, False if it is not. ''' return data.loop_mode moosicd_methods.register(is_looping, [[BOOLEAN]]) def set_loop_mode(value): '''Turns loop mode on or off. If loop mode is on, songs are returned to the end of the song queue after they finish playing. If loop mode is off, songs that have finished playing are not returned to the queue. Arguments: True if you want to turn loop mode on, False if you want to turn it off. Return value: Nothing meaningful. ''' data.lock.acquire() try: if value: data.loop_mode = True else: data.loop_mode = False finally: data.lock.release() return True moosicd_methods.register(set_loop_mode, [[BOOLEAN, BOOLEAN]]) def toggle_loop_mode(): '''Turns loop mode on if it is off, and turns it off if it is on. If loop mode is on, songs are returned to the end of the song queue after they finish playing. If loop mode is off, songs that have finished playing are not returned to the queue. Arguments: None. Return value: Nothing meaningful. ''' data.lock.acquire() try: if data.loop_mode: data.loop_mode = False else: data.loop_mode = True finally: data.lock.release() return True moosicd_methods.register(toggle_loop_mode, [[BOOLEAN]]) def skip(): '''Skips the rest of the current song to play the next song in the queue. This only has an effect if there actually is a current song. Arguments: None. Return value: Nothing meaningful. ''' if data.current_song and data.player_pid: # ogg123 behaves very stupidly when it gets a TERM signal, so it needs # to be handled specially. This logic for determining when to apply this # special case is complicated, and interferes with readability, and is # yucky and evil and sloppy, and makes assumptions which are not # necessarily true. I hate you, ogg123. command = None for regex, cmd in data.config: if regex.search(data.current_song): command = cmd[:] break try: if command and command[0] == 'ogg123': os.kill(data.player_pid, signal.SIGINT) else: os.kill(data.player_pid, signal.SIGTERM) except OSError, e: e.strerror += ' (in method "skip")' raise e # Unpause the song player, so that termination takes place right away # (instead of having to wait until the song player somehow happens to # receive a CONT signal). unpause() return True moosicd_methods.register(skip, [[BOOLEAN]]) def next(howmany=1): '''Stops the current song (if any), and jumps ahead to a song that is currently in the queue. The skipped songs are recorded in the history as if they had been played. When called without arguments, this behaves very much like the skip() method, except that it will have an effect even if nothing is currently playing. Arguments: A single integer that tells how far forward into the song queue to advance. A value of 1 will cause the first song in the queue to play, 2 will cause the second song in the queue to play, and so on. If no argument is given, a value of 1 is assumed. Return value: Nothing meaningful. ''' data.lock.acquire() try: #if not data.current_song: howmany -= 1 queue_was_running = is_queue_running() stop() for i in range(howmany): if not data.song_queue: break song = data.song_queue.pop(0) if data.loop_mode: data.song_queue.append(song) data.history.append((song, data.song_start_event, time.time())) while len(data.history) > data.max_hist_size: data.history.pop(0) finally: data.last_queue_update = time.time() if queue_was_running: run_queue() data.lock.release() return True moosicd_methods.register(next, [[BOOLEAN], [BOOLEAN, INT]]) def previous(howmany=1): '''Stops the current song (if any), removes the most recently played song from the history, and puts these songs at the head of the queue. When loop mode is on, the songs at the tail of the song queue are used instead of the most recently played songs in the history. Arguments: A single integer that tells how far back in the history list to retreat. A value of 1 will cause the most recent song to play, 2 will cause the second most recent song to play, and so on. If no argument is given, a value of 1 is assumed. Return value: Nothing meaningful. ''' data.lock.acquire() try: queue_was_running = is_queue_running() stop() for i in range(howmany): if not data.loop_mode: if not data.history: break data.song_queue.insert(0, data.history.pop()[0]) else: data.song_queue.insert(0, data.song_queue.pop()) finally: data.last_queue_update = time.time() if queue_was_running: run_queue() data.lock.release() return True moosicd_methods.register(previous, [[BOOLEAN], [BOOLEAN, INT]]) def stop(): '''Stops playing the current song and stops new songs from playing. The current song is returned to the head of the song queue and is not recorded in the history list. If loop mode is on, the current song won't be placed at the end of the song queue when it is stopped. Arguments: None. Return value: Nothing meaningful. ''' putback() halt_queue() if data.current_song: data.ignore_song_finish = True skip() return True moosicd_methods.register(stop, [[BOOLEAN]]) def remove(regexp, range=()): '''Removes all items that match the given regular expression. Arguments: A regular expression that specifies which items to remove. * Optionally, an array of integers may be given as a second argument. This argument represents a range to which the removal will be limited. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. ''' start, end = split_range(range) data.lock.acquire() try: if hasattr(regexp, 'data'): regexp = regexp.data data.song_queue[start:end] = antigrep(regexp, data.song_queue[start:end]) finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(remove, [[BOOLEAN, BASE64], [BOOLEAN, BASE64, ARRAY]]) def filter_(regexp, range=()): '''Removes all items that don't match the given regular expression. Arguments: A regular expression that specifies which items to keep. * Optionally, an array of integers may be given as a second argument. This argument represents a range to which the filtering will be limited. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. ''' start, end = split_range(range) data.lock.acquire() try: if hasattr(regexp, 'data'): regexp = regexp.data data.song_queue[start:end] = grep(regexp, data.song_queue[start:end]) finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(filter_, [[BOOLEAN, BASE64], [BOOLEAN, BASE64, ARRAY]], 'filter') def move(range, dest): '''Moves a range of items to a new position within the queue. Arguments: The first argument is an array of integers that represents a range of items to be moved. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. * The second argument, "destination", specifies the position in the queue where the items will be moved. Return value: Nothing meaningful. ''' start, end = split_range(range) # Use an out-of-bound piece of data to mark the old positions of moved # items. Since the playlist normally only contains strings, any non-string # value will work as an effective marker. mark = None data.lock.acquire() try: # Copy the items to be moved. stuff_to_move = data.song_queue[start:end] # "Delete" the items from their old position by replacing them with # marker values. Regular removal isn't done at this point because we # don't want to invalidate the meaning of our destination index. data.song_queue[start:end] = [mark]*len(stuff_to_move) # Place the collected items at their destination. data.song_queue[dest:dest] = stuff_to_move # Remove the markers. data.song_queue = [item for item in data.song_queue if item is not mark] finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(move, [[BOOLEAN, ARRAY, INT]]) def move_list(indices, dest): '''Moves the items referenced by a list of positions to a new position. Arguments: The first argument is an array of integers that represents a list of the positions of the items to be moved. * The second argument, "destination", specifies the position in the queue where the items will be moved. Return value: Nothing meaningful. ''' # Use an out-of-bound piece of data to mark the old positions of moved # items. Since the playlist normally only contains strings, any non-string # value will work as an effective marker. mark = None data.lock.acquire() try: stuff_to_move = [] for i in indices: # Copy each item to be moved. stuff_to_move.append(data.song_queue[i]) # "Delete" each item from its old position by replacing it with a # marker. Regular removal isn't done at this point because we don't # want to invalidate the meaning of our destination index. data.song_queue[i] = mark # Place the collected items at their destination. data.song_queue[dest:dest] = stuff_to_move # Remove the markers. data.song_queue = [item for item in data.song_queue if item is not mark] finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(move_list, [[BOOLEAN, ARRAY, INT]]) def swap(range_A, range_B): '''Swaps the items contained in one range with the items contained in the other range. Return value: Nothing meaningful. ''' A = split_range(range_A) B = split_range(range_B) data.lock.acquire() try: # Normalize the case where negative numbers are used as range indices. n = len(data.song_queue) if A[0] < 0: A[0] = n - A[0] if A[1] < 0: A[1] = n - A[1] if B[0] < 0: B[0] = n - B[0] if B[1] < 0: B[1] = n - B[1] # Forbid overlapping ranges. if is_overlapping(A, B, len(data.song_queue)): raise ValueError("Overlapping ranges may not be swapped: %s %s" % (A, B)) # Make sure range A is closer to the head of the queue than range B. if A > B: A, B = B, A # Split the queue into various slices, delineated by the given ranges. prefix = data.song_queue[:A[0]] slice_A = data.song_queue[A[0]:A[1]] infix = data.song_queue[A[1]:B[0]] slice_B = data.song_queue[B[0]:B[1]] suffix = data.song_queue[B[1]:] # Piece the slices back together, swapping A with B. data.song_queue = prefix + slice_B + infix + slice_A + suffix finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(swap, [[BOOLEAN, ARRAY, ARRAY]]) def cut(range): '''Remove all queued items that fall within the given range. Arguments: An array of integers that represents a range. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. ''' start, end = split_range(range) data.lock.acquire() try: del data.song_queue[start:end] finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(cut, [[BOOLEAN, ARRAY]]) def cut_list(indices): '''Removes the items referenced by a list of positions within the queue. Arguments: An array of integers that represents a list of the positions of the items to be removed. Return value: Nothing meaningful. ''' mark = None data.lock.acquire() try: for i in indices: # Replace the item at each index with a marker. This is done instead # of removing the item from the list in order to prevent the # invalidation of later index values. data.song_queue[i] = mark # Remove the markers now that all indices have been handled. data.song_queue = [item for item in data.song_queue if item is not mark] finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(cut_list, [[BOOLEAN, ARRAY]]) def crop(range): '''Remove all queued items that do not fall within the given range. Arguments: An array of integers that represents a range. * If the range contains a single integer, it will represent all members of the queue whose index is greater than or equal to the value of the integer. * If the range contains two integers, it will represent all members of the queue whose index is greater than or equal to the value of the first integer and less than the value of the second integer. * If the range contains more than two integers, an error will occur. Return value: Nothing meaningful. ''' start, end = split_range(range) data.lock.acquire() try: data.song_queue = data.song_queue[start:end] finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(crop, [[BOOLEAN, ARRAY]]) def crop_list(indices): '''Removes all items except for those referenced by a list of positions. Arguments: An array of integers that represents a list of the positions of the items to be kept. Return value: Nothing meaningful. ''' data.lock.acquire() try: data.song_queue = [data.song_queue[i] for i in indices] finally: data.last_queue_update = time.time() data.lock.release() return True moosicd_methods.register(crop_list, [[BOOLEAN, ARRAY]]) def history(limit=0): '''Returns a list of the items that were recently played. Arguments: If a positive integer argument is given, then no more than that number of entries will be returned. If a number is not specified, or if zero is given, then the entire history is returned. The result is undefined if a negative integer argument is given (but does not raise an exception). Return value: An array of triples, each representing a song that was played along with the times that it started and finished playing. * The first member of the pair is a (base64-encoded) string which represents the song that was previously played. * The second member of the pair is a floating point number which represents the time that the song started playing in seconds since the epoch. * The third member of the pair is a floating point number which represents the time that the song finished playing in seconds since the epoch. ''' return [(Binary(item), starttime, endtime) for item, starttime, endtime in data.history[-limit:]] moosicd_methods.register(history, [[ARRAY], [ARRAY, INT]]) def get_history_limit(): '''Gets the limit on the size of the history list stored in memory. Arguments: None. Return value: The maximum number of history entries that the server will remember. ''' return data.max_hist_size moosicd_methods.register(get_history_limit, [[INT]]) def set_history_limit(size): '''Sets the limit on the size of the history list stored in memory. This will irrevocably discard history entries if the new limit is lower than the current size of the history list. Arguments: The new maximum number of history entries. If this value is negative, the history limit will be set to zero. Return value: Nothing meaningful. ''' data.lock.acquire() try: data.max_hist_size = int(size) if data.max_hist_size < 0: data.max_hist_size = 0 while len(data.history) > data.max_hist_size: data.history.pop(0) finally: data.lock.release() return True moosicd_methods.register(set_history_limit, [[BOOLEAN, INT]]) def die(): '''Tells the server to terminate itself. Arguments: None. Return value: Nothing meaningful. ''' data.quitFlag = True # Tell the queue consumer to quit. if data.current_song: data.ignore_song_finish = True skip() # Stop the current song. # There's no need to do anything to make the request handler quit, since it # will terminate as soon as the queue consumer is gone. return True moosicd_methods.register(die, [[BOOLEAN]]) def is_paused(): '''Tells you whether the current song is paused or not. Arguments: None. Return value: True if the current song is paused, otherwise False. ''' return Boolean(data.paused) moosicd_methods.register(is_paused, [[BOOLEAN]]) def is_queue_running(): '''Tells you whether the queue consumption (advancement) is activated. Arguments: None. Return value: True if new songs are going to be played from the queue after the current song is finished, otherwise False. ''' return Boolean(data.qrunning) moosicd_methods.register(is_queue_running, [[BOOLEAN]]) def queue_length(): '''Returns the number of items in the song queue. Arguments: None. Return value: The number of items in the song queue. ''' return len(data.song_queue) moosicd_methods.register(queue_length, [[INT]]) length = queue_length moosicd_methods.register(queue_length, [[INT]], "length") def current_time(): # This alternate version works in constant time, rather than depending on # the length of an arbitrarily long list of play/pause events. '''Returns the amount of time that the current song has been playing. Arguments: None. Return value: The number of seconds that the current song has been playing. ''' if not data.current_song: return 0 if data.paused: return data.last_pause_event - data.song_start_event - data.current_paused_time else: return time.time() - data.song_start_event - data.current_paused_time moosicd_methods.register(current_time, [[DOUBLE]]) def version(): '''Returns the Moosic server's version string. Arguments: None. Return value: The version string for the Moosic server. ''' return VERSION moosicd_methods.register(version, [[STRING]]) def api_version(): '''Returns the version number for the API that the server implements. Arguments: None. Return value: The version number, which is a 2-element array of integers. The first element is the major version, and the second element is the minor version. ''' return API_MAJOR_VERSION, API_MINOR_VERSION moosicd_methods.register(api_version, [[ARRAY]]) def reconfigure(): '''Tells the server to reread its player configuration file. Arguments: None. Return value: Nothing meaningful. ''' try: data.lock.acquire() try: data.config = moosic.server.support.readConfig(data.conffile) finally: data.lock.release() except IOError, e: data.log(Log.ERROR, "The configuration file could not be reloaded!\n" + "%s: %s" % (data.conffile, e)) raise e return True moosicd_methods.register(reconfigure, [[BOOLEAN]]) def showconfig(): '''Returns a textual description of the server's player configuration. Arguments: None. Return value: A (base64-encoded) string that shows which programs will be used to play the various file-types recognized by the Moosic server. ''' return Binary(moosic.server.support.strConfig(data.config)) moosicd_methods.register(showconfig, [[BASE64]]) def getconfig(): '''Returns a list of the server's filetype-player associations. Arguments: None. Return value: An array of pairs. The first element of each pair is a (base64-encoded) string that represents a regular expression pattern, and the second element is a (base64-encoded) string that represents the system command that should be used to handle songs that match the corresponding pattern. ''' # For full generality, the Binary() wrapper is used to base64-encode the # strings in this list. This is less convenient, and it seems unlikely that # either the pattern or the command would contain non-ASCII characters... # but you can never be sure. Better safe than sorry. return [(Binary(regex.pattern), Binary(' '.join(cmd))) for regex, cmd in data.config] moosicd_methods.register(getconfig, [[ARRAY]]) def no_op(): '''Does nothing, successfully. Arguments: None. Return value: Nothing meaningful. ''' return True moosicd_methods.register(no_op, [[BOOLEAN]]) def last_queue_update(): '''Returns the time at which the song queue was last modified. This method is intended for use by GUI clients that don't want to waste time downloading the entire contents of the song queue if it hasn't changed. Arguments: None. Return value: A floating-point number that represents time as the number of seconds since the epoch. ''' return data.last_queue_update moosicd_methods.register(last_queue_update, [[DOUBLE]]) # The following additions make the proxy objects for the server act like normal # Python objects when subjected to certain common operations. moosicd_methods.register(queue_length, [[INT]], "__len__") moosicd_methods.register(lambda:1, [[BOOLEAN]], "__nonzero__") #def __getitem__(index): # """This is experimental. Ignore it. # """ # return Binary(data.song_queue[index]) #moosicd_methods.register(__getitem__, [[BASE64, INT]]) ##moosicd_methods.register(__getitem__, [[BASE64, INT]], "getitem") #def __getslice__(start, stop): # """This is experimental. Ignore it. # """ # return [Binary(i) for i in data.song_queue[start:stop]] #moosicd_methods.register(__getslice__, [[ARRAY, INT]]) ##moosicd_methods.register(__getslice__, [[ARRAY, INT]], "getslice") # XXX: This is intended for debugging purposes, and should probably be disabled # when it is time to make a release. #def echo(arg): # '''Returns whatever is passed to it. # ''' # return arg # This type signature is a bit of a lie. #moosicd_methods.register(echo, [[BASE64, BASE64]]) #def debug(code): # '''Evaluates arbitrary code within the context of the server and returns the result. # # This method is very powerful and dangerous and is only intended to be used # for the purpose of debugging the server. It should be disabled in # production releases because it is a GIANT GAPING SECURITY HOLE. Needless to # say, it is not an official part of the Moosic server API. # # Just once more, for dramatic effect: # XXX GIANT GAPING SECURITY HOLE!!! XXX # ''' # if hasattr(code, 'data'): # code = str(code.data) # return Binary('%s' % eval(code)) #moosicd_methods.register(debug, [[BASE64, BASE64]]) moosic-1.5.6/moosic/server/main.py0000644000175000017500000005621011655333760017640 0ustar danieldaniel00000000000000# moosic/server/main.py - the server portion of the Moosic jukebox system. # # This is free and unencumbered software released into the public domain. # # Anyone is free to copy, modify, publish, use, compile, sell, or # distribute this software, either in source code form or as a compiled # binary, for any purpose, commercial or non-commercial, and by any # means. # # In jurisdictions that recognize copyright laws, the author or authors # of this software dedicate any and all copyright interest in the # software to the public domain. We make this dedication for the benefit # of the public at large and to the detriment of our heirs and # successors. We intend this dedication to be an overt act of # relinquishment in perpetuity of all present and future rights to this # software under copyright law. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # # For more information, please refer to import sys, os, os.path, string, time, threading, re import socket, signal, atexit import cPickle as pickle from moosic import VERSION from moosic.server.daemonize import daemonize # Define the True and False constants if they don't already exist. try: True except NameError: True = 1 try: False except NameError: False = 0 #----- The Primary Components of moosicd -----# # moosicd consists of several interacting components: the request handler, the # queue consumer, and the song player. The request handler is in charge of # listening for requests from a moosic client and reacting accordingly. The # queue consumer pops items off of the playlist and plays each of them, one # after another. The song player is what the queue consumer uses to play each # song. #----- Threads of control -----# # moosicd is a multi-threaded program, and the two most important threads are # the ones in which the queue consumer and the request handler run. The queue # consumer is run in the program's "main" thread, while the request handler is # run in a "daemon" thread that quits as soon as the main thread quits. # # The song player also represents a separate subprocess, since it forks off to # spawn a player program, but this runs in series with the queue consumer and # blocks until its child returns, so it is considered part of the queue # consumer's thread. # # Finally, each time the request handler receives a request, it creates a new # thread to execute the request so that multiple requests can be processed # simultaneously. In practice, however, multiple requests are usually not # really executing at the same time for two reasons: 1) request execution is # normally completed in a very short amount of time, 2) requests which modify # the state of the Moosic server must use locking to make other non-read-only # requests wait before executing, so that data consistency is ensured. These # threads are are specifically set to have non-daemon status so that they can # properly return a response to the client even if the other threads have # terminated. #---------- the request handler ----------# # The logic for actually handling specific requests is implemented by the # moosicd_methods object, which is initialized in the moosic.server.methods # module. The rest of the request handler is implemented by classes in the # moosic.server.support module, UnixMoosicServer and TcpMoosicServer, which are # minor adaptations of classes from the Python standard library. from moosic.server.methods import moosicd_methods from moosic.server.support import * def request_handler(server): try: server.serve_forever() except socket.error, e: data.quitFlag = True # Tell the queue consumer to quit. import errno if e[0] == errno.EINTR: sys.exit() # Ignore "Interrupted system call" exceptions. else: sys.exit(data.log(Log.ERROR, 'Socket error: %s' % e)) #---------- the song player ----------# def play(config, songname): """Plays a single music file, and returns when it's over. """ # Match the songname against the regexps in our filetype association table. command = None for regex, cmd in config: match = regex.search(songname) if match: command = cmd[:] break if not command: data.log(Log.NOTICE, 'No player could be found for "%s".' % songname) data.ignore_song_finish = True return did_replacement = False for i in range(len(command)): # Replace occurrences of "$item" in the command list. replaced = re.sub(r'\$item', songname, command[i]) if command[i] != replaced: command[i] = replaced did_replacement = True # Replace references to match groups in the command list. replaced = match.expand(command[i]) if command[i] != replaced: command[i] = replaced did_replacement = True if not did_replacement: command.append(songname) # I forget why I'm flushing stdout here, but it can't hurt. Can it? sys.stdout.flush() # Classic fork & exec to spawn the external player. # Portability note: os.fork() is only available on Unix systems. data.player_pid = os.fork() if data.player_pid == 0: # We don't want the program to grab input. fd = os.open('/dev/null', os.O_RDONLY) os.dup2(fd, sys.__stdin__.fileno()) os.close(fd) # Open up a file for logging the command's output. try: logfilename = os.path.join(data.confdir, 'player_log') if os.path.exists(logfilename): buffering = 1 # Use line-buffered output. logfile = open(logfilename, 'a', buffering) else: buffering = 1 # Use line-buffered output. logfile = open(logfilename, 'w', buffering) # Delimit each entry in the log file with a time-stamped message. now = time.strftime('%I:%M:%S%p', time.localtime(time.time())) logfile.write('%s Executing "%s"\n' % (now, string.join(command))) logfile.flush() # Capture the program's standard error stream. os.dup2(logfile.fileno(), sys.__stderr__.fileno()) # Capture the program's standard output stream. os.dup2(logfile.fileno(), sys.__stdout__.fileno()) except IOError, e: data.log(Log.ERROR, 'Cannot open player log file "%s": %s' % (e.songname, e.strerror)) # Execute the command. try: os.execvp(command[0], command) except StandardError, e: print 'Could not execute "%s": %s' % (' '.join(command), e) else: os.waitpid(data.player_pid, 0) data.player_pid = None #---------- the queue consumer ----------# def queue_consumer(): """The Moosic queue consumer. This procedure is a simple indefinite loop which is responsible for consuming the items in moosicd's queue. """ from moosic.server.methods import current_time while not data.quitFlag: if data.song_queue and data.qrunning: data.lock.acquire() try: # Pop a song off of the playlist. data.current_song = data.song_queue.pop(0) # Update internal state variables. data.last_queue_update = time.time() data.song_start_event = time.time() data.current_paused_time = 0 finally: data.lock.release() # Play the song. data.log(Log.NOTICE, 'Started playing ' + data.current_song) play(data.config, data.current_song) # Note that control does not return to this point until the # song is finished playing. data.log(Log.NOTICE, 'Finished playing ' + data.current_song + ' (total playing time: %s)' % time.strftime('%H:%M:%S', time.gmtime(current_time()))) data.lock.acquire() try: if data.ignore_song_finish: # Handle the song that just finished playing specially by # skipping certain actions. data.ignore_song_finish = False # Reset the flag now that # the special handling has # been done. else: # Return the song to the end of the queue if loop mode is on. if data.loop_mode: data.song_queue.append(data.current_song) data.last_queue_update = time.time() # Update the history to reflect the fact that the song was # played. data.history.append((data.current_song, data.song_start_event, time.time())) while len(data.history) > data.max_hist_size: data.history.pop(0) # Reset current_song to indicate that nothing is being played. if not data.quitFlag: # (unless moosicd is shutting down) data.current_song = '' finally: data.lock.release() else: time.sleep(0.05) def handleOptions(argv, defaultOpts): '''This function interprets the command-line options passed to moosicd.''' import getopt opts = defaultOpts.copy() try: options, arglist = getopt.getopt(argv, 'hvqds:c:St:T:fl', ['help', 'version', 'quiet', 'debug', 'history-size=', 'config=', 'stdout', 'tcp=', 'tcp-also=', 'foreground', 'local-only']) except getopt.GetoptError, e: sys.exit('Option processing error: %s' % e) for opt, val in options: if opt == '-h' or opt == '--help': print 'usage:', os.path.basename(argv[0]), '[options]' + ''' Options: -s, --history-size Sets the maximum size of the history list. (Default: 50) -c, --config Specifies the directory where moosicd should keep the various files that it uses. (Default: ~/.moosic/) -t, --tcp Listen to the given TCP port number for client requests instead of using the normal communication method. (Beware: this may create network security vulnerabilities.) -T, --tcp-also Listen to the given TCP port number for client requests in addition to using the normal communication method. (Beware: this may create network security vulnerabilities.) -l, --local-only Only listen for TCP connections that originate from the local computer. This only has an effect when --tcp or --tcp-also is used. -f, --foreground Stay in the foreground instead of detaching from the current terminal and going into the background. -q, --quiet Don't print any informational messages. -d, --debug Print additional informational messages. -S, --stdout Output messages to stdout instead of logging to a file. This also prevents the program from putting itself in the background and detaching from the current terminal. -v, --version Print version information and exit. -h, --help Print this help text and exit.''' sys.exit(0) if opt == '-v' or opt == '--version': print "moosicd", VERSION print """ Copyright (C) 2001-2003 Daniel Pearson This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.""" sys.exit(0) if opt == '-q' or opt == '--quiet': opts['verbosity'] = Log.ERROR if opt == '-d' or opt == '--debug': opts['verbosity'] = Log.DEBUG if opt == '-s' or opt == '--history-size': try: opts['max hist size'] = int(val) except ValueError, e: print 'Warning: %s. This option has been ignored.' % e if opt == '-c' or opt == '--config': opts['confdir'] = os.path.abspath(val) if opt == '-S' or opt == '--stdout': opts['log to stdout'] = True opts['daemonize'] = False if opt == '-f' or opt == '--foreground': opts['daemonize'] = False if opt == '-t' or opt == '--tcp': try: opts['tcp-port'] = int(val) opts['ip-socket'] = True opts['unix-socket'] = False except ValueError, e: print 'Warning: %s. This option has been ignored.' % e if opt == '-T' or opt == '--tcp-also': try: opts['tcp-port'] = int(val) opts['ip-socket'] = True opts['unix-socket'] = True except ValueError, e: print 'Warning: %s. This option has been ignored.' % e if opt == '-l' or opt == '--local-only': opts['local-only'] = True if arglist: print 'Warning: non-option command line arguments are ignored.' return opts #------------ "main" - The program's execution starts here ------------# def main(argv): # Collect appropriate data from the command-line arguments. options = {'log to stdout':False, 'daemonize':True, 'unix-socket':True, 'ip-socket':False, 'tcp-port':None, 'local-only':False, 'verbosity':Log.NOTICE, 'max hist size':data.max_hist_size, 'confdir':data.confdir } options = handleOptions(argv[1:], options) # Read the configuration file, creating it first if it doesn't already # exist. It is important to call getConfigFile() before trying to use # confdir as a valid path, since this function creates confdir in the case # when it doesn't already exist. data.confdir = options['confdir'] data.conffile = getConfigFile(data.confdir) try: data.config = readConfig(data.conffile) except IOError, e: sys.exit('Error reading configuration file "%s": %s' % (data.conffile, e)) # Initialize the logging system. logfilename = os.path.join(data.confdir, 'server_log') try: if options['log to stdout']: logfile = sys.stdout elif os.path.exists(logfilename): buffering = 1 # Use line-buffered output. logfile = open(logfilename, 'a', buffering) else: buffering = 1 # Use line-buffered output. logfile = open(logfilename, 'w', buffering) except IOError, e: sys.exit('Cannot open server log file "%s": %s' % (logfilename, e.strerror)) data.log = Log(logfile, options['verbosity']) data.log(Log.NOTICE, "Starting up.") # Load previously saved state data, if any. savefilename = os.path.join(data.confdir, 'saved_state') if os.path.exists(savefilename): try: data.setstate(pickle.load(open(savefilename))) except IOError, e: data.log(Log.WARNING, 'Cannot open saved-state file "%s": %s' % (e.filename,e.strerror)) except pickle.PickleError, e: data.log(Log.WARNING, 'Unpickling error: %s\nCannot load saved state.' % (e)) except: data.log(Log.WARNING, 'Saved-state file "%s" could not be loaded.' % (savefilename)) # Allow the max_hist_size specified in the command-line options to override # the value from the saved state. data.max_hist_size = options['max hist size'] # Create an instance of the server for listening on a Unix socket. if options['unix-socket']: server_addr = os.path.join(data.confdir, 'socket') try: data.moosic_server = UnixMoosicServer(server_addr) except socket.error, e: import errno if e[0] == errno.EADDRINUSE: try: import moosic.client.factory moosic.client.factory.LocalMoosicProxy(server_addr).no_op() # If the above hasn't thrown an exception, then there # really is a Moosic server using this socket address. sys.exit(data.log(Log.ERROR, "Error: Tried to start a new moosicd, but an instance " "of moosicd is already running.")) except socket.error, e: # The socket file at our desired address exists, but it # isn't actually being used by a Moosic server, so we'll # assume that this socket file is stale and remove it. data.log(Log.WARNING, 'Cleaning up stale socket file: "%s".'%server_addr) os.remove(server_addr) # Try to instantiate UnixMoosicServer again. try: data.moosic_server = UnixMoosicServer(server_addr) except socket.error, e: # If we still get an error, it's time to give up. We've # done the best we can do. sys.exit(data.log(Log.ERROR, 'Socket error: %s: %s' % (server_addr, e))) else: sys.exit(data.log(Log.ERROR, 'Socket error: %s: %s' % (server_addr, e))) # Create an instance of the server for listening on an IP socket. if options['ip-socket']: if options['local-only']: server_addr = ('127.0.0.1', options['tcp-port']) else: server_addr = ('', options['tcp-port']) try: data.extra_moosic_server = TcpMoosicServer(server_addr) except socket.error, e: import errno if e[0] == errno.EADDRINUSE: try: import moosic.client.factory moosic.client.factory.InetMoosicProxy(*server_addr).no_op() # If the above hasn't thrown an exception, then there # really is a server using this socket address. sys.exit(data.log(Log.ERROR, "A server is already running " "on port %d" % options['tcp-port'])) except socket.error, e: sys.exit(data.log(Log.ERROR, "localhost:%d is somehow in use already, but I cannot " "contact a server at that address." % options['tcp-port'])) else: sys.exit(data.log(Log.ERROR, 'Socket error: localhost %s: %s' % (options['tcp-port'], e))) # Deal with the case where there's no server listening on an IP socket. if not data.extra_moosic_server: data.extra_moosic_server = data.moosic_server # Deal with the case where there's no server listening on a Unix socket. if not data.moosic_server: data.moosic_server = data.extra_moosic_server # Install the methods that handle requests into the server. data.moosic_server.register_instance(moosicd_methods) data.extra_moosic_server.register_instance(moosicd_methods) # Daemonize (go into the background, detach from the terminal, etc.). if options['daemonize']: daemonize(stderr=logfilename) data.log(Log.NOTICE, "Transformed into a daemon with PID: %d" % (os.getpid())) # Set up a timer to automatically save state at regular intervals. def savestate(): "Save the Moosic server's current state to disk." try: savefilename = os.path.join(data.confdir, 'saved_state') pickle.dump(data.getstate(), open(savefilename, 'w')) except IOError, e: data.log(Log.WARNING, 'Cannot open saved-state file "%s" for writing: %s' % (e.filename, e.strerror)) except PicklingError, e: data.log(Log.WARNING, 'Pickling error: %s\nCannot save state.' % (e)) def save_timer(prev_update): if data.last_queue_update != prev_update: # Don't bother waking up the savestate() # hard disk if nothing has # changed. t = threading.Timer(300, save_timer, args=(data.last_queue_update,)) t.setDaemon(True) t.start() save_timer(0) # Set up the signal handlers and exit handler. def reconfig(signum=None, stackframe=None): from moosic.server.methods import reconfigure reconfigure() signal.signal(signal.SIGHUP, reconfig) def quit(signum=None, stackframe=None): #if stackframe: # DEBUG # import traceback # # data.log(Log.DEBUG, traceback.format_stack(stackframe)) # if signum: data.log(Log.NOTICE, "Killed by signal %d (PID: %d)." % (signum, os.getpid())) sys.exit() signal.signal(signal.SIGINT, quit) signal.signal(signal.SIGTERM, quit) signal.signal(signal.SIGUSR1, quit) signal.signal(signal.SIGUSR2, quit) def cleanup(): '''This function is called to perform any cleanup which needs to be done when moosicd shuts down. ''' data.log(Log.NOTICE, "Shutting down (PID: %d)." % (os.getpid())) # Don't accept any more requests. try: data.moosic_server.server_close() data.extra_moosic_server.server_close() except: pass # Don't leave unused socket files around. if isinstance(data.moosic_server, UnixMoosicServer): try: os.remove(data.moosic_server.server_address) except: pass # Save our current state to disk. savestate() # Kill the song player. if data.current_song: try: os.kill(data.player_pid, signal.SIGTERM) except: pass atexit.register(cleanup) # Run the request handler in a separate thread. t = threading.Thread(target=request_handler, args=(data.moosic_server,)) t.setDaemon(True) t.start() # Start a second request handler if we are serving requests from two # different transport methods. if data.extra_moosic_server is not data.moosic_server: t = threading.Thread(target=request_handler, args=(data.extra_moosic_server,)) t.setDaemon(True) t.start() # Run the queue consumer. queue_consumer() if __name__ == '__main__': main(sys.argv) moosic-1.5.6/moosic/server/daemonize.py0000644000175000017500000000471710032731101020647 0ustar danieldaniel00000000000000#!/usr/bin/env python import sys, os '''This module is used to fork the current process into a daemon. Almost none of this is necessary (or advisable) if your daemon is being started by inetd. In that case, stdin, stdout and stderr are all set up for you to refer to the network connection, and the fork()s and session manipulation should not be done (to avoid confusing inetd). Only the chdir() and umask() steps remain as useful. References: UNIX Programming FAQ 1.7 How do I get my program to act like a daemon? http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 Advanced Programming in the Unix Environment W. Richard Stevens, 1992, Addison-Wesley, ISBN 0-201-56317-7. ''' def daemonize (stdin='/dev/null', stdout='/dev/null', stderr='/dev/null', reset_umask=False): '''This forks the current process into a daemon. The stdin, stdout, and stderr arguments are file names that will be opened and be used to replace the standard file descriptors in sys.stdin, sys.stdout, and sys.stderr. These arguments are optional and default to /dev/null. Note that stderr is opened unbuffered, so if it shares a file with stdout then interleaved output may not appear in the order that you expect. ''' # Do first fork. pid = os.fork() if pid > 0: sys.exit(0) # Exit first parent. # Decouple from parent environment. os.chdir("/") if reset_umask: os.umask(0) os.setsid() # Do second fork. pid = os.fork() if pid > 0: os._exit(0) # Exit second parent, without calling cleanup handlers. # Now I am a daemon! # Redirect standard file descriptors. si = open(stdin, 'r') so = open(stdout, 'a+') se = open(stderr, 'a+', 0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) def main (): '''This is an example main function run by the daemon. This prints a count and timestamp once per second. ''' import time sys.stdout.write ('Daemon started with pid %d\n' % os.getpid() ) sys.stdout.write ('Daemon stdout output\n') sys.stderr.write ('Daemon stderr output\n') c = 0 while 1: sys.stdout.write ('%d: %s\n' % (c, time.ctime(time.time())) ) sys.stdout.flush() c = c + 1 time.sleep(1) if __name__ == "__main__": daemonize('/dev/null','/tmp/daemon.log','/tmp/daemon.log') main() moosic-1.5.6/moosic/server/xmlrpc_registry.py0000644000175000017500000001757607653557424022175 0ustar danieldaniel00000000000000# xmlrpc_registry.py - A method registry for use with xmlrpclib # Copyright (C) 2001 by Eric Kidd. All rights reserved. # Modified by Daniel J. Pearson on 24 Feb 2003 # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. import sys import xmlrpclib # Some type names for use in method signatures. INT="int" BOOLEAN="boolean" DOUBLE="double" STRING="string" DATETIME="dateTime.iso8601" BASE64="base64" ARRAY="array" STRUCT="struct" # Some error codes, borrowed from xmlrpc-c. INTERNAL_ERROR = -500 TYPE_ERROR = -501 INDEX_ERROR = -502 PARSE_ERROR = -503 NETWORK_ERROR = -504 TIMEOUT_ERROR = -505 NO_SUCH_METHOD_ERROR = -506 REQUEST_REFUSED_ERROR = -507 INTROSPECTION_DISABLED_ERROR = -508 LIMIT_EXCEEDED_ERROR = -509 INVALID_UTF8_ERROR = -510 class Registry: """A registry for XML-RPC methods supported by a server. This is based on the method registry in xmlrpc-c.""" def __init__ (self, allow_introspection=1): """Create a new method registry.""" self._allow_introspection = allow_introspection self._methods = {} self._signatures = {} self._help = {} self._default_method = None self._install_system_methods() def _install_system_methods (self): """Install the build-in methods in the system.* suite.""" self.add_method('system.listMethods', self.system_listMethods, [[ARRAY]]) self.add_method('system.methodSignature', self.system_methodSignature, [[ARRAY, STRING]]) self.add_method('system.methodHelp', self.system_methodHelp, [[STRING, STRING]]) self.add_method('system.multicall', self.system_multicall, [[ARRAY, ARRAY]]) def add_method (self, name, method, signature=None, help=None): """Add a method to the registry, with optional documentation and signature.""" # Try to default things sensibly. if help is None: help = method.__doc__ if help is None: help = '' if signature is None: signature = 'undef' # Install our new method. self._methods[name] = method self._signatures[name] = signature self._help[name] = help def register(self, func, signature=None, name=None, help=None): if name is None: name = func.__name__ if signature is None and hasattr(func, 'signature'): signature = func.signature self.add_method(name, func, signature, help) def set_default_method (self, method): """Set a default method to handle otherwise unsupported requests.""" self._default_method = method def dispatch_call (self, name, params): """Dispatch an XML-RPC request, and return the result.""" try: # Try to find our method. if self._methods.has_key(name): method = self._methods[name] else: method = self._default_method if method is None: return self._no_such_method(name) # Call our method and return the result. return apply(method, params) #except: # DEBUG # import traceback # # traceback.print_exc() # # raise # except xmlrpclib.Fault, f: if f.faultCode == TYPE_ERROR: raise TypeError(f.faultString) elif f.faultCode == INDEX_ERROR: raise IndexError(f.faultString) elif f.faultCode == NO_SUCH_METHOD_ERROR: raise AttributeError(f.faultString) elif f.faultCode == INVALID_UTF8_ERROR: raise UnicodeError(f.faultString) else: raise RuntimeError(f.faultString) _dispatch = dispatch_call def _no_such_method (self, name): """Raise a no-such-method error.""" raise xmlrpclib.Fault(NO_SUCH_METHOD_ERROR, "Method '%s' not found" % (name)) def _introspection_check (self): """Raise an error if introspection is disabled.""" if not self._allow_introspection: raise xmlrpclib.Fault(INTROSPECTION_DISABLED_ERROR, ("Introspection has been disabled on this " + "server, probably for security reasons.")) def system_listMethods (self): """Return an array of all available XML-RPC methods on this server. """ self._introspection_check() return self._methods.keys() def system_methodSignature (self, name): """Given the name of a method, return an array of legal signatures. Each signature is an array of strings. The first item of each signature is the return type, and any others items are parameter types. """ self._introspection_check() if self._signatures.has_key(name): return self._signatures[name] else: self._no_such_method(name) def system_methodHelp (self, name): """Given the name of a method, return a help string. """ self._introspection_check() if self._help.has_key(name): return self._help[name] else: self._no_such_method(name) def system_multicall (self, calls): """Process an array of calls, and return an array of results. Calls should be structs of the form {'methodName': string, 'params': array}. Each result will either be a single-item array containg the result value, or a struct of the form {'faultCode': int, 'faultString': string}. This is useful when you need to make lots of small calls without lots of round trips. """ results = [] for call in calls: try: name = call['methodName'] params = call['params'] if name == 'system.multicall': errmsg = "Recursive system.multicall forbidden" raise xmlrpclib.Fault(REQUEST_REFUSED_ERROR, errmsg) result = [self.dispatch_call(name, params)] except xmlrpclib.Fault, fault: result = {'faultCode': fault.faultCode, 'faultString': fault.faultString} except: errmsg = "%s:%s" % (sys.exc_type, sys.exc_value) result = {'faultCode': 1, 'faultString': errmsg} results.append(result) return results moosic-1.5.6/moosic/server/__init__.py0000644000175000017500000000000010243123647020426 0ustar danieldaniel00000000000000moosic-1.5.6/moosic/utilities.py0000644000175000017500000003333311655333744017424 0ustar danieldaniel00000000000000# -*- coding: iso-8859-1 -*- # utilities.py - A library of commonly useful functions and classes. # # This is free and unencumbered software released into the public domain. # # Anyone is free to copy, modify, publish, use, compile, sell, or # distribute this software, either in source code form or as a compiled # binary, for any purpose, commercial or non-commercial, and by any # means. # # In jurisdictions that recognize copyright laws, the author or authors # of this software dedicate any and all copyright interest in the # software to the public domain. We make this dedication for the benefit # of the public at large and to the detriment of our heirs and # successors. We intend this dedication to be an overt act of # relinquishment in perpetuity of all present and future rights to this # software under copyright law. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # # For more information, please refer to """A library of commonly useful functions and classes. This module contains a varied collection of functions and classes that are potentially useful in a wide range of programming tasks. It is safe to "import *" from this module. """ from __future__ import generators import re, random, string, operator, os, os.path __all__ = ('grep', 'antigrep', 'staggered_merge', 'parse_range', 'wrap', 'xmlrpc_server_doc', 'center_text', 'uniq', 'sh_escape', 'flatten', 'canLoopOver', 'isStringLike', 'isScalar', 'make_string_filter') def uniq(seq): """Returns a list of all the elements of the given sequence in their original order, but without duplicates. Unlike the Unix tool of the same name, duplicates are removed even if they aren't adjacent. This function only works if the elements of the input sequence are hashable. """ d = {} return [d.setdefault(i, i) for i in seq if i not in d] # The following four functions were written by Luther Blissett, and were taken # from recipe 1.12 in the Python Cookbook. def canLoopOver(maybeIterable): 'Tests whether an object can be looped over.' try: iter(maybeIterable) except: return False else: return True def isStringLike(obj): 'Tests whether an object behaves like a string.' try: obj+'' except TypeError: return False else: return True def isScalar(obj): 'Tests whether an object is a scalar value. Strings are considered scalar.' return isStringLike(obj) or not canLoopOver(obj) def flatten(seq, scalarp=isScalar): '''Flattens a nested sequence. For example, [[1, 2], [3, [4, 5]] becomes [1, 2, 3, 4, 5]. ''' for item in seq: if scalarp(item): yield item else: for subitem in flatten(item, scalarp): yield subitem def shuffle(seq): """Returns a shuffled version of the given sequence. (Deprecated) The returned list contains exactly the same elements as the given sequence, but in a random order. This function is much slower than the shuffle function in the random module in the standard library, and it returns a new list instead of shuffling the sequence in place. Using this function is not recommended. """ shuffled = [] seq = list(seq) while seq: shuffled.append( seq.pop( random.choice( range( len(seq) ) ) ) ) return shuffled def staggered_merge_old(*sequences): """Merges multiple sequences, interleaving their elements. (Old version.) This returns a sequence whose elements alternate between the elements of the input sequences. For example, if the input sequences are (1, 2, 3) and (11, 12, 13), then the output will be (1, 11, 2, 12, 3, 13). And if the input sequences are (a, b, c), (v, w, x, y, z), and (q, r, s, t), then the output will be (a, v, q, b, w, r, c, x, s, y, t, z). Beware that this function throws away input elements that are equal to None. This is the price that must be paid in order to merge sequences of different lengths. """ # If "sequences" is empty, then the later call to map() won't have enough # arguments. In that case, it is replaced with a 1-tuple whose single # element is the empty tuple. sequences = sequences or ((),) # step 3: filter out the "None" values. return filter( (lambda x: x is not None), # step 2: concatenate the tuples together. reduce( operator.concat, # step 1: collect the corresponding elements into a list of tuples. map(*((None,) + sequences)), # The argument list for map() must be built dynamically because # map() takes a variable number of arguments, and we want each # member of "sequences" to be a separate argument to map(). () ) ) # The astute reader might exclaim here that I could have used the builtin # zip() function instead of using map() with the identity function. # However, zip() truncates all sequences that are longer than the shortest # sequence in the series, meaning that elements can get thrown away, which # is not what I want. def staggered_merge(*sequences): """Merges multiple sequences, interleaving their elements. This returns a sequence whose elements alternate between the elements of the input sequences. For example, if the input sequences are (1, 2, 3) and (11, 12, 13), then the output will be (1, 11, 2, 12, 3, 13). And if the input sequences are (a, b, c), (v, w, x, y, z), and (q, r, s, t), then the output will be (a, v, q, b, w, r, c, x, s, y, t, z). Backward compatibility notes: This function does not throw away None values found in the input sequences, although previous versions did. Also, this function always returns a list, while previous versions tended to always return a tuple. """ if not len(sequences): return [] # base case: there's nothing to merge. else: cars = [i[0] for i in sequences if i[:1]] # each list's head item cdrs = [i[1:] for i in sequences if i[1:]] # each list's tail items return cars + staggered_merge(*cdrs) def grep(regex, seq): """Returns a list of the elements of "seq" that match the regular expression represented by "regex", which may be a string or a regular expression object. """ if isStringLike(regex): regex = re.compile(regex) search = regex.search return [i for i in seq if search(i)] def antigrep(regex, seq): """Returns a list of the elements of "seq" that do not match the regular expression represented by "regex", which may be a string or a regular expression object. """ if isStringLike(regex): regex = re.compile(regex) search = regex.search return [i for i in seq if not search(i)] def parse_range(range, start=0, end=0): '''Changes a string representing a range into the indices of the range. This function converts a string with a form similar to that of the Python array slice notation into into a pair of ints which represent the starting and ending indices of of the slice. If the string contains a pair of colon-separated integers, a 2-tuple of ints with the respective values of those integers will be returned. If the first number in this pair is omitted, then the value of the "start" argument is used. If the second number in this pair is omitted, then the value of the "end" argument is used. If the string contains a single integer, then a pair of ints with the value of that integer and its successor, respectively, will be returned. The exception to this rule is when the successor of the single integer is zero, in which case the value of the second element of the returned pair will be the value of the "end" argument. The string can optionally be bracketed by single non-digit, non-dash characters at the beginning and/or the end. The character at the beginning doesn't have to be the same as the one at the end. If either of these characters exist, they will be stripped away (and ignored) before any of the above-mentioned processing is done. If the string contains anything else, it is considered invalid and a ValueError will be thrown. ''' if not range: raise ValueError, 'Invalid range: empty string.' if not (range[0].isdigit() or range[0] in ('-', ':')): range = range[1:] if not (range[-1].isdigit() or range[-1] in ('-', ':')): range = range[:-1] colon_count = string.count(range, ':') if colon_count == 1: a, b = string.split(range, ':') try: if a: start = int(a) if b: end = int(b) except ValueError: raise ValueError, 'Invalid range (non-integer value): "%s".' % range elif colon_count == 0: try: start = int(range) except ValueError: raise ValueError, 'Invalid range (non-integer value): "%s".' % range if (start + 1) != 0: end = start + 1 else: raise ValueError, 'Invalid range (wrong number of colons): "%s".' % range return start, end # The following function was written by Mike Brown, and was taken from the # Python Cookbook. I would like to dearly thank my grandparents, Sari and Larry # Pearson, for buying me this book for my birthday. def wrap(text, width=79): """A word-wrap function that preserves existing line breaks and most spaces in the text. Expects that existing line breaks are posix newlines (\\n). """ return reduce(lambda line, word, width=width: '%s%s%s' % (line, ' \n'[(len(line[line.rfind('\n')+1:]) + len(word.split('\n',1)[0]) >= width)], word), text.split(' ') ) # The following function is modeled after recipe 3.7 from the Python Cookbook, # which was written by JÜrgen Hermann and Nick Perkins. def make_string_filter(keep): '''Return a function that takes a string and returns a partial copy of that string consisting only of the characters in 'keep'. ''' allchars = string.maketrans('', '') delchars = allchars.translate(allchars, keep) def string_filter(s, a=allchars, d=delchars): return s.translate(a, d) return string_filter def center_text(text, line_width=80, pad_char='-'): 'Returns the given text centered within a line, with padding.' if len(text) > line_width: text = text[:line_width-4-3] + '...' pad_len = (line_width - len(text) - 2) // 2 extra = pad_char * (len(text) % 2) return '%s %s %s' % (pad_char * pad_len, text, pad_char * pad_len + extra) def sh_escape(text): '''Returns a version of the given text that uses backslashes to make it safe to pass to a Bourne-type shell if you enclose it in double quotes. ''' return re.sub(r'([$`\"])', r'\\\1', text) def xmlrpc_server_doc(server_proxy): '''Produces documentation for the methods of an XML-RPC server. This function queries an XML-RPC server via the introspection API, and returns a collection of descriptions that document each of the server's methods. The returned collection is a dictionary whose keys are the names of the methods and whose values are blurbs of text that describe the methods. This was inspired by the xml-rpc-api2txt Perl script that comes with xmlrpc-c. ''' method_docs = {} for method in server_proxy.system.listMethods(): method_docs[method] = '' signature = server_proxy.system.methodSignature(method) for sig in signature: method_docs[method] += '=item %s B<%s> (%s)\n\n' % \ (sig[0], method, ', '.join(sig[1:])) help = server_proxy.system.methodHelp(method) method_docs[method] += re.sub(r'(?m)^', r' ', help) return method_docs def splitpath(path): '''Splits a Unix pathname into a list of its components. If the pathname is absolute, the resulting list will have '/' as its first element. ''' plist = [] while path not in ('/', ''): path, plist[0:0] = os.path.dirname(path), [os.path.basename(path)] if path: plist[0:0] = path return plist def is_overlapping(A, B, n): '''Tests whether the slices described by two ranges overlap each other. Arguments "A" and "B": pairs (2-tuples) of integers, each of which represents a range within a sequence. The first of each pair is the index of the first item included in the range, and the second of each pair is the index of the item that terminates the range (but is not included in the range). Argument "n": an integer that represents the length of the sequence in which these ranges will be applied. This is needed to handle negative range indices sensibly. Returns: True if the ranges overlap, otherwise False. ''' # Handle negative numbers as range indices. if A[0] < 0: A[0] = n - A[0] if A[1] < 0: A[1] = n - A[1] if B[0] < 0: B[0] = n - B[0] if B[1] < 0: B[1] = n - B[1] # Any one of these tests indicate overlap. if A[0] == B[0]: return True if A[1] == B[1]: return True if A[0] < B[0] < A[1]: return True if A[0] < B[1] < A[1]: return True if B[0] < A[0] < B[1]: return True if B[0] < A[1] < B[1]: return True return False moosic-1.5.6/moosic/client/0000755000175000017500000000000011655460503016302 5ustar danieldaniel00000000000000moosic-1.5.6/moosic/client/factory.py0000644000175000017500000001307111655334040020321 0ustar danieldaniel00000000000000# moosic/client/factory.py - Factory functions for creating Moosic server proxies. # # This is free and unencumbered software released into the public domain. # # Anyone is free to copy, modify, publish, use, compile, sell, or # distribute this software, either in source code form or as a compiled # binary, for any purpose, commercial or non-commercial, and by any # means. # # In jurisdictions that recognize copyright laws, the author or authors # of this software dedicate any and all copyright interest in the # software to the public domain. We make this dedication for the benefit # of the public at large and to the detriment of our heirs and # successors. We intend this dedication to be an overt act of # relinquishment in perpetuity of all present and future rights to this # software under copyright law. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # # For more information, please refer to """Factory functions for creating Moosic server proxies. This module contains functions that make it very quick and easy to create a proxy object that exposes all the methods of a Moosic server. These proxy objects are instances of xmlrpclib.ServerProxy, so you should refer to the documentation for the xmlrpclib module for more detailed information about ServerProxy objects. These factory functions are not necessary for creating proper Moosic server proxies, but they are a very convenient way of doing so. This module also contains a subclass of xmlrpclib.Transport that adapts the XML-RPC client implementation for use with Unix sockets. It is safe to "import *" from this module. """ import xmlrpclib, urllib, httplib, socket, os, os.path, sys import moosic.server.main # Define the True and False constants if they don't already exist. try: True except NameError: True = 1 try: False except NameError: False = 0 __all__ = ('startServer', 'LocalMoosicProxy', 'UnixMoosicProxy', 'InetMoosicProxy', 'UnixStreamTransport') class UnixStreamTransport(xmlrpclib.Transport): '''An adapter for speaking HTTP over a Unix socket instead of a TCP/IP socket. This class mainly exists to serve as a helper for implementing the LocalMoosicProxy class, and will not be directly useful for most Moosic client developers. ''' def make_connection(self, host): class HTTPConnection(httplib.HTTPConnection): def connect(self): host = urllib.unquote(self.host) try: self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) if self.debuglevel > 0: print "connect: (%s)" % host self.sock.connect(host) except socket.error, msg: if self.debuglevel > 0: print 'connect fail:', host if self.sock: self.sock.close() self.sock = None raise class HTTP(httplib.HTTP): _connection_class = HTTPConnection # Older versions of xmlrpclib.Transport use the deprecated httplib.HTTP # class, while newer versions use httplib.HTTPConnection. if sys.version_info < (2,7): return HTTP(host) else: return HTTPConnection(host) def LocalMoosicProxy(filename=None): '''Creates a proxy to a Moosic server that is listening to a local (Unix address family) socket. The optional "filename" argument is the location of the socket file that the Moosic server is using as its address. ''' if not filename: filename = os.path.join(os.getenv('HOME', '/tmp'), '.moosic', 'socket') server_address = 'http://%s/' % urllib.quote(filename, safe='') return xmlrpclib.ServerProxy(uri=server_address, transport=UnixStreamTransport(), verbose=False) def UnixMoosicProxy(filename=None): '''An alias for the LocalMoosicProxy function. ''' return LocalMoosicProxy(filename) def InetMoosicProxy(host, port): '''Creates a proxy to a Moosic server that is listening to a TCP/IP socket. The first argument, "host", is the hostname or IP address where the server is located. The second argument, "port", is the TCP port that the server is listening to. ''' return xmlrpclib.ServerProxy(uri='http://%s:%d/' % (host, port), verbose=False) def startServer(*argv): '''Attempts to start an instance of the Moosic server. The parameters given to this function are used verbatim to form the argument vector that will be passed to the server process (i.e. sys.argv). See the moosicd(1) man page for a description of valid arguments. If the server can't be started, a string describing why is returned. Otherwise, None is returned. ''' status = None try: moosic.server.main.main(argv) except SystemExit, e: status = e[0] if not status: status = None elif type(status) == int: status = "The server exited with an exit status of %d." % status else: status = str(status) return status moosic-1.5.6/moosic/client/__init__.py0000644000175000017500000000000010032006124020360 0ustar danieldaniel00000000000000moosic-1.5.6/moosic/client/cli/0000755000175000017500000000000011655460503017051 5ustar danieldaniel00000000000000moosic-1.5.6/moosic/client/cli/main.py0000644000175000017500000003543311655334055020361 0ustar danieldaniel00000000000000# moosic/client/cli/main.py - The client portion of the moosic jukebox system. # # This is free and unencumbered software released into the public domain. # # Anyone is free to copy, modify, publish, use, compile, sell, or # distribute this software, either in source code form or as a compiled # binary, for any purpose, commercial or non-commercial, and by any # means. # # In jurisdictions that recognize copyright laws, the author or authors # of this software dedicate any and all copyright interest in the # software to the public domain. We make this dedication for the benefit # of the public at large and to the detriment of our heirs and # successors. We intend this dedication to be an overt act of # relinquishment in perpetuity of all present and future rights to this # software under copyright law. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # # For more information, please refer to import sys, socket, os, os.path, getopt, xmlrpclib, errno, time, locale, re from moosic import VERSION from moosic.utilities import * from moosic.client.factory import * from moosic.client.cli.dispatcher import * # Define the True and False constants if they don't already exist. try: True except NameError: True = 1 try: False except NameError: False = 0 # Giving stderr a shorter name is convenient when printing error messages. err = sys.stderr # Set the locale to the user's default settings, if possible. try: locale.setlocale(locale.LC_ALL, '') except: pass def main(argv): COMMANDS = get_command_docs() \ + center_text('This Moosic has Super Cow Powers.', pad_char=' ') USAGE = "usage: " + os.path.basename(argv[0]) + \ " [options] " + ''' Options: -d, --shuffle-dir When a directory is named on the command line, shuffle the result of recursing through the directory before inserting it into the list. -a, --shuffle-args Shuffle only the arguments explicitly specified on the command line. -g, --shuffle-global Shuffle the entire argument list after directory arguments specified on the command line have been replaced with their contents. This is the default behavior. -o, --inorder Don't shuffle the given filelist at all, and maintain the order specified on the command line. -s, --sort Sort the filelist, regardless of the order specified on the command line. -i, --ignore-case Treat any given regular expressions as if they were case-insensitive. -r, --no-recurse Don't replace directories named on the command line with their contents. -n, --non-file-args Don't change any names given in a filelist. Useful if your filelist consists of URLs or other objects that aren't local files. -f, --auto-find Replace each string in the given filelist with the results of a "fuzzy" search for music files which match that string. (Beware: this can be slow with large music collections.) -F, --auto-grep Replace each string in the given filelist with the results of a regular-expression search for music files which match that string. (Beware: this can be slow with large music collections.) -m, --music-dir Specifies the directory to search when using the "auto-find" and "auto-grep" features. (Default: ~/music/) -c, --config-dir Specifies the directory where moosic should find the files kept by moosicd. (Default: ~/.moosic/) -t, --tcp : Communicate with a Moosic server that is listening to the specified TCP/IP port on the specified host. (Not recommended.) -N, --no-startserver Don't automatically start moosicd if it isn't already running. -U, --allow-unplayable Allow songs that the server doesn't know how to play to be added into the song queue. -C, --current-in-list Show the currently playing song in the "list" and "plainlist" commands. -S, --showcommands Print the list of possible commands and exit. -h, --help Print this help text and exit. -v, --version Print version information and exit. This Moosic has Super Cow Powers.''' # Option processing. def process_options(arglist, opts): opts = opts.copy() try: opt_spec = { 'd':'shuffle-dir', 'a':'shuffle-args', 'g':'shuffle-global', 'o':'inorder', 's':'sort', 'i':'ignore-case', 'r':'no-recurse', 'n':'non-file-args', '':'no-file-munge', 'f':'auto-find', 'F':'auto-grep', 'm:':'music-dir=', 'c:':'config-dir=', 't:':'tcp=', 'N':'no-startserver', 'U':'allow-unplayable', 'C':'current-in-list', 'S':'showcommands', 'h':'help', 'v':'version', } short_opts = ''.join(opt_spec.keys()) long_opts = opt_spec.values() options, arglist = getopt.getopt(arglist, short_opts, long_opts) except getopt.error, e: sys.exit('Option processing error: %s' % e) for option, val in options: if option == '-d' or option == '--shuffle-dir': opts['shuffle-dir'] = True opts['shuffle-global'] = False if option == '-a' or option == '--shuffle-args': opts['shuffle-args'] = True opts['shuffle-global'] = False if option == '-g' or option == '--shuffle-global': opts['shuffle-global'] = True if option == '-o' or option == '--inorder': opts['shuffle-global'] = False opts['shuffle-args'] = False opts['shuffle-dir'] = False if option == '-s' or option == '--sort': opts['shuffle-global'] = False opts['shuffle-args'] = False opts['shuffle-dir'] = False opts['sort'] = True if option == '-i' or option == '--ignore-case': opts['ignore-case'] = True if option == '-r' or option == '--no-recurse': opts['dir-recurse'] = False if option == '-n' or option == '--no-file-munge' or option == '--non-file-args': opts['file-munge'] = False opts['dir-recurse'] = False if option == '-f' or option == '--auto-find': opts['auto-find'] = True opts['auto-grep'] = False if option == '-F' or option == '--auto-grep': opts['auto-grep'] = True opts['auto-find'] = False if option == '-m' or option == '--music-dir': opts['music-dir'] = os.path.abspath(os.path.expanduser(val)) if option == '-c' or option == '--config-dir': opts['config-dir'] = os.path.abspath(os.path.expanduser(val)) if option == '-t' or option == '--tcp': if ":" not in val: sys.exit(('Invalid address: %s\n' % val) + 'You must specify both a hostname and a port number.\n' 'For example, "example.com:123"') host, port = val.split(':', 1) try: port = int(port) except ValueError, e: sys.exit("Invalid port number: %s" % port) opts['tcp-address'] = (host, port) if option == '-N' or option == '--no-startserver': opts['start moosicd'] = False if option == '-U' or option == '--allow-unplayable': opts['no-unplayables'] = False if option == '-C' or option == '--current-in-list': opts['current-in-list'] = True if option == '-S' or option == '--showcommands': print COMMANDS sys.exit(0) if option == '-h' or option == '--help': print USAGE sys.exit(0) if option == '-v' or option == '--version': print "moosic", VERSION print """ Copyright (C) 2001-2003 Daniel Pearson This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.""" sys.exit(0) return arglist, opts home = os.getenv('HOME', '/tmp') # Set the built-in default options. opts = {'shuffle-global':True, 'shuffle-args':False, 'shuffle-dir':False, 'debug':False, 'file-munge':True, 'sort':False, 'dir-recurse':True, 'config-dir':os.path.join(home, '.moosic', ''), 'tcp-address':None, 'music-dir':os.path.join(home, 'music', ''), 'auto-find':False, 'auto-grep':False, 'start moosicd':True, 'ignore-case':False, 'sort':False, 'rc-filename':os.path.join(home, '.moosicrc'), 'no-unplayables':True, 'current-in-list':False} # Gather options specified before the command. arglist, opts = process_options(argv[1:], opts) # Pluck the command out of the argument list. if not arglist: sys.exit("You must provide a command.\n" "Use the --showcommands option to learn what commands are available.\n" "Use the --help option to learn what options are available.\n" "usage: %s [options] " % os.path.basename(argv[0])) command = arglist.pop(0) command = command.lower() command = re.sub(r'[\W_]', r'', command) # Gather options specified after the command. if command != 'startserver': # ...except for the startserver command. arglist, opts = process_options(arglist, opts) # TODO: Gather option values from a config file. #file_opts = process_configfile(opts['rc-filename'], opts) # Use the options from the command-line to override the options from the # config file. #file_opts.update(opts) #opts = file_opts if command not in dispatcher: sys.exit(wrap(('Error: invalid command: "%s"\n' % command) + "Use the --showcommands option or the 'help' command to see the " "available commands. Use the --help option to learn about the " "possible options.\n" "usage: %s [options] " % os.path.basename(argv[0]), 79)) # Check the number of arguments given to the command. if not check_args(command, arglist): sys.exit(2) # Create a proxy object for speaking to the Moosic server. if opts['tcp-address']: host, port = opts['tcp-address'] moosic = InetMoosicProxy(host, port) else: server_address = os.path.join(opts['config-dir'], 'socket') moosic = LocalMoosicProxy(server_address) # Make sure that our connection to the server is working properly. try: if command != 'startserver': # unless the user is starting it explicitly moosic.no_op() except socket.error, e: if e[0] in (errno.ECONNREFUSED, errno.ENOENT): # The server doesn't seem to be running, so let's try to start it # for ourselves. if opts['tcp-address']: failure_reason = "The target Moosic server is on a remote computer." elif opts['start moosicd']: print >>err, "Notice: The Moosic server isn't running, so it " \ "is being started automatically." failure_reason = startServer('moosicd', '-c', opts['config-dir']) else: failure_reason = "Automatic launching of the server is disabled." if failure_reason: sys.exit(wrap("Error: The server (moosicd) doesn't seem to be " "running, and it could not be started automatically " "because:\n" + failure_reason, 79)) else: # Wait bit to give moosicd time to start up. time.sleep(0.25) # Test the server connection again. try: moosic.no_op() except Exception, e: # We tried our best. Finally give up. sys.exit("An attempt was made to start the Moosic " "server, but it still can't be contacted.\n" "%s: %s" % (str(e.__class__).split('.')[-1], e)) else: sys.exit("Socket error: %s" % e) try: # Dispatch the command. exit_status = dispatcher[command](moosic, arglist, opts) except socket.error, e: exit_status = "Socket error: %s" % e[1] except xmlrpclib.Fault, e: if ':' in e.faultString: fault_type, fault_msg = e.faultString.split(':', 1) fault_type = fault_type.split('.')[-1] exit_status = "Error from Moosic server: [%s] %s" % (fault_type, fault_msg) else: exit_status = "Error from Moosic server: %s" % e.faultString except xmlrpclib.ProtocolError, e: exit_status = "RPC protocol error: %s %s." % (e.errcode, e.errmsg) except ValueError, e: exit_status = "Error: %s" % e except Exception, e: if isinstance(e, SystemExit): raise e else: exit_status = "%s: %s" % (str(e.__class__).split('.')[-1], e) sys.exit(exit_status) if __name__ == '__main__': main(sys.argv) moosic-1.5.6/moosic/client/cli/__init__.py0000644000175000017500000000000010243123665021145 0ustar danieldaniel00000000000000moosic-1.5.6/moosic/client/cli/dispatcher.py0000644000175000017500000013534411655334065021566 0ustar danieldaniel00000000000000# moosic/client/cli/dispatcher.py - The code for dispatching moosic commands. # # This is free and unencumbered software released into the public domain. # # Anyone is free to copy, modify, publish, use, compile, sell, or # distribute this software, either in source code form or as a compiled # binary, for any purpose, commercial or non-commercial, and by any # means. # # In jurisdictions that recognize copyright laws, the author or authors # of this software dedicate any and all copyright interest in the # software to the public domain. We make this dedication for the benefit # of the public at large and to the detriment of our heirs and # successors. We intend this dedication to be an overt act of # relinquishment in perpetuity of all present and future rights to this # software under copyright law. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # # For more information, please refer to __all__ = ("dispatcher", "command_categories", "get_command_docs", "check_args") import base64, fileinput, sys, os, os.path, time, re import xmlrpclib, random, errno, string from moosic.utilities import * from moosic.client.factory import startServer from moosic import VERSION # Portability note: the Unix "find" program is required to be present on the # system. It is almost unthinkable that you could have a Unix system that # didn't have the "find" command, but you never know. try: import subprocess def call_find(dir): return subprocess.Popen(['find',dir,'-follow','-type','f','-print'], \ stdout=subprocess.PIPE).stdout.readlines() except ImportError: def call_find(dir): return os.popen('find "%s" -follow -type f -print' % sh_escape(dir)).readlines() # Define the True and False constants if they don't already exist. try: True except NameError: True = 1 try: False except NameError: False = 0 command_categories = { "add":"Adding to the song queue", "remove":"Removing from the song queue", "rearrange":"Rearranging the song queue", "query":"Querying for information", "manage":"Player management", } # "dispatcher" is a table that maps moosic command names to the functions that # implement them. Each value is a function that accepts 3 arguments: (moosic, # arglist, opts). The first argument, "moosic", is the Moosic server proxy that # is used to communicate with a Moosic server. The second argument, "arglist", # is a list of the command's arguments. The third argument, "opts", is a # dictionary of the options that were set from the program's command-line # switches. # # Every function in this table must have the following attributes: # # category - One of the keys from the command_categories dictionary. # num_args - One of the following: "zero", "zero_or_one", "zero_or_many", # "one", "two", "two_or_three", "many". # __doc__ - A string that contains a one-line description of the command's # behavior. # # Note that the return value of each of these functions is interpreted by the # "moosic" program as an exit status for the program (by feeding the returned # value to sys.exit()). dispatcher = {} #--------------------------- Public Helper Functions --------------------------# def get_command_docs(): 'Gathers the doc strings for all the moosic commands into a single string.' doc = '' for category in command_categories: doc += center_text(command_categories[category]) + '\n' commands = [command for command in dispatcher if dispatcher[command].category == category] commands.sort() for command in commands: doc += dispatcher[command].__doc__.strip() + '\n\n' return doc def check_args(command, arglist): '''Checks a list of arguments to see if they are valid for a command. The first parameter, command, is a string that represents the name of the command in question. The second parameter, args, is the list of arguments to be checked. Returns True if the argument list is valid, otherwise False. The behavior of this function depends on the value of the num_args attribute of the function object associated with the command. num_args should have one of the following values: "zero", "zero_or_one", "zero_or_many", "one", "two", "two_or_three", "many". ''' err = sys.stderr # Giving stderr a shorter name is convenient when printing # error messages. # Check the number of arguments given to the command. if dispatcher[command].num_args not in ("zero", "zero_or_one", "zero_or_many"): if not arglist: print >>err, \ 'Error: the "%s" command requires at least one argument.' % command return False if dispatcher[command].num_args == "zero": if arglist: print >>err, \ 'Warning: the "%s" command doesn\'t take any arguments.' % command print >>err, 'The given arguments will be ignored.' elif dispatcher[command].num_args == "one": if len(arglist) != 1: print >>err, \ 'Warning: the "%s" command takes only one argument.' % command print >>err, 'Extraneous arguments beyond the first will be ignored.' elif dispatcher[command].num_args == "zero_or_one": if len(arglist) > 1: print >>err, \ 'Warning: the "%s" command takes at most one argument.' % command print >>err, 'Extraneous arguments beyond the first will be ignored.' elif dispatcher[command].num_args == "two": if len(arglist) != 2: print >>err, \ 'Error: the "%s" command requires exactly two arguments.' % command return False elif dispatcher[command].num_args == "two_or_three": if len(arglist) > 3: print >>err, \ 'Warning: the "%s" command takes at most three arguments.' % command print >>err, 'Extraneous arguments beyond the third will be ignored.' if len(arglist) < 2: print >>err, \ 'Error: the "%s" command requires at least two arguments.' % command return False # Pluck off the "index" argument to the insert and pl-insert commands # before processing the rest of their argument lists. if command == 'insert' or command == 'pl-insert': try: index = arglist.pop() if not (index[0].isdigit() or index[0] == '-'): index = index[1:] if not (index[-1].isdigit() or index[-1] == '-'): index = index[:-1] dispatcher['insert'].position = int(index) except ValueError, e: print >>err, "Error:", e print >>err, "An integer is required as the final argument." return False if not arglist: print >>err, \ 'Error: the "%s" command needs something to insert.' % command return False return True #-------------------------- Private Helper Functions --------------------------# def unmangle(s): '''Certain command names are invalid Python identifiers. This converts a command name that was mangled into a valid identifier into its original form. It also lowers the case of the given string for normalization purposes.''' return re.sub(r'[\W_]', r'', s).lower() def read_playlists(playlists, opts): '''Inputs a list of playlist file-names and outputs a list of the file names contained in the playlists.''' arglist = [] for line in fileinput.input(playlists): # skip empty lines if re.search(r'^\s*$', line): continue # skip lines that begin with a '#' character if re.search(r'^#', line): continue # chomp off trailing newlines if line.endswith('\n'): line = line[:-1] # tack the dirname onto the front of relative paths if not os.path.isabs(line) and opts['file-munge']: arglist.append(os.path.join(os.path.dirname(fileinput.filename()), line)) else: arglist.append(line) return arglist def process_filelist(moosic, arglist, opts): if opts['auto-grep']: # Be case-insensitive if that was requested. if opts['ignore-case']: arglist = [i + '(?i)' for i in arglist] # Get a list of filenames which are eligible for auto-finding. files = call_find(opts['music-dir']) files.sort() # Strip off the trailing EOL character. files = [item[:-1] for item in files] # Grep the list of filenames for each regex argument, and include each # grep result in the output list. output = [] [output.extend(grep(a, files)) for a in arglist] arglist = output if opts['auto-find']: # This feature was inspired by David McCabe. del_non_wordchars = make_string_filter(string.letters+string.digits+'/') def simplify(s): return del_non_wordchars(s).lower() # Simplify the strings to be sought after. arglist = [simplify(a) for a in arglist] # Get a list of filenames which are eligible for auto-finding. files = call_find(opts['music-dir']) # Strip off the trailing EOL character. files = [item[:-1] for item in files] # Simplify the filenames to be searched through, and map each simplified # name to the original name. names = {} [names.setdefault(simplify(f), []).append(f) for f in files] # If an argument is a substring of one of the simplified filenames, # include the full filename in the output list. output = [] # Using the "in" operator to find substrings doesn't work in Python 2.2 :( #[output.extend(names[n]) for a in arglist for n in names if (a and a in n)] [output.extend(names[n]) for a in arglist for n in names if (a and n.find(a) != -1)] output.sort() arglist = output # When dealing with local files, all filenames should be specified by # their absolute paths before telling the server to play them. Using # relative pathnames would be foolish because the server has no idea # what our current working directory is. if opts['file-munge']: arglist = map(os.path.expanduser, arglist) arglist = map(os.path.abspath, arglist) if opts['shuffle-args']: random.shuffle(arglist) # If an item in the filelist is a directory, then recurse through the # directory, replacing the item with its children. if opts['dir-recurse']: pos = 0 while pos < len(arglist): if os.path.isdir(arglist[pos]): contents = call_find(arglist.pop(pos)) # Strip off the trailing EOL character. contents = [item[:-1] for item in contents] if opts['shuffle-dir']: random.shuffle(contents) else: contents.sort() contents.reverse() for item in contents: arglist.insert(pos, item) pos = pos + 1 if opts['shuffle-global']: random.shuffle(arglist) if opts['sort']: arglist.sort() if opts['no-unplayables']: playable_patterns = [entry[0].data for entry in moosic.getconfig()] newlist = [] for i in arglist: for pat in playable_patterns: if re.search(pat, i): newlist.append(i) break arglist = newlist return arglist #---------------------------- Dispatcher functions ----------------------------# def start_server(moosic, arglist, opts): "start-server [server-options] - Start the server when it isn't already running." if opts['tcp-address']: print >>sys.stderr, \ wrap("Warning: the Moosic server is being started on the local " "computer, even though you specified the -t option, which " "usually causes this client to interact with a server on " "a remote computer. This is probably not what you want.") if not arglist: arglist = ['-c', opts['config-dir']] return startServer(*(['moosicd'] + arglist)) f = start_server f.category = 'manage' f.num_args = 'zero_or_many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def append(moosic, arglist, opts): 'append - Add files to the end of the song queue.' arglist = process_filelist(moosic, arglist, opts) arglist = [xmlrpclib.Binary(i) for i in arglist] moosic.append(arglist) f = append f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def add(moosic, arglist, opts): 'add - An alias for "append".' append(moosic, arglist, opts) f = add f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def pl_append(moosic, arglist, opts): "pl-append - Add the playlists' contents to the end of the queue." arglist = read_playlists(arglist, opts) append(moosic, arglist, opts) f = pl_append f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def pl_add(moosic, arglist, opts): 'pl-add - An alias for "pl-append".' pl_append(moosic, arglist, opts) f = pl_add f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def prepend(moosic, arglist, opts): 'prepend - Add files to the beginning of the song queue.' arglist = process_filelist(moosic, arglist, opts) arglist = [xmlrpclib.Binary(i) for i in arglist] moosic.prepend(arglist) f = prepend f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def pre(moosic, arglist, opts): 'pre - An alias for "prepend".' prepend(moosic, arglist, opts) f = pre f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def pl_prepend(moosic, arglist, opts): "pl-prepend - Add playlists' contents to the start of the queue." arglist = read_playlists(arglist, opts) prepend(moosic, arglist, opts) f = pl_prepend f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def mixin(moosic, arglist, opts): 'mixin - Add files to the song queue and reshuffle the entire queue.' arglist = process_filelist(moosic, arglist, opts) arglist = [xmlrpclib.Binary(i) for i in arglist] moosic.append(arglist) moosic.shuffle() f = mixin f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def pl_mixin(moosic, arglist, opts): "pl-mixin - Add playlists' contents to the queue and reshuffle all." arglist = read_playlists(arglist, opts) mixin(moosic, arglist, opts) f = pl_mixin f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def putback(moosic, arglist, opts): 'putback - Reinsert the current song at the start of the song queue.' moosic.putback() f = putback f.category = 'add' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def insert(moosic, arglist, opts): 'insert - Insert files at a specific point in the song queue.' arglist = process_filelist(moosic, arglist, opts) arglist = [xmlrpclib.Binary(i) for i in arglist] moosic.insert(arglist, insert.position) insert.position = 0 f = insert f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def pl_insert(moosic, arglist, opts): """pl-insert - Insert playlists' contents at a specific point the song queue.""" arglist = read_playlists(arglist, opts) insert(moosic, arglist, opts) f = pl_insert f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def replace(moosic, arglist, opts): "replace - Clear the current the queue and add a new list of songs." arglist = process_filelist(moosic, arglist, opts) arglist = [xmlrpclib.Binary(i) for i in arglist] moosic.replace(arglist) f = replace f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def pl_replace(moosic, arglist, opts): """pl-replace - Clear the current the queue and add new songs from playlists.""" arglist = read_playlists(arglist, opts) replace(moosic, arglist, opts) f = pl_replace f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def interval_add(moosic, arglist, opts): '''interval-add - Insert songs into the current queue at the specified interval.''' try: interval = int(arglist[0]) except ValueError, e: print >>err, "Error:", e print >>err, "An integer is required as the first argument." return 2 new_songs = process_filelist(moosic, arglist[1:], opts) for i in range(len(new_songs)): moosic.insert([xmlrpclib.Binary(new_songs[i])], i*interval) f = interval_add f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def cut(moosic, arglist, opts): 'cut - Remove all queued items that fall within the given range.' start, end = parse_range(arglist[0], 0, None) if end is None: moosic.cut((start,)) else: moosic.cut((start, end)) f = cut f.category = 'remove' f.num_args = 'one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def del_(moosic, arglist, opts): 'del - An alias for "cut".' cut(moosic, arglist, opts) f = del_ f.category = 'remove' f.num_args = 'one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def crop(moosic, arglist, opts): 'crop - Remove all queued items that do NOT fall within the given range.' start, end = parse_range(arglist[0], 0, None) if end is None: moosic.crop((start,)) else: moosic.crop((start, end)) f = crop f.category = 'remove' f.num_args = 'one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def remove(moosic, arglist, opts): 'remove - Remove all queued items that match the given regex(s).' if opts['ignore-case']: arglist = [i + '(?i)' for i in arglist] arglist = [xmlrpclib.Binary(i) for i in arglist] for pattern in arglist: moosic.remove(pattern) f = remove f.category = 'remove' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def filter_(moosic, arglist, opts): 'filter - Remove all queued items that do NOT match the given regex.' if opts['ignore-case']: arglist = [i + '(?i)' for i in arglist] arglist = [xmlrpclib.Binary(i) for i in arglist] for pattern in arglist: moosic.filter(pattern) f = filter_ f.category = 'remove' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def clear(moosic, arglist, opts): 'clear - Clear the song queue.' moosic.clear() f = clear f.category = 'remove' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def wipe(moosic, arglist, opts): 'wipe - Clear the song queue and stop the current song.' was_running = False if moosic.is_queue_running(): moosic.halt_queue() was_running = True moosic.stop() moosic.clear() if was_running: moosic.run_queue() f = wipe f.category = 'remove' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def move(moosic, arglist, opts): '''move - Moves all items in the given range to a new position in the song queue.''' start, end = parse_range(arglist[0], 0, None) dest = arglist[1] if not (dest[0].isdigit() or dest[0] == '-'): dest = dest[1:] if not (dest[-1].isdigit() or dest[-1] == '-'): dest = dest[:-1] dest = int(dest) if end is None: moosic.move((start,), dest) else: moosic.move((start, end), dest) f = move f.category = 'rearrange' f.num_args = 'two' dispatcher[unmangle(f.__name__)] = f #----------------------------# def move_pattern(moosic, arglist, opts): '''move-pattern - Moves all items that match the given regex to a new position in the song queue.''' pattern, destination = arglist try: if not (destination[0].isdigit() or destination[0] == '-'): destination = destination[1:] if not (destination[-1].isdigit() or destination[-1] == '-'): destination = destination[:-1] destination = int(destination) except ValueError, e: print >>err, 'Error:', e print >>err, 'An integer is required as the final argument.' return 2 all_items = [i.data for i in moosic.list()] if opts['ignore-case']: pattern += '(?i)' items_to_move = grep(pattern, all_items) head = antigrep(pattern, all_items[:destination]) tail = antigrep(pattern, all_items[destination:]) all_items = head + items_to_move + tail moosic.replace([xmlrpclib.Binary(i) for i in all_items]) f = move_pattern f.category = 'rearrange' f.num_args = 'two' dispatcher[unmangle(f.__name__)] = f #----------------------------# def swap(moosic, arglist, opts): '''swap - Causes two segments of the queue to trade places.''' r1 = parse_range(arglist[0], 0, None) r2 = parse_range(arglist[1], 0, None) if r1[1] is None: r1 = (r1[0],) if r2[1] is None: r2 = (r2[0],) moosic.swap(r1, r2) f = swap f.category = 'rearrange' f.num_args = 'two' dispatcher[unmangle(f.__name__)] = f #----------------------------# def shuffle_(moosic, arglist, opts): 'shuffle [range] - Put contents of the song queue into a random order.' if arglist: start, end = parse_range(arglist[0], 0, None) else: start, end = 0, None if end is None: moosic.shuffle((start,)) else: moosic.shuffle((start, end)) f = shuffle_ f.category = 'rearrange' f.num_args = 'zero_or_one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def reshuffle(moosic, arglist, opts): 'reshuffle [range] - An alias for "shuffle".' shuffle_(moosic, arglist, opts) f = reshuffle f.category = 'rearrange' f.num_args = 'zero_or_one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def sort(moosic, arglist, opts): 'sort [range] - Rearrange the song queue in sorted order.' if arglist: start, end = parse_range(arglist[0], 0, None) else: start, end = 0, None if end is None: moosic.sort((start,)) else: moosic.sort((start, end)) f = sort f.category = 'rearrange' f.num_args = 'zero_or_one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def reverse(moosic, arglist, opts): 'reverse [range] - Reverse the order of the song queue.' if arglist: start, end = parse_range(arglist[0], 0, None) else: start, end = 0, None if end is None: moosic.reverse((start,)) else: moosic.reverse((start, end)) f = reverse f.category = 'rearrange' f.num_args = 'zero_or_one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def partial_sort(moosic, arglist, opts): 'partial-sort - Separate the queue items into categories.' items = [i.data for i in moosic.list()] buckets = {} if opts['ignore-case']: arglist = [i + '(?i)' for i in arglist] for pattern in arglist: # Store all items that match the pattern. buckets[pattern] = grep(pattern, items) # Only do further processing on the items that were not matched. items = antigrep(pattern, items) leftovers = items items = [] [items.extend(buckets[pattern]) for pattern in arglist] items.extend(leftovers) items = [xmlrpclib.Binary(i) for i in items] moosic.replace(items) f = partial_sort f.category = 'rearrange' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def stagger(moosic, arglist, opts): '''stagger - Arrange the queue contents into a list that alternates between two or more categories.''' items = [i.data for i in moosic.list()] list_of_lists = [] if opts['ignore-case']: arglist = [i + '(?i)' for i in arglist] for pattern in arglist: # Collect all items that match the pattern. list_of_lists.append(grep(pattern, items)) # Only do further processing on the items that were not matched. items = antigrep(pattern, items) # Perform a staggered merge on all the lists we collected, along # with any remaining items. items = staggered_merge(*(list_of_lists)) + items items = [xmlrpclib.Binary(i) for i in items] moosic.replace(items) f = stagger f.category = 'rearrange' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def sub(moosic, arglist, opts): '''sub [range] - Perform a regular expression substitution on the items in the queue.''' if len(arglist) > 2: start, end = parse_range(arglist[2], 0, None) else: start, end = 0, None if end is None: range = (start,) else: range = (start, end) bin = xmlrpclib.Binary moosic.sub(bin(arglist[0]), bin(arglist[1]), range) f = sub f.category = 'rearrange' f.num_args = 'two_or_three' dispatcher[unmangle(f.__name__)] = f #----------------------------# def suball(moosic, arglist, opts): '''suball [range] - Perform a global regular expression substitution on the items in the queue.''' if len(arglist) > 2: start, end = parse_range(arglist[2], 0, None) else: start, end = 0, None if end is None: range = (start,) else: range = (start, end) bin = xmlrpclib.Binary moosic.sub_all(bin(arglist[0]), bin(arglist[1]), range) f = suball f.category = 'rearrange' f.num_args = 'two_or_three' dispatcher[unmangle(f.__name__)] = f #----------------------------# def stagger_add(moosic, arglist, opts): '''stagger-add - Arrange the given file list into an alternating order and then add it to the queue.''' items = process_filelist(moosic, arglist, opts) list_of_lists = [] for pattern in [re.escape(pattern) for pattern in arglist]: # Collect all items that match the pattern. list_of_lists.append(grep(pattern, items)) # Only do further processing on the items that were not matched. items = antigrep(pattern, items) # Perform a staggered merge on all the lists we collected, along # with any remaining items. items = staggered_merge(*(list_of_lists + [items])) items = [xmlrpclib.Binary(i) for i in items] moosic.append(items) f = stagger_add f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def stagger_merge(moosic, arglist, opts): '''stagger-merge - Interleave the given file list into the song queue. ''' new_items = process_filelist(moosic, arglist, opts) old_items = [i.data for i in moosic.list()] items = [xmlrpclib.Binary(i) for i in staggered_merge(new_items, old_items)] moosic.replace(items) f = stagger_merge f.category = 'add' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def current(moosic, arglist, opts): 'current - Print the name of the song that is currently playing.' print moosic.current().data f = current f.category = 'query' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def curr(moosic, arglist, opts): 'curr - An alias for "current".' current(moosic, arglist, opts) f = curr f.category = 'query' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def current_time(moosic, arglist, opts): '''current-time [format] - Print the amount of time that the current song has been playing.''' if arglist: print time.strftime(arglist[0], time.gmtime(moosic.current_time())) else: print time.strftime('%H:%M:%S', time.gmtime(moosic.current_time())) #current_time = moosic.current_time() #hours, remainder = divmod(current_time, 3600) #minutes, seconds = divmod(remainder, 60) #print '%d:%02d:%02d' % (hours, minutes, seconds) f = current_time f.category = 'query' f.num_args = 'zero_or_one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def list_(moosic, arglist, opts): '''list [range] - Print the list of items in the current song queue. If a range is specified, only the items that fall within that range are listed.''' if arglist: start, end = parse_range(arglist[0], 0, None) else: start, end = 0, None if opts['current-in-list']: if arglist: print >>sys.stderr, wrap("Warning: the -C option has no effect " "if an argument is given.") else: print "[*]", moosic.current().data if end is None: d = moosic.indexed_list((start,)) else: d = moosic.indexed_list((start, end)) index, items = d['start'], d['list'] for item in items: try: print '[%d] %s' % (index, item.data) index += 1 except IOError, e: if e[0] == errno.EPIPE: break # Ignore "broken pipe" errors. else: raise e f = list_ f.category = 'query' f.num_args = 'zero_or_one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def plainlist(moosic, arglist, opts): 'plainlist [range] - Print the current song queue without numbering each line.' if arglist: start, end = parse_range(arglist[0], 0, None) else: start, end = 0, None try: if opts['current-in-list']: if arglist: print >>sys.stderr, wrap("Warning: the -C option has no effect " "if an argument is given.") else: print moosic.current().data if end is None: print '\n'.join([i.data for i in moosic.list((start,))]) else: print '\n'.join([i.data for i in moosic.list((start, end))]) except IOError, e: if e[0] == errno.EPIPE: # Ignore "broken pipe" errors. pass else: raise e f = plainlist f.category = 'query' f.num_args = 'zero_or_one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def history(moosic, arglist, opts): 'history [number] - Print a list of files that were recently played.' if not arglist: num = 0 else: try: num = int(arglist[0]) except ValueError, e: print "Error:", e print 'The argument to the "history" command must be an integer.' sys.exit(1) for item, starttime, endtime in moosic.history(num): item = item.data endtime = time.strftime('%I:%M:%S%p', time.localtime(endtime)) print string.join((endtime, item)) f = history f.category = 'query' f.num_args = 'zero_or_one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def hist(moosic, arglist, opts): 'hist [number] - An alias for "history".' history(moosic, arglist, opts) f = hist f.category = 'query' f.num_args = 'zero_or_one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def state(moosic, arglist, opts): 'state - Print the current state of the music daemon.' current_song = moosic.current().data if current_song != '': print 'Currently playing: "%s"' % current_song else: print 'Nothing is currently playing.' if moosic.is_paused(): print 'The current song is paused.' else: print 'The current song is not paused.' if moosic.is_queue_running(): print 'Queue advancement is enabled.' else: print 'Queue advancement is disabled.' if moosic.is_looping(): print 'Loop mode is on.' else: print 'Loop mode is off.' print "There are %d items in the queue." % moosic.queue_length() f = state f.category = 'query' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def status(moosic, arglist, opts): 'status - An alias for "state".' state(moosic, arglist, opts) f = status f.category = 'query' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def length(moosic, arglist, opts): 'length - Print the number of items in the queue.' print moosic.queue_length() f = length f.category = 'query' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def len_(moosic, arglist, opts): 'len - An alias for "length".' length(moosic, arglist, opts) f = len_ f.category = 'query' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def ispaused(moosic, arglist, opts): 'ispaused - Show whether the current song is paused.' if moosic.is_paused(): print "True" return 0 else: print "False" return 1 f = ispaused f.category = 'query' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def islooping(moosic, arglist, opts): 'islooping - Show whether the server is in loop mode.' if moosic.is_looping(): print "True" return 0 else: print "False" return 1 f = islooping f.category = 'query' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def isadvancing(moosic, arglist, opts): 'isadvancing - Show whether the server is advancing through the song queue.' if moosic.is_queue_running(): print "True" return 0 else: print "False" return 1 f = isadvancing f.category = 'query' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def version(moosic, arglist, opts): 'version - Print version information for the client and server, and then exit.' print 'Moosic Client version:', VERSION print 'Moosic Server version:', moosic.version() f = version f.category = 'query' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def next(moosic, arglist, opts): 'next [number] - Skip ahead in the song queue.' if arglist: try: howmany = int(arglist[0]) except ValueError, e: print "Error:", e print 'The argument to the "next" command must be an integer.' sys.exit(1) moosic.next(howmany) else: moosic.next() f = next f.category = 'manage' f.num_args = 'zero_or_one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def previous(moosic, arglist, opts): '''previous [number] - Returns the most recently played song(s) to the queue and starts playing.''' if arglist: try: howmany = int(arglist[0]) except ValueError, e: print "Error:", e print 'The argument to the "previous" command must be an integer.' sys.exit(1) moosic.previous(howmany) else: moosic.previous() f = previous f.category = 'manage' f.num_args = 'zero_or_one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def prev(moosic, arglist, opts): 'prev - An alias for "previous".' previous(moosic, arglist, opts) f = prev f.category = 'manage' f.num_args = 'zero_or_one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def goto(moosic, arglist, opts): 'goto - Jump to the first song in the queue that matches the regex.' if opts['ignore-case']: arglist = [i + '(?i)' for i in arglist] queue = moosic.list() dest = 1 while queue: if re.search(arglist[0], queue[0].data): break dest += 1 queue.pop(0) if queue: if not moosic.current().data: dest -= 1 moosic.next(dest) else: print 'No match found:', arglist[0] f = goto f.category = 'manage' f.num_args = 'one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def gobackto(moosic, arglist, opts): 'gobackto - Jump to the previous song that matches the regex.' if opts['ignore-case']: arglist = [i + '(?i)' for i in arglist] history = moosic.history() dest = 1 while history: if re.search(arglist[0], history[-1][0].data): break dest += 1 history.pop() if history: moosic.previous(dest) else: print 'No match found:', arglist[0] f = gobackto f.category = 'manage' f.num_args = 'one' dispatcher[unmangle(f.__name__)] = f #----------------------------# def noadvance(moosic, arglist, opts): 'no-advance - Stop playing any new songs, but without interrupting the current song.' moosic.halt_queue() f = noadvance f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def noadv(moosic, arglist, opts): 'no-adv - An alias for "noadvance".' noadvance(moosic, arglist, opts) f = noadv f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def advance(moosic, arglist, opts): 'advance - Activate advancement through the song queue.' moosic.run_queue() f = advance f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def adv(moosic, arglist, opts): 'adv - An alias for "advance".' advance(moosic, arglist, opts) f = adv f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def toggle_advance(moosic, arglist, opts): '''toggle-advance - Halts queue advancement if it is enabled, and enables advancement if it is halted.''' if moosic.is_queue_running(): moosic.halt_queue() else: moosic.run_queue() f = toggle_advance f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def stop(moosic, arglist, opts): '''stop - Stop playing the current song and stop playing new songs. The current song is put back into the song queue as if it had not been played.''' moosic.stop() f = stop f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# # FIXME: this needs a good name def stop_but_no_replay(moosic, arglist, opts): '''stop_but_no_replay - Stop playing the current song and stop processing the song queue.''' moosic.halt_queue() moosic.skip() f = stop_but_no_replay f.category = 'manage' f.num_args = 'zero' #dispatcher[unmangle(f.__name__)] = f #----------------------------# def pause(moosic, arglist, opts): '''pause - Suspend the current song so that it can be resumed at the exact same point at a later time. Note: this often leaves the sound device locked.''' moosic.pause() f = pause f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def unpause(moosic, arglist, opts): '''unpause - Unpause the current song if the current song is paused, otherwise do nothing.''' moosic.unpause() f = unpause f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def toggle_pause(moosic, arglist, opts): '''toggle-pause - Pause the current song if it is not already paused, and unpause the current song if it is already paused.''' moosic.toggle_pause() f = toggle_pause f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def play(moosic, arglist, opts): 'play - Resume playing. (Use after "stop", "sleep", "noplay", or "pause".)' moosic.run_queue() moosic.unpause() f = play f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def loop(moosic, arglist, opts): 'loop - Turn loop mode on.' moosic.set_loop_mode(True) f = loop f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def noloop(moosic, arglist, opts): 'noloop - Turn loop mode off.' moosic.set_loop_mode(False) f = noloop f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def toggle_loop(moosic, arglist, opts): 'toggle-loop - Turn loop mode on if it is off, and turn it off if it is on.' moosic.toggle_loop_mode() f = toggle_loop f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def reconfigure(moosic, arglist, opts): 'reconfigure - Reload the player configuration file.' moosic.reconfigure() f = reconfigure f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def reconfig(moosic, arglist, opts): 'reconfig - An alias for "reconfigure".' reconfigure(moosic, arglist, opts) f = reconfig f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def showconfig(moosic, arglist, opts): 'showconfig - Query and print the filetype to song player associations.' print moosic.showconfig().data f = showconfig f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def help(moosic, arglist, opts): 'help [commands] - Get documentation on the available moosic commands.' if not arglist: print 'The following commands are available:' col_count = 0 commands = dispatcher.keys() commands.sort() for command in commands: if dispatcher[command].category not in command_categories: continue print command, col_count += 1 if not col_count % 5: print else: print ' ' * (13 - len(command)), if col_count % 5: print print 'Use "' + os.path.basename(sys.argv[0]), print 'help " to display help for a particular command.' else: for command in arglist: command = unmangle(command.lower()) if command in dispatcher: print dispatcher[command].__doc__ else: print "No such command:", command f = help f.category = 'query' f.num_args = 'zero_or_many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def exit(moosic, arglist, opts): 'exit - Tell the music daemon to quit.' moosic.die() f = exit f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def quit(moosic, arglist, opts): 'quit - An alias for "exit".' exit(moosic, arglist, opts) f = quit f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def die(moosic, arglist, opts): 'die - An alias for "exit".' exit(moosic, arglist, opts) f = die f.category = 'manage' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def moo(moosic, arglist, opts): print base64.decodestring('''\ ICAgICAgICAoX19fKSAgIChfX18pICAoX19fKSAoX19fKSAgICAgIChfX18pICAgKF9fXykgICAo X19fKSAoX19fKSAgICAgCiAgICAgICAgKG8gbyhfX18pbyBvKShfX18pbykgKG8gbykgKF9fXyko byBvKF9fXylvIG8pKF9fXykgbykgKG8gbykoX19fKQogICAgICAgICBcIC8obyBvKVwgLyAobyBv KS8gKF9fXykgIChvIG8pIFwgLyhvIG8pXCAvIChvIG8pIC8oX19fKS8gKG8gbykKICAgICAgICAg IE8gIFwgLyAgTyAgIFwgL08gIChvIG8pICAgXCAvICAgTyAgXCAvICBPICAgXCAvIE8gKG8gbykg ICBcIC8gCiAgICAgICAgKF9fKSAgTyAoPT0pICAgTyhfXykgXCAvKHx8KSBPICAoX18pICBPIChf XykgICAgKCAgKSBcIC8oX18pIE8gIAogICAgICAgIChvbykoX18pKG9vKShfXykob28pKF9fKShv bykoX18pKG9vKShfXykoIyMpKF9fKShvbykoX18pKG9vKShfXykKICAgICAgICAgXC8gKG9vKSBc LyAob28pIFwvICgsLCkgXC8gKG9vKSBcLyAob28pIFwvIChvbykgXC8gKC0tKSBcLyAoT08pCiAg ICAgICAgKF9fKSBcLyAoX18pIFwvIChfXykgXC8gKF9fKSBcLyAoX18pIFwvIChfXykgXC8gKCws KSBcLyAoX18pIFwvIAogICAgICAgICgqKikoX18pKC0tKShfXykob28pKF9fKSgwMCkoX18pKG9v KShfXykob28pKF9fKShvbykoX18pKG9vKShfXykKICAgICAgICAgXC8gKG9vKSBcLyAob28pIFwv IChvbykgXC8gKG9vKSBcLyAoKiopIFwvIChPTykgXC8gKD8/KSBcLyAob28pCiAgICAgICAgKF9f KSBcLyAoX18pIFwvIChfXykgXC8gKF9fKSBcLyAoX18pIFwvIChfXykgXC8gKF9fKSBcLyAoX18p IFwvIAogICAgICAgIChvbykoX18pKG9vKShfXykoQEApKF9fKShvbykoX18pKG9vKShfXykob28p KF9fKSgtMCkoLCwpKG9vKShfXykKICAgICAgICAgXC8gKG9fKSBcLyAob28pIFwvIChvbykgXC8g KG8jKSBcLyAob28pIFwvIChvbykgXC8gKG9vKSBcLyAob28pCiAgICAgICAgICAgICBcLyAgICAg IFwvICAgICAgXC8gICAgICBcLyAgICAgIFwvICAgICAgXC8gICAgICBcLyAgICAgIFwvIAogICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAoX18pICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAs Jy0wMCAgICAgICAgICAgICAgICAgICAgICAgVGhlIExvY2FsIE1vb3NpY2FsIFNvY2lldHkgICAg ICAKICAgICAgICAgICAvIC9cX3wgICAgICAvICAgICAgICAgICAgICAgICAgICAgICBpbiBDb25j ZXJ0ICAgICAgICAgICAgICAgCiAgICAgICAgICAvICB8ICAgICAgIF8vX19fX19fX19fX19fX19f X19fICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICB8ICAgXD09PV5fX3wg ICAgICAgICAgICAgICAgIF9ffCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAg ICBffF9fXyAvXCAgfF9fX19fX19fX19fX19fX19fX198ICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgCiAgICAgICAgfD09PT09fCB8ICAgSSAgICAgICAgICAgICAgSSAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICpJICAgSXwgfCAgIEkgICAgICAgICAg ICAgIEkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgSSAgIEle IF4gICBJICAgICAgICAgICAgICBJICAgICAgICAgICAgICAgICAgICAgLWNmYmQt''') f = moo f.category = 'hidden' f.num_args = 'zero' dispatcher[unmangle(f.__name__)] = f #----------------------------# def debuglist(moosic, arglist, opts): arglist = process_filelist(moosic, arglist, opts) for x in arglist: print x f = debuglist f.category = 'hidden' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# def debug(moosic, arglist, opts): print moosic.debug(' '.join(arglist)) f = debug f.category = 'hidden' f.num_args = 'many' dispatcher[unmangle(f.__name__)] = f #----------------------------# del f moosic-1.5.6/moosic/__init__.py0000644000175000017500000000002211655324411017124 0ustar danieldaniel00000000000000VERSION = "1.5.6" moosic-1.5.6/README.txt0000644000175000017500000001276611655332617015251 0ustar danieldaniel00000000000000Moosic - A musical jukebox program. Version 1.5.6 By Daniel Pearson Moosic is a music player that focuses on convenient and powerful playlist management. It consists of a server process that maintains a queue of music files to play and a client program which sends commands to the server. The server continually runs through its playlist, popping items off the top of the list and playing each with an external program. The standard client is a simple command-line utility which allows you to perform powerful operations upon the server's queue, including the addition of whole directory trees, automatic shuffling, and item removal according to regular expressions. The server comes configured to play MP3, Ogg, MIDI, MOD, and WAV files. REQUIREMENTS: The primary requirement is a Python interpreter that supports version 2.2 of the language (or later) and also includes support for threads. It also relies on the Unix "find" utility. It relies upon external programs for actually playing the music files. The default setup uses mpg123 for MP3, timidity for MIDI, ogg123 for Ogg/Vorbis, mikmod for the whole range of MOD formats, TakCD for audio CDs, and SOX for a wide variety of sound other (mostly uncompressed) sound file formats. Moosic will only work on Unix systems, since it uses a wide variety of Unix-only features. All of these portability issues are noted within the source code. INSTALLATION: After extracting the archive file (tarball) used to distribute Moosic, change to the directory that contains Moosic, and run "python setup.py install" in a terminal window with the privileges of root (the system administration account). If you are using the Python installation that is included with Mac OS X, then you should add the "--install-scripts" option to the installation command to choose to install the executable portions of Moosic into a directory that is listed in your PATH environment variable. For instance, "python setyp.py install --install-scripts /usr/bin". If you cannot or do not want to install Moosic with root privileges, you can install it into your own home directory by running "python setup.py install --home=~" instead. If you install Moosic into your home directory, make sure that the PYTHONPATH environment variable is set and includes the directory ~/lib/python/. Also make sure that your PATH environment variable includes the ~/bin/ directory. USAGE: "moosic" is the command-line program that provides the main interface to the Moosic jukebox. It works by sending a command to the Moosic server and returning the response, if any. The first non-option argument given to moosic is the name of the command to be performed. Use "moosic --showcommands" to get a list of all the different possible commands, or read moosic's manual page. There are very many commands, so you should start by just learning a few commonly used commands, and only learning others as you feel the need. I recommend starting with the following short command vocabulary: add, list, stop, play, and shuffle. For example, "moosic add foo.mp3" adds the file foo.mp3 (in the current directory) to the end of the song queue and returns you immediately back to your shell prompt without printing any output (unless an error occurs). Compare with "moosic list", which will list the contents of the song queue. Note that if the song queue is empty, "moosic list" will not display anything. Any command which takes a list of files as an argument will also accept directories, and doing so will cause every file below that directory to be included in the file-list. Note that the default behavior of moosic is to shuffle everything in a file-list before sending the list to the server, but only after recursively expanding named directories. You can disable shuffling by using the -o option. Use "moosic --help" to learn about all the options for changing the shuffling behavior, as well as the other command options. When the Moosic server isn't already running, moosic will automatically start it for you (unless you specifically request otherwise with a command-line option). For backwards compatibility, the "moosicd" program can be run to explicitly start the server. However, this usage is obsolete because you can run "moosic startserver" to achieve the same effect. This means that you can delete the "moosicd" program from your system if you want. LEGAL DETAILS: This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to moosic-1.5.6/NEWS0000644000175000017500000001621711655460350014241 0ustar danieldaniel00000000000000---------------------- Version 1.5.6: Four small bugs were fixed. Moosic has been placed in the public domain by way of The Unlicense. ---------------------- Version 1.5.5: Two fatal bugs that were casued by incompatible changes in the Python standard library were fixed. ---------------------- Version 1.5.4: An incompatibility which prevented moosicd from responding to requests correctly when run with Python 2.5 was fixed. Another obscure and rarely triggered bug was fixed. ---------------------- Version 1.5.3: The "move-pattern" command and the -C option were added to the command-line client. "move-pattern" moves all queued songs whose names match a regular expression to a given position in the queue. The -C option causes the current song to be included in the output of the "list" and "plainlist" commands. ---------------------- Version 1.5.2: The server now logs the total playing time whenever it finishes playing a song. The server no longer saves state to disk if the state hasn't really changed. Range arguments can now be bracketed by non-numeric characters to prevent negative ranges from being mistaken for command line options. Various minor bugs were fixed. ---------------------- Version 1.5.1: New commands include "swap", "replace", "pl-replace", "interval-add", and "toggle-advance". The program is now installed with distutils (i.e. a "setup.py" script). By default, the client filters out songs that the server doesn't know how to play before adding them to the queue. A different method is used for automatically starting up the server. This new method doesn't use the $MOOSICD environment variable, and doesn't depend on the presence of a "moosicd" program at all. The code was organized into a package heirarchy that should ease develpment for third-party client developers who use Python. ---------------------- Version 1.5.0: The "help", "current-time", "advance", and "noadvance" commands were added to moosic. The "noplay", "sleep", and "wake" aliases were removed from moosic. The next() and previous() server methods now behave differently. The skip() and current_time() server methods were added. Most importantly, the hostname is no longer appended to the names of the files stored in ~/.moosic/. Client developers must update their code with the new moosic_factory.py, and users who want to use their old config and log files will have to rename them. ---------------------- Version 1.4.10: The "ispaused", "islooping", and "isadvancing" moosic commands were added. The behavior of the "stagger" command was slightly changed. Incompatibility bugs with BSD and Mac OS X were fixed. The documentation for the server's API was cleaned up a bit. Symlinks are now followed when scanning the music directory with "--auto-grep" or "--auto-find". The getconfig() server method was added. The "--tcp-also" option was added to moosicd, making it possible to serve requests via a Unix socket and a TCP socket at the same time. ---------------------- Version 1.4.9: This release fixes a bug that was even stupider than the one fixed in the previous release. This bug prevented moosicd from running because an essential module had been omitted from the build process. ---------------------- Version 1.4.8: The code for the --auto-find feature wasn't compatible with Python 2.2, and therefore crashed with an ugly exception. The fix for this foolish bug was the only change in this release. ---------------------- Version 1.4.7: moosic can now automatically start moosicd as needed, so the user no longer has to start it by hand. The --ignore-case option was added to moosic to make regular expressions case-insensitive. The --auto-find and --auto-grep options were added to moosic, allowing songs to be added with search strings instead of file names. moosicd now automatically puts itself into the background, as proper daemons should do. ---------------------- Version 1.4.6: The "sub" and "suball" commands were added, reflecting the new "sub" and "sub_all" moosicd methods. The "stop" moosicd method was added, and the "stop" command now uses this method. A race condition in the "previous" feature was fixed, and it now accepts an argument to specify how far back to retreat. The Debian packaging was changed to improve compliance with the Debian Python policy and to create a package that works with both the stable and the testing or unstable distributions. A pickling incompatibility between Python 2.2.x and 2.3 was worked around. ---------------------- Version 1.4.5: The "previous" moosicd method and moosic command were added. A bug in which the "putback" moosicd method didn't update the value returned by the "last_queue_update" moosicd method was fixed. A small modification was made to help the program run with Python 2.2. The "wipe" moosic command was fixed to work properly when loop mode is on. ---------------------- Version 1.4.4: The "stagger-merge" moosic client command was added. The "replace", "replace_range", and "last_queue_update" server methods were added. The Moosic_API document now uses POD as its source format. The manpages are now installed in a location that is compliant with FHS 2.2. A debian/ directory is now included to let you build your own .deb package. ---------------------- Version 1.4.3: The code for moosic was refactored to eliminate its horrendously long if-else chain. Various small bug fixes and cleanups were made. Extraneous HTML documentation is no longer included. The manpage for moosic was polished up. The "stagger-add" command was added to moosic. ---------------------- Version 1.4.2: An omission in version 1.4.1 which caused "moosicd --version" to raise a fatal exception was fixed. A bug which prevented moosicd from saving the current song when shutting down was fixed. A long-unnoticed bug was found, in which the result of "moosic list -- -1" would be an empty list. This command now lists the last song in the queue, as expected. The "length" moosic command was added. ---------------------- Version 1.4.1: moosicd saves state on shutdown and reloads it on startup. A loop mode was added. The "sort", "shuffle", and "reverse" commands can now be limited to a specific range in the song queue. Finally, match group substition was implemented in moosicd's song player. This allows you to insert portions of a song's identifier into the command that plays the song. For example, if the player configuration contains an entry with a regex of "^file://(.*?)\.(.*)$" and a command of "foo -t \2 \1", a song name of "file:///var/music/song.mid" will be played with a command line of "foo -t mid /var/music/song". ---------------------- Version 1.4: The server now communicates with clients via the XML-RPC protocol, and the server's API has been properly documented. Writing your own client for the Moosic server in the programming language of your choice should now be quite easy. Python version 2.2 or higher is now required. A few command aliases were added ("pre", "del", and "status"). moosicd now cleans up stale socket files automatically at startup, saving you the trouble of having to do it by hand. The server log now mentions the priority of each logged message. Many tiny bugs were fixed in various places. ---------------------- moosic-1.5.6/setup.py0000755000175000017500000000510111655333365015252 0ustar danieldaniel00000000000000#!/usr/bin/env python from distutils.core import setup import sys import os.path # Patch distutils if it can't cope with the "classifiers" or # "download_url" keywords. if sys.version < '2.2.3': from distutils.dist import DistributionMetadata DistributionMetadata.classifiers = None DistributionMetadata.download_url = None setup(name = 'moosic', version = '1.5.6', author = 'Daniel J. Pearson', author_email = 'daniel@nanoo.org', url = 'http://www.nanoo.org/moosic/', #download_url = 'http://www.nanoo.org/moosic/dist/', license = 'The Unlicense', description = 'A powerful music queue manager.', long_description = ''' Moosic is a music player that focuses on easy playlist management. It consists of a server process that maintains a queue of music files to play and a client program which sends commands to the server. The server continually runs through its playlist, popping items off the top of the list and playing each with an external program. The client is a simple command-line utility which allows you to perform powerful operations upon the server's queue, including the addition of whole directory trees, automatic shuffling, and item removal according to regular expressions. The server comes configured to play MP3, Ogg, MIDI, MOD, and WAV files. ''', classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Environment :: No Input/Output (Daemon)', 'Intended Audience :: End Users/Desktop', 'License :: Public Domain', 'Natural Language :: English', 'Operating System :: POSIX', 'Operating System :: Unix', 'Programming Language :: Python', 'Topic :: Multimedia :: Sound/Audio :: Players', 'Topic :: Multimedia :: Sound/Audio :: Players :: MP3', ], scripts = ['scripts/moosic', 'scripts/moosicd'], packages = ['moosic', 'moosic.server', 'moosic.client', 'moosic.client.cli'], data_files = [ ('share/man/man1', ['doc/moosic.1', 'doc/moosicd.1']), ('share/man/man3', ['doc/Moosic_API.3']), ('share/doc/moosic', ['README.txt', 'License.txt', 'ChangeLog', 'NEWS', 'doc/History.txt', 'doc/Todo', 'doc/Moosic_API.txt', 'doc/moosic_hackers.txt']), ('share/doc/moosic/html', ['doc/Moosic_API.html', 'doc/moosic.html', 'doc/moosicd.html']), ('share/doc/moosic/examples', ['examples/completion', 'examples/server_config']), #('/etc/bash_completion.d/', ['examples/completion']), ], ) moosic-1.5.6/scripts/0000755000175000017500000000000011655460503015222 5ustar danieldaniel00000000000000moosic-1.5.6/scripts/moosic0000755000175000017500000000013210032203407016417 0ustar danieldaniel00000000000000#!/usr/bin/env python import sys from moosic.client.cli.main import main main(sys.argv) moosic-1.5.6/scripts/docgrabber0000755000175000017500000000066410032562476017250 0ustar danieldaniel00000000000000#!/usr/bin/env python import sys from moosic.client.factory import LocalMoosicProxy from moosic.utilities import xmlrpc_server_doc if len(sys.argv) < 2: server_proxy = LocalMoosicProxy() else: server_proxy = LocalMoosicProxy(sys.argv[1]) items = xmlrpc_server_doc(server_proxy).items() items.sort() print print '=over' print for i in items: if not i[0].startswith('_'): print i[1], "\n" print print '=back' print moosic-1.5.6/scripts/moosicd0000755000175000017500000000012610032203453016567 0ustar danieldaniel00000000000000#!/usr/bin/env python import sys from moosic.server.main import main main(sys.argv) moosic-1.5.6/PKG-INFO0000644000175000017500000000270711655460503014636 0ustar danieldaniel00000000000000Metadata-Version: 1.0 Name: moosic Version: 1.5.6 Summary: A powerful music queue manager. Home-page: http://www.nanoo.org/moosic/ Author: Daniel J. Pearson Author-email: daniel@nanoo.org License: The Unlicense Description: Moosic is a music player that focuses on easy playlist management. It consists of a server process that maintains a queue of music files to play and a client program which sends commands to the server. The server continually runs through its playlist, popping items off the top of the list and playing each with an external program. The client is a simple command-line utility which allows you to perform powerful operations upon the server's queue, including the addition of whole directory trees, automatic shuffling, and item removal according to regular expressions. The server comes configured to play MP3, Ogg, MIDI, MOD, and WAV files. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Environment :: No Input/Output (Daemon) Classifier: Intended Audience :: End Users/Desktop Classifier: License :: Public Domain Classifier: Natural Language :: English Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Programming Language :: Python Classifier: Topic :: Multimedia :: Sound/Audio :: Players Classifier: Topic :: Multimedia :: Sound/Audio :: Players :: MP3 moosic-1.5.6/examples/0000755000175000017500000000000011655460503015351 5ustar danieldaniel00000000000000moosic-1.5.6/examples/server_config0000644000175000017500000001344410066632174020135 0ustar danieldaniel00000000000000# ~/.moosic/config # This file associates filetypes with commands which play them. # # The format of this file is as follows: Every pair of lines forms a unit. # The first line in a pair is a regular expression that will be matched against # items in the play list. The second line in a pair is the command that will # be used to play any items that match the regular expression. # # The name of the item to be played will be appended to the end of the command # line, unless the string "$item" occurs in the command, in which case every # occurrence of "$item" will be replaced with the name of the item to be played. # The command will not be interpreted by a shell, so don't try tricks with # quotes or globbing. # # Blank lines and lines starting with a '#' character are ignored. Regular # expressions specified earlier in this file take precedence over those # specified later. # The configuration data in this file is what the author of Moosic actually uses # himself, and serves as an excellent launch-point into a discussion of the # features that can be exploited here. # First, we have a rather basic entry. It associates the ogg123 program with # .ogg files. You should notice that the regular expression includes "(?i)" at # the beginning to make it case-insensitive. It then matches files that have # ".ogg" in their name. The "." character is escaped with a backslash ("\"), # because otherwise the "." character would have a special meaning. Finally, # the regular expression ends with "$" so that it only matches when the ".ogg" # string occurs at the end of a filename. # # A few options are passed to the ogg123 program. They aren't terribly # noteworthy. The -q option makes the program produce less printed output, and # the "-d arts" option makes the program send its audio output to the aRts sound # daemon. (?i)\.ogg$ ogg123 -q -d arts # This second entry uses a regular expression that is ever so slightly more # complicated than the one in the previous entry. It uses a character class # (with only two members: "2" and "3") so that it will match both .mp3 and mp2 # files. Otherwise, it is quite analogous to the previous entry. (?i)\.mp[23]$ mpg321 -q -o arts # The regular expression in this next entry uses the "?" special character to # indicate that the previous character is optional. This allows this expression # to match both files that end with ".mid" and those that end with ".midi". # # You'll notice that the artsdsp wrapper program is used to start timidity # instead of invoking timidity directly. This is because timidity's aRts output # driver is buggy and crashes when I try to use it. (?i)\.midi?$ artsdsp timidity -idqq # This entry demonstrates the consise way of associating a whole bunch of # filename extensions with a single program. This is done in the regular # expression by separating all of the various extensions with the special "|" # character, and then grouping all of the separated extensions together with # parentheses. # # Again, the artsdsp program is used to wrap a song player that doesn't support # aRts very well. Not so special. (?i)\.(mod|xm|s3m|stm|it|mtm|669|amf)$ artsdsp mikmod -q -nohq # This entry isn't much different from the previous one. If you look closely, # you'll notice the return of the special "?" character to allow both "mpg" and # "mpeg" to be matched without having to type out both separately. # # This entry also demonstrates the best options (IMHO) to use with the # ever-impressive mplayer program if you want to set it up as a song handler for # the Moosic server. You might even be tempted to use mplayer as the one and # only song handler in your Moosic config, since mplayer can play practically # any audio file format in the universe. (?i)\.(avi|asf|mpe?g|wav|aiff|aifc|aif|au|cdr)$ mplayer -vo null -vc null -nocache -really-quiet # This entry serves as a simple demonstration of the "match group substitution" # feature, and also demonstrates one way to play audio CDs with Moosic. # # Match group substitution allows you to insert specific parts of the name of # the song to be played into the command. Whenever you use parentheses in a # regular expression, a "match group" is created. This match group represents # the piece of the song name that matched the part of the regular expression # within the parentheses. The first match group can be inserted into the # command by using the string "\1". The second group can be inserted with "\2", # and so on. # # In this case, the regular expression has a single match group, which captures # the string of non-space characters following the "cda://", if any. ("cda" is # supposed to stand for Compact Disc Audio, and the syntax of cda:// is # meant to resemble a URI. Because, hey, URIs are trendy.) The string captured # by the match group is then passed to the program that will play a particular # CD track. # # In case you haven't already guessed, this entry lets you put a CD track into # the song queue by adding something like "cda://8". You can also take # advantage of the fact that the parts of the song name that are not part of the # match group are not passed to the player program by including some useful # information about the song, like its title. For example: # # moosic -n add "cda://11 - I Only Have Eyes for You" # # The "takcd" program is used here to play each CD track. I don't know of any # other audio CD player that is suitable for use as a song handler for Moosic. # You can find takcd at http://bard.sytes.net/takcd/. (?i)^cda://(\S*) takcd \1 # This final example demonstrates a cute bit of recursion. It tells Moosic to # call itself in order to expand a playlist file that it encounters in the # queue. (?i)\.m3u$ moosic -o pl-add # If you want to learn more about what you can do with the regular expressions # in this file, read http://www.python.org/doc/current/lib/re-syntax.html moosic-1.5.6/examples/completion0000644000175000017500000000404110300323404017424 0ustar danieldaniel00000000000000# moosic(1) completion # # Copyright 2003 "Etienne PIERRE" # Copyright 2004 "Daniel Pearson" # have moosic && _moosic() { local cur options paroptions COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} options='-d --shuffle-dir -a --shuffle-args -g --shuffle-global -o \ --inorder -s --sort -i --ignore-case -r --no-recurse -n \ --non-file-args -f --auto-find -F --auto-grep -m --music-dir \ -c --config-dir -t --tcp -N --no-startserver -S --showcommands \ -h --help -v --version' commands=' \ add adv advance append clear \ crop curr current currenttime cut \ del die exit filter gobackto \ goto help hist history insert \ intervaladd isadvancing islooping ispaused len \ length list loop mixin move \ next noadv noadvance noloop partialsort \ pause pladd plainlist plappend play \ plinsert plmixin plprepend plreplace pre \ prepend prev previous putback quit \ reconfig reconfigure remove replace reshuffle \ reverse showconfig shuffle sort stagger \ staggeradd staggermerge startserver state status \ stop sub suball swap toggleadvance \ toggleloop togglepause unpause version wipe' paroptions="$options $commands" if [ ${COMP_CWORD} -eq 1 ]; then COMPREPLY=($( compgen -W "$paroptions" | grep ^$cur )) else case "$prev" in -*) COMPREPLY=($( compgen -W "$paroptions" | grep ^$cur )) ;; *) _filedir ;; esac fi return 0 } [ "$have" ] && complete -F _moosic -o filenames moosic moosic-1.5.6/License.txt0000644000175000017500000000227311655332105015656 0ustar danieldaniel00000000000000This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to moosic-1.5.6/setup.cfg0000644000175000017500000000042610041311474015344 0ustar danieldaniel00000000000000[bdist_rpm] release = 1 doc_files = README.txt License.txt ChangeLog NEWS doc/History.txt doc/Todo doc/Moosic_API.txt doc/moosic_hackers.txt doc/Moosic_API.html doc/moosic.html doc/moosicd.html