diff --git a/.gitignore b/.gitignore index df6693ff..65ed3f37 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ *.pyc *.swp .coverage +.idea +docs/_build local_settings.py pip-log.txt spotify_appkey.key diff --git a/README.rst b/README.rst index 4e9bc66f..766d5354 100644 --- a/README.rst +++ b/README.rst @@ -1,147 +1,16 @@ ****** -mopidy +Mopidy ****** -mopidy is an MPD server with a Spotify backend. +Mopidy is an `MPD `_ server with a +`Spotify `_ backend. Using a standard MPD client you +can search for music in Spotify's wast archive, manage Spotify play lists and +play music from Spotify. +Mopidy is currently under development. Unless you want to contribute to the +development, you should probably wait for our first release before trying out +Mopidy. -Goal -==== - -Using a standard MPD client we want to search for music in Spotify, manage -Spotify play lists and play music from Spotify. - -To limit scope, we will start by implementing an MPD server which only -supports Spotify, and not playback of files from disk. We will make mopidy -modular, so we can extend it with other backends in the future, like file -playback and other online music services such as Last.fm. - - -Backends -======== - -To use the despotify backend, you first need to install despotify and spytify. -Alternatively, we are working on a libspotify backend, which requires you to -install libspotify and pyspotify. - -Both backends require a Spotify premium account, while only the libspotify -backend requires you to get an application key from Spotify before use. - - -Installing despotify and spytify --------------------------------- - -Check out the despotify source code revision 497 (or possibly newer):: - - svn co https://despotify.svn.sourceforge.net/svnroot/despotify@497 despotify - -Install despotify's dependencies. At Debian/Ubuntu systems:: - - sudo aptitude install libssl-dev zlib1g-dev libvorbis-dev \ - libtool libncursesw5-dev libao-dev - -Build and install despotify:: - - cd despotify/src/ - make - sudo make install - -Build and install spytify:: - - cd despotify/src/bindings/python/ - make - sudo make install - -To validate that everything is working, run the ``test.py`` script which is -distributed with spytify:: - - python test.py - -The test script should ask for your username and password (which must be for a -Spotify Premium account), ask for a search query, list all your playlists with -tracks, play 10s from a random song from the search result, pause for two -seconds, play for five more seconds, and quit. - - -Installing libspotify and pyspotify ------------------------------------ - -As libspotify's installation script at the moment is somewhat broken (see this -`GetSatisfaction thread `_ -for details), it is easiest to use the libspotify files bundled with pyspotify. -The files bundled with pyspotify are for 64-bit, so if you run a 32-bit OS, you -must get libspotify from https://developer.spotify.com/en/libspotify/. - -Install pyspotify's dependencies. At Debian/Ubuntu systems:: - - sudo aptitude install python-alsaaudio - -Check out the pyspotify code, and install it:: - - git clone git://github.com/winjer/pyspotify.git - cd pyspotify - export LD_LIBRARY_PATH=$PWD/lib - sudo python setup.py develop - -Apply for an application key at -https://developer.spotify.com/en/libspotify/application-key, download the -binary version, and place the file at ``pyspotify/spotify_appkey.key``. - -Test your libspotify setup:: - - ./example1.py -u USERNAME -p PASSWORD - -Until Spotify fixes their installation script, you'll have to set -``LD_LIBRARY_PATH`` every time you are going to use libspotify (in other words -before starting mopidy). - - -Running mopidy -============== - -Create a file name ``local_settings.py`` in the same directory as -``settings.py``. Enter your Spotify Premium account's username and password -into the file, like this:: - - SPOTIFY_USERNAME = u'myusername' - SPOTIFY_PASSWORD = u'mysecret' - -To start mopidy, go to the root of the mopidy project, then simply run:: - - python mopidy - -To stop mopidy, press ``CTRL+C``. - - -Running tests -============= - -To run tests, you need a couple of dependiencies. Some can be installed through Debian/Ubuntu package management:: - - sudo aptitude install python-coverage - -The rest can be installed using pip:: - - sudo aptitude install python-pip python-setuptools bzr - pip install -r test-requirements.txt - -Then, to run all tests:: - - python tests - - -Resources -========= - -- MPD - - - `MPD protocol documentation `_ - - The original `MPD server `_ - -- Spotify - - - `spytify `_, - the Python bindings for `despotify `_ - - `pyspotify `_, - Python bindings for the official Spotify library, libspotify - - `Spotify's official metadata API `_ +* `Source code `_ +* `Documentation `_ +* IRC: ``#mopidy`` at `irc.freenode.net `_ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..8b70019c --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,104 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf _build/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html + @echo + @echo "Build finished. The HTML pages are in _build/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) _build/dirhtml + @echo + @echo "Build finished. The HTML pages are in _build/dirhtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in _build/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) _build/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in _build/qthelp, like this:" + @echo "# qcollectiongenerator _build/qthelp/Mopidy.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile _build/qthelp/Mopidy.qhc" + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex + @echo + @echo "Build finished; the LaTeX files are in _build/latex." + @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ + "run these through (pdf)latex." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes + @echo + @echo "The overview file is in _build/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in _build/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) _build/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in _build/doctest/output.txt." + +public: clean html + rm -rf /tmp/mopidy-html && cp -r _build/html /tmp/mopidy-html + git stash save + cd .. && \ + git checkout gh-pages && \ + git pull && \ + rm -r * && \ + cp -r /tmp/mopidy-html/* . && \ + mv _sources sources && \ + (find . -type f | xargs sed -i -e 's/_sources/sources/g') && \ + mv _static static && \ + (find . -type f | xargs sed -i -e 's/_static/static/g') && \ + mv _images images && \ + (find . -type f | xargs sed -i -e 's/_images/images/g') && \ + git add * diff --git a/docs/_themes/nature/static/nature.css_t b/docs/_themes/nature/static/nature.css_t new file mode 100644 index 00000000..03b0379d --- /dev/null +++ b/docs/_themes/nature/static/nature.css_t @@ -0,0 +1,229 @@ +/** + * Sphinx stylesheet -- default theme + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: Arial, sans-serif; + font-size: 100%; + background-color: #111; + color: #555; + margin: 0; + padding: 0; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 230px; +} + +hr{ + border: 1px solid #B1B4B6; +} + +div.document { + background-color: #eee; +} + +div.body { + background-color: #ffffff; + color: #3E4349; + padding: 0 30px 30px 30px; + font-size: 0.8em; +} + +div.footer { + color: #555; + width: 100%; + padding: 13px 0; + text-align: center; + font-size: 75%; +} + +div.footer a { + color: #444; + text-decoration: underline; +} + +div.related { + background-color: #6BA81E; + line-height: 32px; + color: #fff; + text-shadow: 0px 1px 0 #444; + font-size: 0.80em; +} + +div.related a { + color: #E2F3CC; +} + +div.sphinxsidebar { + font-size: 0.75em; + line-height: 1.5em; +} + +div.sphinxsidebarwrapper{ + padding: 20px 0; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: Arial, sans-serif; + color: #222; + font-size: 1.2em; + font-weight: normal; + margin: 0; + padding: 5px 10px; + background-color: #ddd; + text-shadow: 1px 1px 0 white +} + +div.sphinxsidebar h4{ + font-size: 1.1em; +} + +div.sphinxsidebar h3 a { + color: #444; +} + + +div.sphinxsidebar p { + color: #888; + padding: 5px 20px; +} + +div.sphinxsidebar p.topless { +} + +div.sphinxsidebar ul { + margin: 10px 20px; + padding: 0; + color: #000; +} + +div.sphinxsidebar a { + color: #444; +} + +div.sphinxsidebar input { + border: 1px solid #ccc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar input[type=text]{ + margin-left: 20px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #005B81; + text-decoration: none; +} + +a:hover { + color: #E32E00; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Arial, sans-serif; + background-color: #BED4EB; + font-weight: normal; + color: #212224; + margin: 30px 0px 10px 0px; + padding: 5px 0 5px 10px; + text-shadow: 0px 1px 0 white +} + +div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; } +div.body h2 { font-size: 150%; background-color: #C8D5E3; } +div.body h3 { font-size: 120%; background-color: #D8DEE3; } +div.body h4 { font-size: 110%; background-color: #D8DEE3; } +div.body h5 { font-size: 100%; background-color: #D8DEE3; } +div.body h6 { font-size: 100%; background-color: #D8DEE3; } + +a.headerlink { + color: #c60f0f; + font-size: 0.8em; + padding: 0 4px 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + background-color: #c60f0f; + color: white; +} + +div.body p, div.body dd, div.body li { + line-height: 1.5em; +} + +div.admonition p.admonition-title + p { + display: inline; +} + +div.highlight{ + background-color: white; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.topic { + background-color: #eee; +} + +div.warning { + background-color: #ffe4e4; + border: 1px solid #f66; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre { + padding: 10px; + background-color: White; + color: #222; + line-height: 1.2em; + border: 1px solid #C6C9CB; + font-size: 1.2em; + margin: 1.5em 0 1.5em 0; + -webkit-box-shadow: 1px 1px 1px #d8d8d8; + -moz-box-shadow: 1px 1px 1px #d8d8d8; +} + +tt { + background-color: #ecf0f3; + color: #222; + padding: 1px 2px; + font-size: 1.2em; + font-family: monospace; +} diff --git a/docs/_themes/nature/static/pygments.css b/docs/_themes/nature/static/pygments.css new file mode 100644 index 00000000..652b7612 --- /dev/null +++ b/docs/_themes/nature/static/pygments.css @@ -0,0 +1,54 @@ +.c { color: #999988; font-style: italic } /* Comment */ +.k { font-weight: bold } /* Keyword */ +.o { font-weight: bold } /* Operator */ +.cm { color: #999988; font-style: italic } /* Comment.Multiline */ +.cp { color: #999999; font-weight: bold } /* Comment.preproc */ +.c1 { color: #999988; font-style: italic } /* Comment.Single */ +.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ +.ge { font-style: italic } /* Generic.Emph */ +.gr { color: #aa0000 } /* Generic.Error */ +.gh { color: #999999 } /* Generic.Heading */ +.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ +.go { color: #111 } /* Generic.Output */ +.gp { color: #555555 } /* Generic.Prompt */ +.gs { font-weight: bold } /* Generic.Strong */ +.gu { color: #aaaaaa } /* Generic.Subheading */ +.gt { color: #aa0000 } /* Generic.Traceback */ +.kc { font-weight: bold } /* Keyword.Constant */ +.kd { font-weight: bold } /* Keyword.Declaration */ +.kp { font-weight: bold } /* Keyword.Pseudo */ +.kr { font-weight: bold } /* Keyword.Reserved */ +.kt { color: #445588; font-weight: bold } /* Keyword.Type */ +.m { color: #009999 } /* Literal.Number */ +.s { color: #bb8844 } /* Literal.String */ +.na { color: #008080 } /* Name.Attribute */ +.nb { color: #999999 } /* Name.Builtin */ +.nc { color: #445588; font-weight: bold } /* Name.Class */ +.no { color: #ff99ff } /* Name.Constant */ +.ni { color: #800080 } /* Name.Entity */ +.ne { color: #990000; font-weight: bold } /* Name.Exception */ +.nf { color: #990000; font-weight: bold } /* Name.Function */ +.nn { color: #555555 } /* Name.Namespace */ +.nt { color: #000080 } /* Name.Tag */ +.nv { color: purple } /* Name.Variable */ +.ow { font-weight: bold } /* Operator.Word */ +.mf { color: #009999 } /* Literal.Number.Float */ +.mh { color: #009999 } /* Literal.Number.Hex */ +.mi { color: #009999 } /* Literal.Number.Integer */ +.mo { color: #009999 } /* Literal.Number.Oct */ +.sb { color: #bb8844 } /* Literal.String.Backtick */ +.sc { color: #bb8844 } /* Literal.String.Char */ +.sd { color: #bb8844 } /* Literal.String.Doc */ +.s2 { color: #bb8844 } /* Literal.String.Double */ +.se { color: #bb8844 } /* Literal.String.Escape */ +.sh { color: #bb8844 } /* Literal.String.Heredoc */ +.si { color: #bb8844 } /* Literal.String.Interpol */ +.sx { color: #bb8844 } /* Literal.String.Other */ +.sr { color: #808000 } /* Literal.String.Regex */ +.s1 { color: #bb8844 } /* Literal.String.Single */ +.ss { color: #bb8844 } /* Literal.String.Symbol */ +.bp { color: #999999 } /* Name.Builtin.Pseudo */ +.vc { color: #ff99ff } /* Name.Variable.Class */ +.vg { color: #ff99ff } /* Name.Variable.Global */ +.vi { color: #ff99ff } /* Name.Variable.Instance */ +.il { color: #009999 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/_themes/nature/theme.conf b/docs/_themes/nature/theme.conf new file mode 100644 index 00000000..1cc40044 --- /dev/null +++ b/docs/_themes/nature/theme.conf @@ -0,0 +1,4 @@ +[theme] +inherit = basic +stylesheet = nature.css +pygments_style = tango diff --git a/docs/changes.rst b/docs/changes.rst new file mode 100644 index 00000000..3e4edadd --- /dev/null +++ b/docs/changes.rst @@ -0,0 +1,15 @@ +******* +Changes +******* + +This change log is used to track all major changes to Mopidy. + + +0.1 (unreleased) +================ + +Initial version. + +Features: + +* *TODO:* Fill out diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..f0d29003 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,194 @@ +# -*- coding: utf-8 -*- +# +# Mopidy documentation build configuration file, created by +# sphinx-quickstart on Fri Feb 5 22:19:08 2010. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.append(os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Mopidy' +copyright = u'2010, Stein Magnus Jodal' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.1' +# The full version, including alpha/beta/rc tags. +release = '0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'nature' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = ['_themes'] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Mopidydoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Mopidy.tex', u'Mopidy Documentation', + u'Stein Magnus Jodal', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True diff --git a/docs/despotify-issues.rst b/docs/despotify-issues.rst deleted file mode 100644 index 810913b8..00000000 --- a/docs/despotify-issues.rst +++ /dev/null @@ -1,18 +0,0 @@ -Despotify issues ----------------- - -* r483: s.lookup('spotify:track:1mr3616BzLdhXfJmLmRsO8') returns error: - "SpytifyError: URI specifies invalid type: track" - Possibly fixed in r497. -* r483: When accessing an_artist.albums one get the error: - "TypeError: Cannot convert spytify.AlbumDataFull to spytify.ArtistDataFull" -* r483: Sometimes segfaults when traversing stored playlists. May work if one - try again immediately. Another example segfault:: - - >>> In [45]: s.lookup('spotify:user:klette:playlist:5rOGYPwwKqbAcVX8bW4k5V') - Segmentation fault - -* r497: spytify fails on ``make'':: - - src/spytify.c: In function ‘__pyx_pf_7spytify_7Spytify___init__’: - src/spytify.c:7325: error: too few arguments to function ‘despotify_init_client’ diff --git a/docs/development.rst b/docs/development.rst new file mode 100644 index 00000000..7efc4983 --- /dev/null +++ b/docs/development.rst @@ -0,0 +1,132 @@ +*********** +Development +*********** + +Development of Mopidy is coordinated through the IRC channel ``#mopidy`` at +``irc.freenode.net`` and through `GitHub `_. + + +Scope +===== + +To limit scope, we will start by implementing an MPD server which only +supports Spotify, and not playback of files from disk. We will make Mopidy +modular, so we can extend it with other backends in the future, like file +playback and other online music services such as Last.fm. + + +Running tests +============= + +To run tests, you need a couple of dependiencies. Some can be installed through Debian/Ubuntu package management:: + + sudo aptitude install python-coverage + +The rest can be installed using pip:: + + sudo aptitude install python-pip python-setuptools bzr + pip install -r test-requirements.txt + +Then, to run all tests:: + + python tests + + +Music Player Daemon (MPD) +========================= + +The `MPD protocol documentation `_ is a +useful resource. It is rather incomplete with regards to data formats, both for +requests and responses. Thus we have to talk a great deal with the the original +`MPD server `_ using telnet to get the details we need +to implement our own MPD server which is compatible with the numerous existing +`MPD clients `_. + + +spytify +======= + +`spytify `_ +is the Python bindings for the open source `despotify `_ +library. It got no documentation to speak of, but a couple of examples are +available. + +Issues +------ + +A list of the issues we currently experience with spytify, both bugs and +features we wished was there. + +* r483: Track lookup support. Possibly fixed in r497. To reproduce:: + + >>> import spytify + >>> s = spytify.Spytify('alice', 'secret') + >>> s.lookup('spotify:track:1mr3616BzLdhXfJmLmRsO8') + --------------------------------------------------------------------------- + SpytifyError Traceback (most recent call last) + + /home/jodal/ in () + + /usr/local/lib/python2.6/dist-packages/spytify.so in spytify.Spytify.lookup (src/spytify.c:7914)() + + SpytifyError: URI specifies invalid type: track + +* r483: Error when accessing an album through an artist. To reproduce:: + + >>> import spytify + >>> s = spytify.Spytify('alice', 'secret') + >>> result = s.search('Gorillaz') + >>> artist = result.playlist.tracks[0].artists[0] + >>> artist + + >>> artist.albums + ERROR: An unexpected error occurred while tokenizing input + The following traceback may be corrupted or invalid + The error message is: ('EOF in multi-line statement', (1423, 0)) + + ERROR: An unexpected error occurred while tokenizing input + The following traceback may be corrupted or invalid + The error message is: ('EOF in multi-line statement', (1455, 0)) + + --------------------------------------------------------------------------- + TypeError Traceback (most recent call last) + + /home/jodal/ in () + + /usr/local/lib/python2.6/dist-packages/spytify.so in spytify.Artist.albums.__get__ (src/spytify.c:4867)() + + /usr/local/lib/python2.6/dist-packages/spytify.so in spytify.Artist.get_full_data (src/spytify.c:4539)() + + TypeError: Cannot convert spytify.AlbumDataFull to spytify.ArtistDataFull + +* r483: Sometimes segfaults when traversing stored playlists, their tracks, + artists, and albums. As it is not predictable, it may be a concurrency issue. + +* r483: Segfaults when looking up playlists, both your own lists and other + peoples shared lists. To reproduce:: + + >>> import spytify + >>> s = spytify.Spytify('alice', 'secret') + >>> s.lookup('spotify:user:klette:playlist:5rOGYPwwKqbAcVX8bW4k5V') + Segmentation fault + +* r497: spytify fails on ``make`` because the despotify API has changed:: + + src/spytify.c: In function ‘__pyx_pf_7spytify_7Spytify___init__’: + src/spytify.c:7325: error: too few arguments to function ‘despotify_init_client’ + + +pyspotify +========= + +`pyspotify `_ is the Python bindings for +the official Spotify library, libspotify. It got no documentation to speak of, +but multiple examples are available. + +Issues +------ + +A list of the issues we currently experience with pyspotify, both bugs and +features we wished was there. + +* None at the moment. diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..08a9dacd --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,18 @@ +.. include:: ../README.rst + +Contents +======== + +.. toctree:: + :maxdepth: 3 + + changes + installation + development + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search` + diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 00000000..43859605 --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,135 @@ +************ +Installation +************ + +Mopidy itself is a breeze to install, as it just requires a standard Python +installation. The libraries we depend on to connect to the Spotify service is +far more tricky to get working for the time being. Until installation of these +libraries are either well documented by their developers, or the libraries are +packaged for various Linux distributions, we will supply our own installation +guides here. + + +Dependencies +============ + +* Python >= 2.5 +* Dependencies for at least one Mopidy backend: + + * :ref:`despotify` + * :ref:`libspotify` + + +.. _despotify: + +despotify backend +================= + +To use the despotify backend, you first need to install despotify and spytify. + +*This backend requires a Spotify premium account.* + + +Installing despotify and spytify +-------------------------------- + +Install despotify's dependencies. At Debian/Ubuntu systems:: + + sudo aptitude install libssl-dev zlib1g-dev libvorbis-dev \ + libtool libncursesw5-dev libao-dev + +Check out revision 483 of the despotify source code:: + + svn co https://despotify.svn.sourceforge.net/svnroot/despotify@483 despotify + +Build and install despotify:: + + cd despotify/src/ + make + sudo make install + +Build and install spytify:: + + cd despotify/src/bindings/python/ + make + sudo make install + +To validate that everything is working, run the ``test.py`` script which is +distributed with spytify:: + + python test.py + +The test script should ask for your username and password (which must be for a +Spotify Premium account), ask for a search query, list all your playlists with +tracks, play 10s from a random song from the search result, pause for two +seconds, play for five more seconds, and quit. + +.. _libspotify: + +libspotify backend +================== + +As an alternative to the despotify backend, we are working on a libspotify +backend. To use the libspotify backend you must install libspotify and +pyspotify. + +*This backend requires a Spotify premium account.* + +*This backend requires you to get an application key from Spotify before use.* + + +Installing libspotify and pyspotify +----------------------------------- + +As libspotify's installation script at the moment is somewhat broken (see this +`GetSatisfaction thread `_ +for details), it is easiest to use the libspotify files bundled with pyspotify. +The files bundled with pyspotify are for 64-bit, so if you run a 32-bit OS, you +must get libspotify from https://developer.spotify.com/en/libspotify/. + +Install pyspotify's dependencies. At Debian/Ubuntu systems:: + + sudo aptitude install python-alsaaudio + +Check out the pyspotify code, and install it:: + + git clone git://github.com/winjer/pyspotify.git + cd pyspotify + export LD_LIBRARY_PATH=$PWD/lib + sudo python setup.py develop + +Apply for an application key at +https://developer.spotify.com/en/libspotify/application-key, download the +binary version, and place the file at ``pyspotify/spotify_appkey.key``. + +Test your libspotify setup:: + + ./example1.py -u USERNAME -p PASSWORD + +Until Spotify fixes their installation script, you'll have to set +``LD_LIBRARY_PATH`` every time you are going to use libspotify (in other words +before starting Mopidy). + + +Running Mopidy +============== + +Create a file name ``local_settings.py`` in the same directory as +``settings.py``. Enter your Spotify Premium account's username and password +into the file, like this:: + + SPOTIFY_USERNAME = u'myusername' + SPOTIFY_PASSWORD = u'mysecret' + +Currently the despotify backend is the default. If you want to use the +libspotify backend, copy the Spotify application key to +``mopidy/spotify_appkey.key``, and add the following to +``mopidy/mopidy/local_settings.py``:: + + BACKEND=u'mopidy.backends.libspotify.LibspotifyBackend' + +To start Mopidy, go to the root of the Mopidy project, then simply run:: + + python mopidy + +To stop Mopidy, press ``CTRL+C``. diff --git a/docs/pyspotify-issues.rst b/docs/pyspotify-issues.rst deleted file mode 100644 index 777b27be..00000000 --- a/docs/pyspotify-issues.rst +++ /dev/null @@ -1,4 +0,0 @@ -Pyspotify issues ----------------- - -* None at the moment.