├── .gitignore ├── MANIFEST.in ├── Makefile ├── README.rst ├── docs ├── conf.py ├── example.rst ├── index.rst ├── interfaces.rst ├── modules.rst ├── mpris2.decorator.attribute.rst ├── mpris2.decorator.base.rst ├── mpris2.decorator.interface.rst ├── mpris2.decorator.method.rst ├── mpris2.decorator.rst ├── mpris2.decorator.signal.rst ├── mpris2.interfaces.rst ├── mpris2.mediaplayer2.rst ├── mpris2.player.rst ├── mpris2.playlists.rst ├── mpris2.rst ├── mpris2.some_players.rst ├── mpris2.tracklist.rst ├── mpris2.types.loop_status.rst ├── mpris2.types.metadata_map.rst ├── mpris2.types.playback_rate.rst ├── mpris2.types.playback_status.rst ├── mpris2.types.playlist.rst ├── mpris2.types.playlist_id.rst ├── mpris2.types.playlist_ordering.rst ├── mpris2.types.rst ├── mpris2.types.time_in_us.rst ├── mpris2.types.uri.rst ├── mpris2.types.volume.rst ├── mpris2.utils.rst └── types.rst ├── make.bat ├── mpris2 ├── __init__.py ├── __main__.py ├── decorator │ ├── __init__.py │ ├── __main__.py │ ├── attribute.py │ ├── base.py │ ├── interface.py │ ├── method.py │ ├── signal.py │ └── utils.py ├── interfaces.py ├── mediaplayer2.py ├── player.py ├── playlists.py ├── some_players.py ├── tracklist.py ├── types │ ├── __init__.py │ ├── __main__.py │ ├── loop_status.py │ ├── metadata_map.py │ ├── playback_rate.py │ ├── playback_status.py │ ├── playlist.py │ ├── playlist_id.py │ ├── playlist_ordering.py │ ├── time_in_us.py │ ├── uri.py │ └── volume.py └── utils.py ├── setup.cfg ├── setup.py └── test ├── __init__.py └── decorator ├── __init__.py └── test_method.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | build 3 | .project 4 | .pydevproject 5 | .settings 6 | .idea 7 | .pypirc 8 | MANIFEST 9 | dist/ 10 | .cache 11 | .coverage 12 | mpris2.komodoproject 13 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.rst -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINIXBUILDAPI = sphinx-apidoc -e 8 | PAPER = 9 | BUILDDIR = build 10 | INTALLCMD = python setup.py 11 | 12 | # Internal variables. 13 | PAPEROPT_a4 = -D latex_paper_size=a4 14 | PAPEROPT_letter = -D latex_paper_size=letter 15 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) docs 16 | ALLSPHINXAPIOPTS = -f -o docs mpris2 17 | # the i18n builder cannot share the environment and doctrees with the others 18 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) docs 19 | 20 | .PHONY: help clean install html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 21 | 22 | help: 23 | @echo "Please use \`make ' where is one of" 24 | @echo " install to make install" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " devhelp to make HTML files and a Devhelp project" 33 | @echo " epub to make an epub" 34 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 35 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 36 | @echo " text to make text files" 37 | @echo " man to make manual pages" 38 | @echo " texinfo to make Texinfo files" 39 | @echo " info to make Texinfo files and run them through makeinfo" 40 | @echo " gettext to make PO message catalogs" 41 | @echo " changes to make an overview of all changed/added/deprecated items" 42 | @echo " linkcheck to check all external links for integrity" 43 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 44 | 45 | clean: 46 | -rm -rf $(BUILDDIR)/* 47 | 48 | install: 49 | $(INTALLCMD) install 50 | @echo "Install done" 51 | 52 | html: 53 | @echo "Bulding doc from code..." 54 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 55 | @echo "Doc for code generated" 56 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 57 | @echo 58 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 59 | 60 | dirhtml: 61 | @echo "Bulding doc from code..." 62 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 63 | @echo "Doc for code generated" 64 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 65 | @echo 66 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 67 | 68 | singlehtml: 69 | @echo "Bulding doc from code..." 70 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 71 | @echo "Doc for code generated" 72 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 73 | @echo 74 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 75 | 76 | pickle: 77 | @echo "Bulding doc from code..." 78 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 79 | @echo "Doc for code generated" 80 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 81 | @echo 82 | @echo "Build finished; now you can process the pickle files." 83 | 84 | json: 85 | @echo "Bulding doc from code..." 86 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 87 | @echo "Doc for code generated" 88 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 89 | @echo 90 | @echo "Build finished; now you can process the JSON files." 91 | 92 | htmlhelp: 93 | @echo "Bulding doc from code..." 94 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 95 | @echo "Doc for code generated" 96 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 97 | @echo 98 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 99 | ".hhp project file in $(BUILDDIR)/htmlhelp." 100 | 101 | qthelp: 102 | @echo "Bulding doc from code..." 103 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 104 | @echo "Doc for code generated" 105 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 106 | @echo 107 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 108 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 109 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/MPRIS2.qhcp" 110 | @echo "To view the help file:" 111 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/MPRIS2.qhc" 112 | 113 | devhelp: 114 | @echo "Bulding doc from code..." 115 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 116 | @echo "Doc for code generated" 117 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 118 | @echo 119 | @echo "Build finished." 120 | @echo "To view the help file:" 121 | @echo "# mkdir -p $$HOME/.local/share/devhelp/MPRIS2" 122 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/MPRIS2" 123 | @echo "# devhelp" 124 | 125 | epub: 126 | @echo "Bulding doc from code..." 127 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 128 | @echo "Doc for code generated" 129 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 130 | @echo 131 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 132 | 133 | latex: 134 | @echo "Bulding doc from code..." 135 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 136 | @echo "Doc for code generated" 137 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 138 | @echo 139 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 140 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 141 | "(use \`make latexpdf' here to do that automatically)." 142 | 143 | latexpdf: 144 | @echo "Bulding doc from code..." 145 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 146 | @echo "Doc for code generated" 147 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 148 | @echo "Running LaTeX files through pdflatex..." 149 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 150 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 151 | 152 | text: 153 | @echo "Bulding doc from code..." 154 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 155 | @echo "Doc for code generated" 156 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 157 | @echo 158 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 159 | 160 | man: 161 | @echo "Bulding doc from code..." 162 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 163 | @echo "Doc for code generated" 164 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 165 | @echo 166 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 167 | 168 | texinfo: 169 | @echo "Bulding doc from code..." 170 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 171 | @echo "Doc for code generated" 172 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 173 | @echo 174 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 175 | @echo "Run \`make' in that directory to run these through makeinfo" \ 176 | "(use \`make info' here to do that automatically)." 177 | 178 | info: 179 | @echo "Bulding doc from code..." 180 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 181 | @echo "Doc for code generated" 182 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 183 | @echo "Running Texinfo files through makeinfo..." 184 | make -C $(BUILDDIR)/texinfo info 185 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 186 | 187 | gettext: 188 | @echo "Bulding doc from code..." 189 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 190 | @echo "Doc for code generated" 191 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 192 | @echo 193 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 194 | 195 | changes: 196 | @echo "Bulding doc from code..." 197 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 198 | @echo "Doc for code generated" 199 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 200 | @echo 201 | @echo "The overview file is in $(BUILDDIR)/changes." 202 | 203 | linkcheck: 204 | @echo "Bulding doc from code..." 205 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 206 | @echo "Doc for code generated" 207 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 208 | @echo 209 | @echo "Link check complete; look for any errors in the above output " \ 210 | "or in $(BUILDDIR)/linkcheck/output.txt." 211 | 212 | doctest: 213 | @echo "Bulding doc from code..." 214 | $(SPHINIXBUILDAPI) $(ALLSPHINXAPIOPTS) 215 | @echo "Doc for code generated" 216 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 217 | @echo "Testing of doctests in the sources finished, look at the " \ 218 | "results in $(BUILDDIR)/doctest/output.txt." 219 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ==================================== 2 | Python usable definiton of MPRIS2 3 | ==================================== 4 | 5 | This is my lib to help developers work with MPRIS2 and python 6 | 7 | See Mpris2 site (http://specifications.freedesktop.org/mpris-spec/2.2/) 8 | 9 | See also: 10 | ======== 11 | 12 | pympris (https://github.com/wistful/pympris) 13 | 14 | 15 | Example: 16 | ======== 17 | 18 | Here is some examples, that shows how to work with this lib. 19 | 20 | 21 | Configure dbus and mainloop 22 | --------------------------- 23 | 24 | >>> # configure mainloop (not required if you wont expect signals) 25 | >>> from dbus.mainloop.glib import DBusGMainLoop 26 | >>> DBusGMainLoop(set_as_default=True) 27 | 28 | 29 | Discover your player mpris uri 30 | ------------------------------ 31 | 32 | >>> # you can use get_players_uri to get current running players uri 33 | >>> from mpris2 import get_players_uri 34 | >>> # next raise StopIteration if not found 35 | >>> uri = next(get_players_uri()) 36 | 37 | 38 | Connect to player 39 | ----------------- 40 | 41 | >>> # create you player 42 | >>> from mpris2 import Player 43 | >>> player = Player(dbus_interface_info={'dbus_uri': uri}) 44 | 45 | 46 | Call methods 47 | ------------ 48 | 49 | 50 | >>> player.Next() # play next media 51 | 52 | 53 | Get attributes 54 | -------------- 55 | 56 | >>> print(player.Metadata) #current media data 57 | 58 | 59 | Wait signal 60 | ----------- 61 | 62 | 63 | >>> def another_handler(self, *args, **kw): 64 | >>> print(args, kw) 65 | >>> 66 | >>> player.PropertiesChanged = another_handler 67 | >>> # python3 68 | >>> import gi.repository.GLib 69 | >>> mloop = gi.repository.GLib.MainLoop() 70 | >>> mloop.run() 71 | 72 | 73 | Other examples: 74 | --------------- 75 | 76 | >>> # old versions mainloop 77 | >>> import gobject 78 | >>> mloop = gobject.MainLoop() 79 | 80 | >>> # list all running players 81 | >>> from mpris2 import get_players_uri 82 | >>> print([uri for uri in get_players_uri()]) 83 | >>> # get_players_uri can be called with filter parameter 84 | >>> get_players_uri('.+rhythmbox') 85 | >>> # you can set it yourself 86 | >>> uri = 'org.mpris.MediaPlayer2.gmusicbrowser' 87 | >>> # or use one predefined 88 | >>> from mpris2 import SomePlayers, Interfaces 89 | >>> uri = '.'.join([Interfaces.MEDIA_PLAYER, SomePlayers.GMUSICBROWSER]) 90 | 91 | >>> # test other interfaces 92 | >>> from mpris2 import MediaPlayer2 93 | >>> mp2 = MediaPlayer2(dbus_interface_info={'dbus_uri': uri}) 94 | >>> # not all players implement this: 95 | >>> from mpris2 import Playlists, TrackList 96 | >>> pls = Playlists(dbus_interface_info={'dbus_uri': uri}) 97 | >>> tl = TrackList(dbus_interface_info={'dbus_uri': uri}) 98 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # MPRIS2 documentation build configuration file, created by 4 | # sphinx-quickstart on Thu Jan 26 19:34:38 2012. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | import sphinx_rtd_theme 16 | 17 | # If extensions (or modules to document with autodoc) are in another directory, 18 | # add these directories to sys.path here. If the directory is relative to the 19 | # documentation root, use os.path.abspath to make it absolute, like shown here. 20 | sys.path.insert(0, os.path.abspath('../')) 21 | 22 | # -- General configuration ----------------------------------------------------- 23 | 24 | # If your documentation needs a minimal Sphinx version, state it here. 25 | #needs_sphinx = '1.0' 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be extensions 28 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 29 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 30 | 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 31 | 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 32 | 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode'] 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | templates_path = ['_templates'] 36 | 37 | # The suffix of source filenames. 38 | source_suffix = '.rst' 39 | 40 | # The encoding of source files. 41 | #source_encoding = 'utf-8-sig' 42 | 43 | # The master toctree document. 44 | master_doc = 'index' 45 | 46 | # General information about the project. 47 | project = u'MPRIS2' 48 | copyright = u'2015, hugosenari' 49 | 50 | # The version info for the project you're documenting, acts as replacement for 51 | # |version| and |release|, also used in various other places throughout the 52 | # built documents. 53 | # 54 | # The short X.Y version. 55 | version = '0.9.3' 56 | # The full version, including alpha/beta/rc tags. 57 | release = '0.9.3' 58 | 59 | # The language for content autogenerated by Sphinx. Refer to documentation 60 | # for a list of supported languages. 61 | #language = None 62 | 63 | # There are two options for replacing |today|: either, you set today to some 64 | # non-false value, then it is used: 65 | #today = '' 66 | # Else, today_fmt is used as the format for a strftime call. 67 | #today_fmt = '%B %d, %Y' 68 | 69 | # List of patterns, relative to source directory, that match files and 70 | # directories to ignore when looking for source files. 71 | exclude_patterns = [] 72 | 73 | # The reST default role (used for this markup: `text`) to use for all documents. 74 | #default_role = None 75 | 76 | # If true, '()' will be appended to :func: etc. cross-reference text. 77 | #add_function_parentheses = True 78 | 79 | # If true, the current module name will be prepended to all description 80 | # unit titles (such as .. function::). 81 | #add_module_names = True 82 | 83 | # If true, sectionauthor and moduleauthor directives will be shown in the 84 | # output. They are ignored by default. 85 | #show_authors = False 86 | 87 | # The name of the Pygments (syntax highlighting) style to use. 88 | pygments_style = 'sphinx' 89 | 90 | # A list of ignored prefixes for module index sorting. 91 | #modindex_common_prefix = [] 92 | 93 | 94 | # -- Options for HTML output --------------------------------------------------- 95 | 96 | # The theme to use for HTML and HTML Help pages. See the documentation for 97 | # a list of builtin themes. 98 | html_theme = 'sphinx_rtd_theme' 99 | 100 | # Theme options are theme-specific and customize the look and feel of a theme 101 | # further. For a list of options available for each theme, see the 102 | # documentation. 103 | #html_theme_options = {} 104 | 105 | # Add any paths that contain custom themes here, relative to this directory. 106 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 107 | 108 | # The name for this set of Sphinx documents. If None, it defaults to 109 | # " v documentation". 110 | #html_title = None 111 | 112 | # A shorter title for the navigation bar. Default is the same as html_title. 113 | #html_short_title = None 114 | 115 | # The name of an image file (relative to this directory) to place at the top 116 | # of the sidebar. 117 | #html_logo = None 118 | 119 | # The name of an image file (within the static path) to use as favicon of the 120 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 121 | # pixels large. 122 | #html_favicon = None 123 | 124 | # Add any paths that contain custom static files (such as style sheets) here, 125 | # relative to this directory. They are copied after the builtin static files, 126 | # so a file named "default.css" will overwrite the builtin "default.css". 127 | html_static_path = [] 128 | 129 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 130 | # using the given strftime format. 131 | #html_last_updated_fmt = '%b %d, %Y' 132 | 133 | # If true, SmartyPants will be used to convert quotes and dashes to 134 | # typographically correct entities. 135 | #html_use_smartypants = True 136 | 137 | # Custom sidebar templates, maps document names to template names. 138 | #html_sidebars = {} 139 | 140 | # Additional templates that should be rendered to pages, maps page names to 141 | # template names. 142 | #html_additional_pages = {} 143 | 144 | # If false, no module index is generated. 145 | #html_domain_indices = True 146 | 147 | # If false, no index is generated. 148 | #html_use_index = True 149 | 150 | # If true, the index is split into individual pages for each letter. 151 | #html_split_index = False 152 | 153 | # If true, links to the reST sources are added to the pages. 154 | #html_show_sourcelink = True 155 | 156 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 157 | #html_show_sphinx = True 158 | 159 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 160 | #html_show_copyright = True 161 | 162 | # If true, an OpenSearch description file will be output, and all pages will 163 | # contain a tag referring to it. The value of this option must be the 164 | # base URL from which the finished HTML is served. 165 | #html_use_opensearch = '' 166 | 167 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 168 | #html_file_suffix = None 169 | 170 | # Output file base name for HTML help builder. 171 | htmlhelp_basename = 'MPRIS2doc' 172 | 173 | 174 | # -- Options for LaTeX output -------------------------------------------------- 175 | 176 | latex_elements = { 177 | # The paper size ('letterpaper' or 'a4paper'). 178 | #'papersize': 'letterpaper', 179 | 180 | # The font size ('10pt', '11pt' or '12pt'). 181 | #'pointsize': '10pt', 182 | 183 | # Additional stuff for the LaTeX preamble. 184 | #'preamble': '', 185 | } 186 | 187 | # Grouping the document tree into LaTeX files. List of tuples 188 | # (source start file, target name, title, author, documentclass [howto/manual]). 189 | latex_documents = [ 190 | ('index', 'MPRIS2.tex', u'MPRIS2 Documentation', 191 | u'hugosenari', 'manual'), 192 | ] 193 | 194 | # The name of an image file (relative to this directory) to place at the top of 195 | # the title page. 196 | #latex_logo = None 197 | 198 | # For "manual" documents, if this is true, then toplevel headings are parts, 199 | # not chapters. 200 | #latex_use_parts = False 201 | 202 | # If true, show page references after internal links. 203 | #latex_show_pagerefs = False 204 | 205 | # If true, show URL addresses after external links. 206 | #latex_show_urls = False 207 | 208 | # Documents to append as an appendix to all manuals. 209 | #latex_appendices = [] 210 | 211 | # If false, no module index is generated. 212 | #latex_domain_indices = True 213 | 214 | 215 | # -- Options for manual page output -------------------------------------------- 216 | 217 | # One entry per manual page. List of tuples 218 | # (source start file, name, description, authors, manual section). 219 | man_pages = [ 220 | ('index', 'mpris2', u'MPRIS2 Documentation', 221 | [u'hugosenari'], 1) 222 | ] 223 | 224 | # If true, show URL addresses after external links. 225 | #man_show_urls = False 226 | 227 | 228 | # -- Options for Texinfo output ------------------------------------------------ 229 | 230 | # Grouping the document tree into Texinfo files. List of tuples 231 | # (source start file, target name, title, author, 232 | # dir menu entry, description, category) 233 | texinfo_documents = [ 234 | ('index', 'MPRIS2', u'MPRIS2 Documentation', 235 | u'hugosenari', 'MPRIS2', 'One line description of project.', 236 | 'Miscellaneous'), 237 | ] 238 | 239 | # Documents to append as an appendix to all manuals. 240 | #texinfo_appendices = [] 241 | 242 | # If false, no module index is generated. 243 | #texinfo_domain_indices = True 244 | 245 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 246 | #texinfo_show_urls = 'footnote' 247 | 248 | 249 | # -- Options for Epub output --------------------------------------------------- 250 | 251 | # Bibliographic Dublin Core info. 252 | epub_title = u'MPRIS2' 253 | epub_author = u'hugosenari' 254 | epub_publisher = u'hugosenari' 255 | epub_copyright = u'2015, hugosenari' 256 | 257 | # The language of the text. It defaults to the language option 258 | # or en if the language is not set. 259 | #epub_language = '' 260 | 261 | # The scheme of the identifier. Typical schemes are ISBN or URL. 262 | #epub_scheme = '' 263 | 264 | # The unique identifier of the text. This can be a ISBN number 265 | # or the project homepage. 266 | #epub_identifier = '' 267 | 268 | # A unique identification for the text. 269 | #epub_uid = '' 270 | 271 | # A tuple containing the cover image and cover page html template filenames. 272 | #epub_cover = () 273 | 274 | # HTML files that should be inserted before the pages created by sphinx. 275 | # The format is a list of tuples containing the path and title. 276 | #epub_pre_files = [] 277 | 278 | # HTML files shat should be inserted after the pages created by sphinx. 279 | # The format is a list of tuples containing the path and title. 280 | #epub_post_files = [] 281 | 282 | # A list of files that should not be packed into the epub file. 283 | #epub_exclude_files = [] 284 | 285 | # The depth of the table of contents in toc.ncx. 286 | #epub_tocdepth = 3 287 | 288 | # Allow duplicate toc entries. 289 | #epub_tocdup = True 290 | 291 | 292 | # Example configuration for intersphinx: refer to the Python standard library. 293 | intersphinx_mapping = {'http://docs.python.org/': None} 294 | -------------------------------------------------------------------------------- /docs/example.rst: -------------------------------------------------------------------------------- 1 | Here is some examples, that shows how to work with this lib. 2 | 3 | 4 | Configure dbus and mainloop 5 | --------------------------- 6 | 7 | >>> # configure mainloop (not required if you wont expect signals) 8 | >>> from dbus.mainloop.glib import DBusGMainLoop 9 | >>> DBusGMainLoop(set_as_default=True) 10 | 11 | 12 | Discover your player mpris uri 13 | ------------------------------ 14 | 15 | >>> # you can use get_players_uri to get current running players uri 16 | >>> from mpris2 import get_players_uri 17 | >>> # next raise StopIteration if not found 18 | >>> uri = next(get_players_uri()) 19 | 20 | 21 | Connect to player 22 | ----------------- 23 | 24 | >>> # create you player 25 | >>> from mpris2 import Player 26 | >>> player = Player(dbus_interface_info={'dbus_uri': uri}) 27 | 28 | 29 | Call methods 30 | ------------ 31 | 32 | 33 | >>> player.Next() # play next media 34 | 35 | 36 | Get attributes 37 | -------------- 38 | 39 | >>> print(player.Metadata) #current media data 40 | 41 | 42 | Wait signal 43 | ----------- 44 | 45 | 46 | >>> def another_handler(self, *args, **kw): 47 | >>> print(args, kw) 48 | >>> 49 | >>> player.PropertiesChanged = another_handler 50 | >>> # python3 51 | >>> import gi.repository.GLib 52 | >>> mloop = gi.repository.GLib.MainLoop() 53 | >>> mloop.run() 54 | 55 | 56 | Other examples: 57 | --------------- 58 | 59 | >>> # old versions mainloop 60 | >>> import gobject 61 | >>> mloop = gobject.MainLoop() 62 | 63 | >>> # list all running players 64 | >>> from mpris2 import get_players_uri 65 | >>> print([uri for uri in get_players_uri()]) 66 | >>> # get_players_uri can be called with filter parameter 67 | >>> get_players_uri('.+rhythmbox') 68 | >>> # you can set it yourself 69 | >>> uri = 'org.mpris.MediaPlayer2.gmusicbrowser' 70 | >>> # or use one predefined 71 | >>> from mpris2 import SomePlayers, Interfaces 72 | >>> uri = '.'.join([Interfaces.MEDIA_PLAYER, SomePlayers.GMUSICBROWSER]) 73 | 74 | >>> # test other interfaces 75 | >>> from mpris2 import MediaPlayer2 76 | >>> mp2 = MediaPlayer2(dbus_interface_info={'dbus_uri': uri}) 77 | >>> # not all players implement this: 78 | >>> from mpris2 import Playlists, TrackList 79 | >>> pls = Playlists(dbus_interface_info={'dbus_uri': uri}) 80 | >>> tl = TrackList(dbus_interface_info={'dbus_uri': uri}) 81 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. MPRIS2 documentation master file, created by 2 | sphinx-quickstart on Thu Jan 26 19:34:38 2012. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | ====== 7 | MPRIS2 8 | ====== 9 | 10 | .. automodule:: mpris2 11 | 12 | 13 | 14 | Indices and tables 15 | ================== 16 | 17 | * :ref:`genindex` 18 | * :ref:`modindex` 19 | * :ref:`search` 20 | 21 | 22 | Require: 23 | ======== 24 | 25 | To use this lib you need: 26 | Python dbus 27 | 28 | 29 | 30 | Contents: 31 | ========= 32 | 33 | .. toctree:: 34 | :maxdepth: 2 35 | 36 | interfaces.rst 37 | types.rst 38 | modules.rst 39 | example.rst -------------------------------------------------------------------------------- /docs/interfaces.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | MPRIS2 Interfaces 3 | ================= 4 | 5 | .. autoclass:: mpris2.Interfaces 6 | :members: 7 | .. autoclass:: mpris2.MediaPlayer2 8 | :members: 9 | .. autoclass:: mpris2.Player 10 | :members: 11 | .. autoclass:: mpris2.Playlists 12 | :members: 13 | .. autoclass:: mpris2.TrackList 14 | :members: -------------------------------------------------------------------------------- /docs/modules.rst: -------------------------------------------------------------------------------- 1 | mpris2 2 | ====== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | mpris2 8 | -------------------------------------------------------------------------------- /docs/mpris2.decorator.attribute.rst: -------------------------------------------------------------------------------- 1 | mpris2.decorator.attribute module 2 | ================================= 3 | 4 | .. automodule:: mpris2.decorator.attribute 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.decorator.base.rst: -------------------------------------------------------------------------------- 1 | mpris2.decorator.base module 2 | ============================ 3 | 4 | .. automodule:: mpris2.decorator.base 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.decorator.interface.rst: -------------------------------------------------------------------------------- 1 | mpris2.decorator.interface module 2 | ================================= 3 | 4 | .. automodule:: mpris2.decorator.interface 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.decorator.method.rst: -------------------------------------------------------------------------------- 1 | mpris2.decorator.method module 2 | ============================== 3 | 4 | .. automodule:: mpris2.decorator.method 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.decorator.rst: -------------------------------------------------------------------------------- 1 | mpris2.decorator package 2 | ======================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | 9 | mpris2.decorator.attribute 10 | mpris2.decorator.base 11 | mpris2.decorator.interface 12 | mpris2.decorator.method 13 | mpris2.decorator.signal 14 | 15 | Module contents 16 | --------------- 17 | 18 | .. automodule:: mpris2.decorator 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | -------------------------------------------------------------------------------- /docs/mpris2.decorator.signal.rst: -------------------------------------------------------------------------------- 1 | mpris2.decorator.signal module 2 | ============================== 3 | 4 | .. automodule:: mpris2.decorator.signal 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.interfaces.rst: -------------------------------------------------------------------------------- 1 | mpris2.interfaces module 2 | ======================== 3 | 4 | .. automodule:: mpris2.interfaces 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.mediaplayer2.rst: -------------------------------------------------------------------------------- 1 | mpris2.mediaplayer2 module 2 | ========================== 3 | 4 | .. automodule:: mpris2.mediaplayer2 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.player.rst: -------------------------------------------------------------------------------- 1 | mpris2.player module 2 | ==================== 3 | 4 | .. automodule:: mpris2.player 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.playlists.rst: -------------------------------------------------------------------------------- 1 | mpris2.playlists module 2 | ======================= 3 | 4 | .. automodule:: mpris2.playlists 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.rst: -------------------------------------------------------------------------------- 1 | mpris2 package 2 | ============== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | mpris2.decorator 10 | mpris2.types 11 | 12 | Submodules 13 | ---------- 14 | 15 | .. toctree:: 16 | 17 | mpris2.interfaces 18 | mpris2.mediaplayer2 19 | mpris2.player 20 | mpris2.playlists 21 | mpris2.some_players 22 | mpris2.tracklist 23 | mpris2.utils 24 | 25 | Module contents 26 | --------------- 27 | 28 | .. automodule:: mpris2 29 | :members: 30 | :undoc-members: 31 | :show-inheritance: 32 | -------------------------------------------------------------------------------- /docs/mpris2.some_players.rst: -------------------------------------------------------------------------------- 1 | mpris2.some_players module 2 | ========================== 3 | 4 | .. automodule:: mpris2.some_players 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.tracklist.rst: -------------------------------------------------------------------------------- 1 | mpris2.tracklist module 2 | ======================= 3 | 4 | .. automodule:: mpris2.tracklist 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.types.loop_status.rst: -------------------------------------------------------------------------------- 1 | mpris2.types.loop_status module 2 | =============================== 3 | 4 | .. automodule:: mpris2.types.loop_status 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.types.metadata_map.rst: -------------------------------------------------------------------------------- 1 | mpris2.types.metadata_map module 2 | ================================ 3 | 4 | .. automodule:: mpris2.types.metadata_map 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.types.playback_rate.rst: -------------------------------------------------------------------------------- 1 | mpris2.types.playback_rate module 2 | ================================= 3 | 4 | .. automodule:: mpris2.types.playback_rate 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.types.playback_status.rst: -------------------------------------------------------------------------------- 1 | mpris2.types.playback_status module 2 | =================================== 3 | 4 | .. automodule:: mpris2.types.playback_status 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.types.playlist.rst: -------------------------------------------------------------------------------- 1 | mpris2.types.playlist module 2 | ============================ 3 | 4 | .. automodule:: mpris2.types.playlist 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.types.playlist_id.rst: -------------------------------------------------------------------------------- 1 | mpris2.types.playlist_id module 2 | =============================== 3 | 4 | .. automodule:: mpris2.types.playlist_id 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.types.playlist_ordering.rst: -------------------------------------------------------------------------------- 1 | mpris2.types.playlist_ordering module 2 | ===================================== 3 | 4 | .. automodule:: mpris2.types.playlist_ordering 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.types.rst: -------------------------------------------------------------------------------- 1 | mpris2.types package 2 | ==================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | 9 | mpris2.types.loop_status 10 | mpris2.types.metadata_map 11 | mpris2.types.playback_rate 12 | mpris2.types.playback_status 13 | mpris2.types.playlist 14 | mpris2.types.playlist_id 15 | mpris2.types.playlist_ordering 16 | mpris2.types.time_in_us 17 | mpris2.types.uri 18 | mpris2.types.volume 19 | 20 | Module contents 21 | --------------- 22 | 23 | .. automodule:: mpris2.types 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | -------------------------------------------------------------------------------- /docs/mpris2.types.time_in_us.rst: -------------------------------------------------------------------------------- 1 | mpris2.types.time_in_us module 2 | ============================== 3 | 4 | .. automodule:: mpris2.types.time_in_us 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.types.uri.rst: -------------------------------------------------------------------------------- 1 | mpris2.types.uri module 2 | ======================= 3 | 4 | .. automodule:: mpris2.types.uri 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.types.volume.rst: -------------------------------------------------------------------------------- 1 | mpris2.types.volume module 2 | ========================== 3 | 4 | .. automodule:: mpris2.types.volume 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/mpris2.utils.rst: -------------------------------------------------------------------------------- 1 | mpris2.utils module 2 | =================== 3 | 4 | .. automodule:: mpris2.utils 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/types.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | MPRIS2 Types 3 | ============ 4 | 5 | .. autoclass:: mpris2.types.Loop_Status 6 | :members: 7 | 8 | .. autoclass:: mpris2.types.Metadata_Map 9 | :members: 10 | 11 | .. autoclass:: mpris2.types.Playback_Rate 12 | :members: 13 | 14 | .. autoclass:: mpris2.types.Playback_Status 15 | :members: 16 | 17 | .. autoclass:: mpris2.types.Playlist 18 | :members: 19 | 20 | .. autoclass:: mpris2.types.Maybe_Playlist 21 | :members: 22 | 23 | .. autoclass:: mpris2.types.Playlist_Id 24 | :members: 25 | 26 | .. autoclass:: mpris2.types.Playlist_Ordering 27 | :members: 28 | 29 | .. autoclass:: mpris2.types.Time_In_Us 30 | :members: 31 | 32 | .. autoclass:: mpris2.types.Uri 33 | :members: 34 | 35 | .. autoclass:: mpris2.types.Volume 36 | :members: -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. linkcheck to check all external links for integrity 37 | echo. doctest to run all doctests embedded in the documentation if enabled 38 | goto end 39 | ) 40 | 41 | if "%1" == "clean" ( 42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 43 | del /q /s %BUILDDIR%\* 44 | goto end 45 | ) 46 | 47 | if "%1" == "html" ( 48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 49 | if errorlevel 1 exit /b 1 50 | echo. 51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 52 | goto end 53 | ) 54 | 55 | if "%1" == "dirhtml" ( 56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 57 | if errorlevel 1 exit /b 1 58 | echo. 59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 60 | goto end 61 | ) 62 | 63 | if "%1" == "singlehtml" ( 64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 68 | goto end 69 | ) 70 | 71 | if "%1" == "pickle" ( 72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished; now you can process the pickle files. 76 | goto end 77 | ) 78 | 79 | if "%1" == "json" ( 80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished; now you can process the JSON files. 84 | goto end 85 | ) 86 | 87 | if "%1" == "htmlhelp" ( 88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can run HTML Help Workshop with the ^ 92 | .hhp project file in %BUILDDIR%/htmlhelp. 93 | goto end 94 | ) 95 | 96 | if "%1" == "qthelp" ( 97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 98 | if errorlevel 1 exit /b 1 99 | echo. 100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 101 | .qhcp project file in %BUILDDIR%/qthelp, like this: 102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\MPRIS2.qhcp 103 | echo.To view the help file: 104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\MPRIS2.ghc 105 | goto end 106 | ) 107 | 108 | if "%1" == "devhelp" ( 109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 110 | if errorlevel 1 exit /b 1 111 | echo. 112 | echo.Build finished. 113 | goto end 114 | ) 115 | 116 | if "%1" == "epub" ( 117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 118 | if errorlevel 1 exit /b 1 119 | echo. 120 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 121 | goto end 122 | ) 123 | 124 | if "%1" == "latex" ( 125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 129 | goto end 130 | ) 131 | 132 | if "%1" == "text" ( 133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The text files are in %BUILDDIR%/text. 137 | goto end 138 | ) 139 | 140 | if "%1" == "man" ( 141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 145 | goto end 146 | ) 147 | 148 | if "%1" == "texinfo" ( 149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 150 | if errorlevel 1 exit /b 1 151 | echo. 152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 153 | goto end 154 | ) 155 | 156 | if "%1" == "gettext" ( 157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 158 | if errorlevel 1 exit /b 1 159 | echo. 160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 161 | goto end 162 | ) 163 | 164 | if "%1" == "changes" ( 165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 166 | if errorlevel 1 exit /b 1 167 | echo. 168 | echo.The overview file is in %BUILDDIR%/changes. 169 | goto end 170 | ) 171 | 172 | if "%1" == "linkcheck" ( 173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 174 | if errorlevel 1 exit /b 1 175 | echo. 176 | echo.Link check complete; look for any errors in the above output ^ 177 | or in %BUILDDIR%/linkcheck/output.txt. 178 | goto end 179 | ) 180 | 181 | if "%1" == "doctest" ( 182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 183 | if errorlevel 1 exit /b 1 184 | echo. 185 | echo.Testing of doctests in the sources finished, look at the ^ 186 | results in %BUILDDIR%/doctest/output.txt. 187 | goto end 188 | ) 189 | 190 | :end 191 | -------------------------------------------------------------------------------- /mpris2/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF8 -* 3 | ''' 4 | This is a copy of mprisV2.2 documentation 5 | 6 | http://specifications.freedesktop.org/mpris-spec/latest/ 7 | 8 | That also works as python lib. 9 | 10 | 11 | 12 | Version 2.2 13 | =========== 14 | Copyright © 2006-2010 the VideoLAN team(Mirsal Ennaime, Rafaël Carré, Jean-Paul Saman) 15 | 16 | Copyright © 2005-2008 Milosz Derezynski 17 | 18 | Copyright © 2008 Nick Welch 19 | 20 | Copyright © 2010-2012 Alex Merry 21 | 22 | This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. 23 | 24 | This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 25 | 26 | You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 27 | 28 | 29 | 30 | About 31 | ===== 32 | The Media Player Remote Interfacing Specification is a standard D-Bus interface which aims to provide a common programmatic API for controlling media players. 33 | 34 | It provides a mechanism for compliant media players discovery, basic playback and media player state control as well as a tracklist interface which is used to add context to the current item. 35 | 36 | 37 | 38 | Changes 39 | ======= 40 | Changes (Permalink) 41 | 42 | From 2.1 to 2.2: 43 | 44 | * Added the optional Fullscreen and CanSetFullscreen properties to the org.mpris.MediaPlayer2 interface. 45 | * The path /org/mpris/MediaPlayer2/TrackList/NoTrack now represents "no track" where required in the org.mpris.MediaPlayer2.TrackList interface (since empty paths are not allowed by D-Bus). 46 | * The suggested unique instance identifier no longer violates the D-Bus specification by begining with a digit. 47 | 48 | 49 | From 2.0 to 2.1: 50 | 51 | * Added the optional org.mpris.MediaPlayer2.Playlists interface. 52 | 53 | 54 | 55 | Bus Name Policy 56 | =============== 57 | Each media player *must* request a unique bus name which begins with *org.mpris.MediaPlayer2*. For example: 58 | 59 | * org.mpris.MediaPlayer2.audacious 60 | * org.mpris.MediaPlayer2.vlc 61 | * org.mpris.MediaPlayer2.bmp 62 | * org.mpris.MediaPlayer2.xmms2 63 | 64 | This allows clients to list available media players (either already running or which can be started via D-Bus activation) 65 | 66 | In the case where the media player allows multiple instances running simultaneously, each additional instance should request a unique bus name, adding a dot and a unique identifier to its usual bus name, such as one based on a UNIX process id. For example, this could be: 67 | 68 | * org.mpris.MediaPlayer2.vlc.7389 69 | 70 | Note: According to the D-Bus specification, the unique identifier "must only contain the ASCII characters '[A-Z][a-z][0-9]_-'" and "must not begin with a digit". 71 | 72 | Entry point 73 | =========== 74 | The media player *must* expose the */org/mpris/MediaPlayer2* object path, which *must* implement the following interfaces: 75 | 76 | * :doc:`org.mpris.MediaPlayer2<../mpris2.mediaplayer2>` 77 | * :doc:`org.mpris.MediaPlayer2.Player<../mpris2.player>` 78 | 79 | The */org/mpris/MediaPlayer2* object may implement the :doc:`org.mpris.MediaPlayer2.TrackList<../mpris2.tracklist>` interface. 80 | 81 | The */org/mpris/MediaPlayer2* object may implement the :doc:`org.mpris.MediaPlayer2.Playlists<../mpris2.playlists>` interface. 82 | 83 | 84 | The PropertiesChanged signal 85 | ============================ 86 | The MPRIS uses the org.freedesktop.DBus.Properties.PropertiesChanged signal to notify clients of changes in the media player state. If a client implementation uses D-Bus bindings which do not support this signal, then it should connect to it manually. If a media player implementation uses D-Bus bindings which do not support this signal, then it should send it manually 87 | 88 | 89 | Corrections 90 | =========== 91 | 2010-09-26: Added EmitsChangedSignal annotation to Volume property on the Player interface. 92 | 93 | 2011-01-26: Added PlaylistChanged signal to the Playlists interface. 94 | 95 | 96 | Interfaces 97 | ========== 98 | * :doc:`org.mpris.MediaPlayer2<../mpris2.mediaplayer2>` 99 | * :doc:`org.mpris.MediaPlayer2.Player<../mpris2.player>` 100 | * :doc:`org.mpris.MediaPlayer2.Playlists<../mpris2.playlists>` 101 | * :doc:`org.mpris.MediaPlayer2.TrackList<../mpris2.tracklist>` 102 | 103 | 104 | ''' 105 | 106 | from .interfaces import Interfaces 107 | from .mediaplayer2 import MediaPlayer2 108 | from .player import Player 109 | from .playlists import Playlists 110 | from .tracklist import TrackList 111 | from .utils import get_players_uri 112 | from .some_players import Some_Players as SomePlayers 113 | -------------------------------------------------------------------------------- /mpris2/__main__.py: -------------------------------------------------------------------------------- 1 | from . import Interfaces 2 | from . import MediaPlayer2 3 | from . import Player 4 | from . import Playlists 5 | from . import TrackList 6 | 7 | 8 | if __name__ == '__main__': 9 | print(Interfaces()) 10 | print(MediaPlayer2()) 11 | print(Player()) 12 | print(Playlists()) 13 | print(TrackList()) 14 | -------------------------------------------------------------------------------- /mpris2/decorator/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is not part of specification 3 | 4 | Helper class to make it work as python lib 5 | ''' 6 | 7 | 8 | from .attribute import DbusAttr 9 | from .interface import DbusInterface 10 | from .method import DbusMethod 11 | from .signal import DbusSignal 12 | from .utils import get_mainloop, get_uri, implements, \ 13 | list_all_interface, list_interfaces, list_paths 14 | -------------------------------------------------------------------------------- /mpris2/decorator/__main__.py: -------------------------------------------------------------------------------- 1 | from . import DbusAttr 2 | from . import DbusInterface 3 | from . import DbusMethod 4 | from . import DbusSignal 5 | 6 | if __name__ == '__main__': 7 | print(DbusAttr()) 8 | print(DbusInterface()) 9 | print(DbusMethod()) 10 | print(DbusSignal()) 11 | -------------------------------------------------------------------------------- /mpris2/decorator/attribute.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is not part of specification 3 | 4 | Helper class to make it work as python lib 5 | ''' 6 | 7 | from .base import Decorator, ATTR_KEY 8 | 9 | 10 | class DbusAttr(Decorator): 11 | ''' 12 | https://docs.python.org/2/howto/descriptor.html#properties 13 | ''' 14 | 15 | def __init__(self, meth=None, produces=lambda resp: resp): 16 | self.attr = meth 17 | self.produces = produces 18 | self._update_me(meth) 19 | 20 | def __call__(self, meth): 21 | self.attr = meth 22 | self._update_me(meth) 23 | return self 24 | 25 | def __get__(self, obj, objtype=None): 26 | #static call 27 | if not obj: 28 | return self 29 | 30 | _dbus = getattr(obj, ATTR_KEY) 31 | props = _dbus.properties 32 | iface = _dbus.iface 33 | result = props.Get(iface, self.attr.__name__) 34 | produces = self.produces 35 | return produces(result) 36 | 37 | def __set__(self, obj, value): 38 | if obj: 39 | _dbus = getattr(obj, ATTR_KEY) 40 | props = _dbus.properties 41 | iface = _dbus.iface 42 | props.Set(iface, self.attr.__name__, value) 43 | else: #static call 44 | self.attr = value 45 | 46 | def __delete__(self, obj): 47 | raise AttributeError('can not delete attribute') 48 | 49 | 50 | if __name__ == '__main__': 51 | # examples 52 | from .interface import DbusInterface 53 | 54 | @DbusInterface('org.mpris.MediaPlayer2', 55 | '/org/mpris/MediaPlayer2') 56 | class Example(object): 57 | @DbusAttr 58 | def Identity(self): 59 | pass 60 | 61 | d = Example( 62 | dbus_interface_info={ 63 | 'dbus_uri': 'org.mpris.MediaPlayer2.vlc'}) 64 | 65 | assert d.Identity == 'VLC media player' 66 | -------------------------------------------------------------------------------- /mpris2/decorator/base.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is not part of specification 3 | 4 | Helper class to make it work as python lib 5 | ''' 6 | 7 | I_PROP = 'org.freedesktop.DBus.Properties' 8 | ARG_KEY = 'dbus_interface_info' 9 | ATTR_KEY = '_dbus_interface_info' 10 | 11 | 12 | class Decorator(object): 13 | def _update_me(self, target=None): 14 | if hasattr(target, "__doc__"): 15 | self.__doc__ = target.__doc__ 16 | if hasattr(target, "__name__"): 17 | self.__name__ = target.__name__ 18 | if hasattr(target, "__bases__"): 19 | self.__bases__ = target.__bases__ 20 | -------------------------------------------------------------------------------- /mpris2/decorator/interface.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is not part of specification 3 | 4 | Helper class to make it work as python lib 5 | ''' 6 | 7 | import dbus 8 | from functools import wraps 9 | from .base import Decorator, ARG_KEY, I_PROP, ATTR_KEY 10 | 11 | 12 | class _DbusInfoProperty(object): 13 | def __init__(self, iface=None, path=None, 14 | uri=None, dbus_object=None, session=None, wrapped=None): 15 | self.iface = iface 16 | self.path = path 17 | self.uri = uri 18 | self.object = dbus_object 19 | self.session = session 20 | self.wrapped = wrapped 21 | self.interface = None 22 | self.properties = None 23 | 24 | if not self.object: 25 | bus = self.session = self.session or dbus.SessionBus() 26 | self.object = bus.get_object(self.uri, self.path) 27 | if not self.interface: 28 | self.interface = dbus.Interface(self.object, 29 | dbus_interface=self.iface) 30 | if not self.properties: 31 | self.properties = dbus.Interface(self.object, I_PROP) 32 | 33 | def reconnect(self, session=None): 34 | ''' 35 | Required if you need update session/proxy object/interfaces 36 | ''' 37 | 38 | session = session or self.session 39 | if session == self.session: 40 | self.session.close() 41 | session = self.session = dbus.SessionBus() 42 | self.object = session.get_object(self.uri, self.path) 43 | self.interface = dbus.Interface(self.object, dbus_interface=self.iface) 44 | self.properties = dbus.Interface(self.object, I_PROP) 45 | 46 | 47 | class DbusInterface(Decorator): 48 | 49 | def __init__(self, iface=None, path=None, 50 | uri=None, dbus_object=None, session=None): 51 | self.iface = iface 52 | self.path = path 53 | self.uri = uri 54 | self.object = dbus_object 55 | self.session = session 56 | self.wrapped = None 57 | 58 | def __call__(self, meth): 59 | ''' Called when any decorated class is loaded''' 60 | self.wrapped = meth 61 | self._update_me(meth) 62 | 63 | @wraps(meth) 64 | def dbusWrapedInterface(*args, **kw): 65 | _args = kw.get(ARG_KEY, {}) 66 | info_property = _DbusInfoProperty( 67 | iface=_args.get('dbus_iface', self.iface), 68 | path=_args.get('dbus_path', self.path), 69 | uri=_args.get('dbus_uri', self.uri), 70 | dbus_object =_args.get('dbus_object', self.object), 71 | session =_args.get('dbus_session', self.session), 72 | wrapped=self.wrapped 73 | ) 74 | if ARG_KEY in kw: 75 | del kw[ARG_KEY] 76 | 77 | return self.dbusWrapedInterface(info_property, *args, **kw) 78 | 79 | return dbusWrapedInterface 80 | 81 | def dbusWrapedInterface(self, info_property, *args, **kw): 82 | ''' Called when some decoreted class was called 83 | Inject attrs from decorator at new object then return object 84 | 85 | @param *args: list of args to call constructor 86 | @param **kw: dict of keywords, can redefine class default parameters 87 | @return: instance of decoreted class, with new attributes 88 | @see: mpris2.mediaplayer2 to see some examples 89 | ''' 90 | #call decorated class constructor 91 | new_obj = self.wrapped(*args, **kw) 92 | if new_obj: 93 | setattr(new_obj, ATTR_KEY, info_property) 94 | elif len(args) > 0: 95 | setattr(args[0], ATTR_KEY, info_property) 96 | 97 | return new_obj 98 | 99 | 100 | if __name__ == '__main__': 101 | # examples 102 | @DbusInterface('org.freedesktop.DBus', '/') 103 | class Example(object): 104 | pass 105 | 106 | 107 | d = Example( 108 | dbus_interface_info={ 109 | 'dbus_uri': 'org.freedesktop.DBus'}) 110 | assert d._dbus_interface_info.iface == 'org.freedesktop.DBus' 111 | assert d._dbus_interface_info.path == '/' 112 | assert d._dbus_interface_info.uri == 'org.freedesktop.DBus' 113 | 114 | class ExempleToo(object): 115 | @DbusInterface('org.freedesktop.DBus', '/') 116 | def __init__(self): 117 | pass 118 | 119 | dd = ExempleToo( 120 | dbus_interface_info={ 121 | 'dbus_uri': 'org.freedesktop.DBus'}) 122 | 123 | assert dd._dbus_interface_info.iface == 'org.freedesktop.DBus' 124 | assert dd._dbus_interface_info.path == '/' 125 | assert dd._dbus_interface_info.uri == 'org.freedesktop.DBus' 126 | -------------------------------------------------------------------------------- /mpris2/decorator/method.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is not part of specification 3 | 4 | Helper class to make it work as python lib 5 | ''' 6 | 7 | from .base import Decorator, ATTR_KEY 8 | 9 | 10 | def kw_to_dbus(**kw): 11 | return kw 12 | 13 | def args_to_dbus(*args): 14 | return args 15 | 16 | 17 | class DbusMethod(Decorator): 18 | 19 | def __init__(self, meth=None, 20 | iface=None, 21 | produces=lambda resp: resp, 22 | args_to_dbus=args_to_dbus, 23 | kw_to_dbus=kw_to_dbus, 24 | std_args=(), 25 | std_kwds={}): 26 | self.meth = meth 27 | self.handler = None 28 | self.produces = produces 29 | self.iface = iface 30 | self.args_to_dbus = args_to_dbus 31 | self.kw_to_dbus = kw_to_dbus 32 | self.std_args = std_args 33 | self.std_kwds = std_kwds 34 | self.obj = None 35 | self._update_me(meth) 36 | 37 | def __call__(self, meth=None): 38 | self.meth = meth 39 | self._update_me(meth) 40 | return self 41 | 42 | def __get__(self, obj=None, cls=None): 43 | if obj is None: 44 | return self 45 | self.obj = obj 46 | return self._call_dbus 47 | 48 | def _call_dbus(self, *args, **kwds): 49 | _dbus = getattr(self.obj, ATTR_KEY) 50 | if self.iface: 51 | iface = self.iface 52 | else: 53 | iface = _dbus.iface 54 | bus_obj = _dbus.object 55 | bus_meth = bus_obj.get_dbus_method(self.meth.__name__, iface) 56 | _args = self.merge_args(args, self.std_args) 57 | args = self.convert_args_to_dbus_args(*_args) 58 | _kwds = self.std_kwds.copy() 59 | _kwds.update(kwds) 60 | kwds = self.convert_kw_to_dbus_kw(**_kwds) 61 | result = bus_meth(*args, **kwds) 62 | return self.produces(result) 63 | 64 | @classmethod 65 | def merge_args(cls, args, std_args): 66 | _len = len(std_args) - len(args) 67 | return args + std_args[-_len:] if _len > 0 else args 68 | 69 | @classmethod 70 | def merge_kwds(cls, kwds, std_kwds): 71 | _kwds = std_kwds.copy() 72 | _kwds.update(kwds) 73 | return _kwds 74 | 75 | def convert_args_to_dbus_args(self, *args): 76 | args_to_dbus = self.args_to_dbus 77 | if callable(args_to_dbus): 78 | return args_to_dbus(*args) 79 | 80 | #iterate over args 81 | result = [] 82 | for arg in args: 83 | i = args.index(arg) 84 | if i < len(args_to_dbus): 85 | make = args_to_dbus[i] 86 | if callable(make): 87 | arg = make(arg) 88 | result.append(arg) 89 | return tuple(result) 90 | 91 | def convert_kw_to_dbus_kw(self, **kw): 92 | kw_to_dbus = self.kw_to_dbus 93 | if callable(kw_to_dbus): 94 | return kw_to_dbus(**kw) 95 | 96 | if hasattr(self.kw_to_dbus, 'keys'): 97 | for key, val in kw.items(): 98 | make = kw_to_dbus.get(key, lambda v: v) 99 | kw[key] = make(val) 100 | return kw 101 | 102 | 103 | if __name__ == '__main__': 104 | # examples 105 | from .interface import DbusInterface 106 | @DbusInterface('org.freedesktop.DBus', '/') 107 | class Example(object): 108 | 109 | @DbusMethod 110 | def GetId(self): 111 | pass 112 | 113 | @DbusMethod 114 | def GetNameOwner(self, name): 115 | pass 116 | 117 | d = Example( 118 | dbus_interface_info={ 119 | 'dbus_uri': 'org.freedesktop.DBus'}) 120 | assert d.GetId() 121 | assert d.GetNameOwner('org.freedesktop.DBus') == 'org.freedesktop.DBus' 122 | -------------------------------------------------------------------------------- /mpris2/decorator/signal.py: -------------------------------------------------------------------------------- 1 | from .base import Decorator, ATTR_KEY 2 | 3 | 4 | class DbusSignal(Decorator): 5 | ''' 6 | https://docs.python.org/2/howto/descriptor.html#properties 7 | ''' 8 | 9 | def __init__(self, meth=None, iface=None): 10 | self.attr = meth 11 | self.handler = None 12 | self.iface = iface 13 | self._update_me(meth) 14 | 15 | def __call__(self, meth): 16 | self.attr = meth 17 | self._update_me(meth) 18 | return self 19 | 20 | def __get__(self, obj, objtype=None): 21 | if obj: 22 | return self.handler 23 | 24 | #static call 25 | return self 26 | 27 | def __set__(self, obj, value): 28 | if obj: 29 | _dbus = getattr(obj, ATTR_KEY) 30 | interface = _dbus.interface 31 | def handle(*args, **kwds): 32 | h = self.handler 33 | h and h(*args, **kwds) 34 | 35 | if not self.handler: 36 | interface.connect_to_signal(self.attr.__name__, handle, 37 | dbus_interface=self.iface) 38 | self.handler = value 39 | else: #static call 40 | self.attr = value 41 | 42 | def __delete__(self, obj): 43 | self.handler = None 44 | 45 | 46 | if __name__ == '__main__': 47 | from .interface import DbusInterface 48 | from .utils import get_mainloop 49 | mainloop = get_mainloop() 50 | print('mainloop', mainloop) 51 | 52 | @DbusInterface('org.mpris.MediaPlayer2.player', 53 | '/org/mpris/MediaPlayer2') 54 | class Example(object): 55 | 56 | @DbusSignal 57 | def Seeked(self): 58 | pass 59 | 60 | 61 | d = Example( 62 | dbus_interface_info={ 63 | 'dbus_uri': 'org.mpris.MediaPlayer2.gmusicbrowser'}) 64 | 65 | def handler(self, *args): 66 | print(args) 67 | 68 | d.Seeked = handler 69 | mainloop and mainloop.run() 70 | -------------------------------------------------------------------------------- /mpris2/decorator/utils.py: -------------------------------------------------------------------------------- 1 | ''' 2 | utils functions 3 | ''' 4 | 5 | import dbus, re 6 | import xml.etree.ElementTree as ET 7 | 8 | 9 | I_INSPECT = 'org.freedesktop.DBus.Introspectable' 10 | 11 | def _match_uri(name, pattern='.+'): 12 | ''' 13 | Filter logic for get_players and get_player_uri 14 | @param name: string name to test 15 | @param pattern=None: string RegEx to test 16 | @return: boolean 17 | ''' 18 | return re.match(pattern, name) 19 | 20 | def get_session(): 21 | ''' 22 | @return: dbus.SessionBus.get_session() 23 | ''' 24 | return dbus.SessionBus.get_session() 25 | 26 | def get_uri(pattern='.'): 27 | ''' 28 | Return string of player bus name 29 | @param pattern=None: string RegEx that filter response 30 | @return: array string of players bus name 31 | ''' 32 | for item in get_session().list_names(): 33 | if _match_uri(item, pattern): 34 | yield item 35 | 36 | 37 | def get_mainloop(): 38 | ''' 39 | @return: mainloop (with 'run' method) 40 | ''' 41 | try: 42 | from dbus.mainloop.glib import DBusGMainLoop 43 | DBusGMainLoop(set_as_default=True) 44 | try: 45 | import gobject 46 | return gobject.MainLoop() 47 | except: 48 | pass 49 | 50 | try: 51 | import gi.repository.GLib 52 | return gi.repository.GLib.MainLoop() 53 | except: 54 | pass 55 | except: 56 | pass 57 | 58 | class PyQtMainLoop(): 59 | def __init__(self, app): 60 | self.app = app 61 | 62 | def run(self): 63 | if hasattr(self.app, 'exec_'): 64 | self.app.exec_() 65 | if hasattr(self.app, 'exec'): 66 | method = getattr(self.app, 'exec') 67 | method(self.app) 68 | 69 | 70 | try: 71 | from dbus.mainloop.qt import DBusQtMainLoop 72 | DBusQtMainLoop(set_as_default=True) 73 | try: 74 | from PySide.QtGui import QApplication 75 | return PyQtMainLoop(QApplication([])) 76 | except: 77 | pass 78 | 79 | try: 80 | from PyQt5.QtWidgets import QApplication 81 | return PyQtMainLoop(QApplication([])) 82 | except: 83 | pass 84 | 85 | try: 86 | from PyQt4 import Qt 87 | return PyQtMainLoop(Qt.QApplication([])) 88 | except: 89 | pass 90 | except: 91 | pass 92 | 93 | return None 94 | 95 | def _introspect_uri(bus_name, path, bus): 96 | bus = bus or dbus.SessionBus.get_session() 97 | path = path.replace('//', '/') or '/' 98 | obj = bus.get_object(bus_name, path) 99 | xml = obj.Introspect(dbus_interface=I_INSPECT) 100 | return ET.fromstring(xml) 101 | 102 | def list_paths(bus_name, path='/', bus=None): 103 | ''' 104 | Return generator with all subpaths of uri 105 | @param bus_name: string dbus object name 106 | @param path='/': string dbus object path 107 | @param bus=None: Conn dbus.Conn (commonly SessionBus or SystemBus) 108 | @return: generator with all paths that has interfaces 109 | ''' 110 | nodes = _introspect_uri(bus_name, path, bus) 111 | if [iface for iface in nodes.findall('interface')]: 112 | yield path.replace('//', '/') 113 | 114 | for node in nodes.findall('node'): 115 | sub_path = path + '/'+ node.attrib['name'] 116 | for path_name in list_paths(bus_name, sub_path, bus): 117 | yield path_name 118 | 119 | def list_interfaces(bus_name, path, bus=None): 120 | ''' 121 | Return generator with all interfaces of path 122 | @param bus_name: string dbus object name 123 | @param path: string dbus object path 124 | @param bus=None: Conn dbus.Conn (commonly SessionBus or SystemBus) 125 | @return: generator with all interfaces of path 126 | ''' 127 | nodes = _introspect_uri(bus_name, path, bus) 128 | for interface in nodes.findall('interface'): 129 | yield interface.attrib['name'] 130 | 131 | def list_all_interface(bus_name, path='/', bus=None): 132 | ''' 133 | Return generator with all interfaces of path and subpaths 134 | @param bus_name: string dbus object name 135 | @param path: string dbus object path 136 | @param bus=None: Conn dbus.Conn (commonly SessionBus or SystemBus) 137 | @return: generator with all interfaces of path and subpaths 138 | ''' 139 | paths = list_paths(bus_name, path, bus) 140 | for sub_path in paths: 141 | for interface in list_interfaces(bus_name, sub_path, bus): 142 | yield sub_path, interface 143 | 144 | def implements(uri, interface, path, bus=None): 145 | ''' 146 | ''' 147 | for iface in list_interfaces(uri, path, bus): 148 | if iface == interface: 149 | return True 150 | 151 | 152 | if __name__ == '__main__': 153 | uri = None 154 | for uri in get_uri(): 155 | if uri.count(':') == 0: 156 | print(uri) 157 | 158 | if uri: 159 | for interface in list_all_interface(uri, '/'): 160 | print('\t', interface) 161 | else: 162 | print('Nothing running') 163 | -------------------------------------------------------------------------------- /mpris2/interfaces.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/2.2/#Interfaces 5 | ''' 6 | 7 | 8 | class Interfaces(object): 9 | ''' 10 | This class contains the constants defined at index of MPRIS2 definition: 11 | 12 | 13 | **Interfaces:** 14 | 15 | * MEDIA_PLAYER 16 | 'org.mpris.MediaPlayer2' 17 | * TRACK_LIST 18 | 'org.mpris.MediaPlayer2.TrackList' 19 | * PLAYER 20 | 'org.mpris.MediaPlayer2.Player' 21 | * PLAYLISTS 22 | 'org.mpris.MediaPlayer2.Playlists' 23 | * PROPERTIES 24 | 'org.freedesktop.DBus.Properties' 25 | 26 | 27 | **Signals:** 28 | 29 | * SIGNAL 30 | 'PropertiesChanged' 31 | 32 | 33 | **Objects:** 34 | 35 | * OBJECT_PATH 36 | '/org/mpris/MediaPlayer2' 37 | 38 | ''' 39 | #interface 40 | MEDIA_PLAYER = 'org.mpris.MediaPlayer2' 41 | TRACK_LIST = 'org.mpris.MediaPlayer2.TrackList' 42 | PLAYER = 'org.mpris.MediaPlayer2.Player' 43 | PLAYLISTS = 'org.mpris.MediaPlayer2.Playlists' 44 | PROPERTIES = 'org.freedesktop.DBus.Properties' 45 | #signal 46 | SIGNAL = 'PropertiesChanged' 47 | #Object 48 | OBJECT_PATH = '/org/mpris/MediaPlayer2' 49 | -------------------------------------------------------------------------------- /mpris2/mediaplayer2.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/latest/Media_Player.html 5 | ''' 6 | 7 | from .decorator import DbusAttr 8 | from .decorator import DbusInterface 9 | from .decorator import DbusMethod 10 | from .decorator import DbusSignal 11 | from .interfaces import Interfaces 12 | 13 | 14 | class MediaPlayer2(Interfaces): 15 | ''' 16 | Interface for MediaPlayer2 (org.mpris.MediaPlayer2) 17 | ''' 18 | PROPERTIES_CAN_QUIT = 'CanQuit' 19 | PROPERTIES_CAN_RAISE = 'Identity' 20 | PROPERTIES_HAS_TRACK_LIST = 'HasTrackList' 21 | PROPERTIES_IDENTITY = 'Identity' 22 | PROPERTIES_DESKTOP_ENTRY = 'DesktopEntry' 23 | PROPERTIES_SUPPORTED_URI_SCHEMES = 'SupportedUriSchemes' 24 | PROPERTIES_SUPPORTED_MINE_TYPES = 'SupportedMimeTypes' 25 | SIGNALS_PROPERTIES_CHANGED = 'PropertiesChanged' 26 | 27 | @DbusInterface(Interfaces.MEDIA_PLAYER, Interfaces.OBJECT_PATH) 28 | def __init__(self): 29 | '''Constructor''' 30 | 31 | @DbusMethod 32 | def Raise(self): 33 | ''' 34 | Brings the media player's user interface to the front using any appropriate mechanism available. 35 | 36 | The media player may be unable to control how its user interface is displayed, or it may not have a graphical user interface at all. In this case, the Identity property is false and this method does nothing. 37 | ''' 38 | 39 | @DbusMethod 40 | def Quit(self): 41 | ''' 42 | Causes the media player to stop running. 43 | 44 | The media player may refuse to allow clients to shut it down. In this case, the CanQuit property is false and this method does nothing. 45 | 46 | ..note:: 47 | Media players which can be D-Bus activated, or for which there is no sensibly easy way to terminate a running instance (via the main interface or a notification area icon for example) should allow clients to use this method. Otherwise, it should not be needed. 48 | 49 | If the media player does not have a UI, this should be implemented 50 | ''' 51 | 52 | @DbusAttr 53 | def CanQuit(self): 54 | ''' 55 | **Returns** 56 | 57 | Read only 58 | Inject attrs from decorator at new object then return obje 59 | 60 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 61 | 62 | If false, calling Quit will have no effect, and may raise a NotSupported error. If true, calling Quit will cause the media application to attempt to quit (although it may still be prevented from quitting by the user, for example). 63 | ''' 64 | 65 | @DbusAttr 66 | def Fullscreen(self): 67 | ''' 68 | **Returns** 69 | 70 | Read Write 71 | Whether the media player is occupying the fullscreen. 72 | 73 | This property is optional. Clients should handle its absence gracefully. 74 | 75 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 76 | 77 | This is typically used for videos. A value of true indicates that the media player is taking up the full screen. 78 | 79 | Media center software may well have this value fixed to true 80 | 81 | If CanSetFullscreen is true, clients may set this property to true to tell the media player to enter fullscreen mode, or to false to return to windowed mode. 82 | 83 | If CanSetFullscreen is false, then attempting to set this property should have no effect, and may raise an error. However, even if it is true, the media player may still be unable to fulfil the request, in which case attempting to set this property will have no effect (but should not raise an error). 84 | 85 | Added in 2.2. 86 | ''' 87 | 88 | @DbusAttr 89 | def CanSetFullscreen(self): 90 | ''' 91 | **Returns** 92 | 93 | Read only 94 | If false, attempting to set Fullscreen will have no effect, and may raise an error. If true, attempting to set Fullscreen will not raise an error, and (if it is different from the current value) will cause the media player to attempt to enter or exit fullscreen mode. 95 | 96 | This property is optional. Clients should handle its absence gracefully. 97 | 98 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 99 | 100 | Added in 2.2. 101 | 102 | ..note:: 103 | Note that the media player may be unable to fulfil the request. In this case, the value will not change. If the media player knows in advance that it will not be able to fulfil the request, however, this property should be false. 104 | ''' 105 | 106 | @DbusAttr 107 | def CanRaise(self): 108 | ''' 109 | **Returns** 110 | 111 | Read only 112 | If false, calling Raise will have no effect, and may raise a NotSupported error. If true, calling Raise will cause the media application to attempt to bring its user interface to the front, although it may be prevented from doing so (by the window manager, for example). 113 | 114 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 115 | ''' 116 | 117 | @DbusAttr 118 | def HasTrackList(self): 119 | ''' 120 | **Returns** 121 | 122 | Read only 123 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 124 | 125 | Indicates whether the /org/mpris/MediaPlayer2 object implements the org.mpris.MediaPlayer2.TrackList interface. 126 | ''' 127 | 128 | @DbusAttr 129 | def DesktopEntry(self): 130 | ''' 131 | **Returns** 132 | 133 | Read only 134 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 135 | 136 | The basename of an installed .desktop file which complies with the Desktop entry specification, with the '.desktop' extension stripped. 137 | 138 | Example: The desktop entry file is '/usr/share/applications/vlc.desktop', and this property contains 'vlc' 139 | 140 | This property is optional. Clients should handle its absence gracefully 141 | ''' 142 | 143 | @DbusAttr 144 | def Identity(self): 145 | ''' 146 | **Returns** 147 | 148 | Read only 149 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 150 | 151 | If false, calling Raise will have no effect, and may raise a NotSupported error. If true, calling Raise will cause the media application to attempt to bring its user interface to the front, although it may be prevented from doing so (by the window manager, for example). 152 | ''' 153 | 154 | @DbusAttr 155 | def SupportedUriSchemes(self): 156 | ''' 157 | **Returns** 158 | 159 | Read only 160 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 161 | 162 | The URI schemes supported by the media player. 163 | 164 | This can be viewed as protocols supported by the player in almost all cases. Almost every media player will include support for the 'file' scheme. Other common schemes are 'http' and 'rtsp'. 165 | 166 | Note that URI schemes should be lower-case. 167 | 168 | .. note:: 169 | This is important for clients to know when using the editing capabilities of the Playlist interface, for example. 170 | ''' 171 | 172 | @DbusAttr 173 | def SupportedMimeTypes(self): 174 | ''' 175 | **Returns** 176 | 177 | Read only 178 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 179 | 180 | The mime-types supported by the media player. 181 | 182 | Mime-types should be in the standard format (eg: audio/mpeg or application/ogg). 183 | 184 | .. note:: 185 | This is important for clients to know when using the editing capabilities of the Playlist interface, for example. 186 | ''' 187 | 188 | @DbusSignal(iface=Interfaces.PROPERTIES) 189 | def PropertiesChanged(self, *args, **kw): 190 | ''' 191 | **Parameters:** 192 | 193 | * args - list 194 | unnamed parameters passed by dbus signal 195 | * kw - dict 196 | named parameters passed by dbus signal 197 | 198 | Every time that some property change, signal will be called 199 | ''' 200 | 201 | 202 | if __name__ == '__main__': 203 | from .utils import get_players_uri, implements, get_mainloop 204 | mainloop = get_mainloop() 205 | # to set signal handler 206 | # set_default_mainloop 207 | # need to be called before instance creation 208 | 209 | for uri in get_players_uri(): 210 | if implements(uri, Interfaces.PLAYER): 211 | 212 | mp2 = MediaPlayer2(dbus_interface_info={ 213 | 'dbus_uri': uri 214 | }) 215 | print(mp2.SupportedUriSchemes) 216 | 217 | def another_handler(self, *args, **kw): 218 | print(args, '\n', kw) 219 | 220 | if mainloop: 221 | mp2.PropertiesChanged = another_handler 222 | mainloop.run() 223 | break 224 | else: 225 | print('no player with mediaplayer2 interface found') -------------------------------------------------------------------------------- /mpris2/player.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html 5 | ''' 6 | 7 | from .decorator import DbusAttr 8 | from .decorator import DbusInterface 9 | from .decorator import DbusMethod 10 | from .decorator import DbusSignal 11 | from .interfaces import Interfaces 12 | from .types import Time_In_Us 13 | from .types import Loop_Status 14 | from .types import Playback_Status 15 | from .types import Playback_Rate 16 | from .types import Metadata_Map 17 | from .types import Volume 18 | 19 | 20 | class Player(Interfaces): 21 | ''' 22 | This interface implements the methods for querying and providing basic control over what is currently playing. 23 | ''' 24 | 25 | @DbusInterface(Interfaces.PLAYER, Interfaces.OBJECT_PATH) 26 | def __init__(self): 27 | '''Constructor''' 28 | 29 | 30 | @DbusMethod 31 | def Next(self): 32 | ''' 33 | Skips to the next track in the tracklist. 34 | 35 | If there is no next track (and endless playback and track repeat are both off), stop playback. 36 | 37 | If playback is paused or stopped, it remains that way. 38 | 39 | If CanGoNext is false, attempting to call this method should have no effect. 40 | ''' 41 | 42 | 43 | @DbusMethod 44 | def Previous(self): 45 | ''' 46 | Skips to the previous track in the tracklist. 47 | 48 | If there is no previous track (and endless playback and track repeat are both off), stop playback. 49 | 50 | If playback is paused or stopped, it remains that way. 51 | 52 | If CanGoPrevious is false, attempting to call this method should have no effect. 53 | ''' 54 | 55 | 56 | @DbusMethod 57 | def Pause(self): 58 | ''' 59 | Pauses playback. 60 | 61 | If playback is already paused, this has no effect. 62 | 63 | Calling Play after this should cause playback to start again from the same position. 64 | 65 | If CanPause is false, attempting to call this method should have no effect. 66 | ''' 67 | 68 | 69 | @DbusMethod 70 | def PlayPause(self): 71 | ''' 72 | Pauses playback. 73 | 74 | If playback is already paused, resumes playback. 75 | 76 | If playback is stopped, starts playback. 77 | 78 | If CanPause is false, attempting to call this method should have no effect and raise an error. 79 | ''' 80 | 81 | 82 | @DbusMethod 83 | def Stop(self): 84 | ''' 85 | Stops playback. 86 | 87 | If playback is already stopped, this has no effect. 88 | 89 | Calling Play after this should cause playback to start again from the beginning of the track. 90 | 91 | If CanControl is false, attempting to call this method should have no effect and raise an error. 92 | ''' 93 | 94 | 95 | @DbusMethod 96 | def Play(self): 97 | ''' 98 | Starts or resumes playback. 99 | 100 | If already playing, this has no effect. 101 | 102 | If there is no track to play, this has no effect. 103 | 104 | If CanPlay is false, attempting to call this method should have no effect. 105 | ''' 106 | 107 | 108 | @DbusMethod 109 | def Seek(self, Offet): 110 | ''' 111 | **Parameters:** 112 | 113 | * Offset - x (Time_In_Us) 114 | The number of microseconds to seek forward. 115 | 116 | Seeks forward in the current track by the specified number of microseconds. 117 | 118 | A negative value seeks back. If this would mean seeking back further than the start of the track, the position is set to 0. 119 | 120 | If the value passed in would mean seeking beyond the end of the track, acts like a call to Next. 121 | 122 | If the CanSeek property is false, this has no effect. 123 | ''' 124 | 125 | 126 | @DbusMethod 127 | def SetPosition(self, TrackId, Position): 128 | ''' 129 | **Parameters** 130 | 131 | * TrackId - o (Track_Id) 132 | The currently playing track's identifier. 133 | 134 | If this does not match the id of the currently-playing track, the call is ignored as 'stale'. 135 | * Position - x (Time_In_Us) 136 | Track position in microseconds. 137 | 138 | This must be between 0 and . 139 | 140 | Sets the current track position in microseconds. 141 | 142 | If the Position argument is less than 0, do nothing. 143 | 144 | If the Position argument is greater than the track length, do nothing. 145 | 146 | If the CanSeek property is false, this has no effect. 147 | ''' 148 | 149 | 150 | @DbusMethod 151 | def OpenUri(self, Uri): 152 | ''' 153 | **Parameters:** 154 | 155 | * Uri - s (Uri) 156 | Uri of the track to load. Its uri scheme should be an element of the org.mpris.MediaPlayer2.SupportedUriSchemes property and the mime-type should match one of the elements of the org.mpris.MediaPlayer2.SupportedMimeTypes. 157 | 158 | Opens the Uri given as an argument 159 | 160 | If the playback is stopped, starts playing 161 | 162 | If the uri scheme or the mime-type of the uri to open is not supported, this method does nothing and may raise an error. In particular, if the list of available uri schemes is empty, this method may not be implemented. 163 | 164 | Clients should not assume that the Uri has been opened as soon as this method returns. They should wait until the mpris:trackid field in the Metadata property changes. 165 | 166 | If the media player implements the TrackList interface, then the opened track should be made part of the tracklist, the org.mpris.MediaPlayer2.TrackList.TrackAdded or org.mpris.MediaPlayer2.TrackList.TrackListReplaced signal should be fired, as well as the org.freedesktop.DBus.Properties.PropertiesChanged signal on the tracklist interface. 167 | ''' 168 | 169 | 170 | @DbusSignal 171 | def Seeked(self, Position): 172 | ''' 173 | **Parameters:** 174 | 175 | * Position - x (Time_In_Us) 176 | The new position, in microseconds. 177 | 178 | Indicates that the track position has changed in a way that is inconsistant with the current playing state. 179 | 180 | When this signal is not received, clients should assume that: 181 | 182 | * When playing, the position progresses according to the rate property. 183 | * When paused, it remains constant. 184 | 185 | This signal does not need to be emitted when playback starts or when the track changes, unless the track is starting at an unexpected position. An expected position would be the last known one when going from Paused to Playing, and 0 when going from Stopped to Playing. 186 | ''' 187 | return Time_In_Us(Position) 188 | 189 | @DbusSignal(iface=Interfaces.PROPERTIES) 190 | def PropertiesChanged(self, *args, **kw): 191 | ''' 192 | **Parameters** 193 | 194 | * args - list 195 | unnamed parameters passed by dbus signal 196 | * kw - dict 197 | named parameters passed by dbus signal 198 | 199 | Every time that some property change, signal will be called 200 | ''' 201 | 202 | 203 | @DbusAttr(produces=Playback_Status) 204 | def PlaybackStatus(self): 205 | ''' 206 | **Returns** 207 | 208 | Read only 209 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 210 | 211 | The current playback status. 212 | 213 | May be 'Playing', 'Paused' or 'Stopped'. 214 | ''' 215 | 216 | 217 | @DbusAttr(produces=Loop_Status) 218 | def LoopStatus(self): 219 | ''' 220 | **Returns** 221 | 222 | Read/Write 223 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 224 | 225 | The current loop / repeat status 226 | 227 | May be: 228 | 229 | * 'None' if the playback will stop when there are no more tracks to play 230 | * 'Track' if the current track will start again from the begining once it has finished playing 231 | * 'Playlist' if the playback loops through a list of tracks 232 | 233 | This property is optional, and clients should deal with NotSupported errors gracefully. 234 | 235 | If CanControl is false, attempting to set this property should have no effect and raise an error. 236 | ''' 237 | 238 | 239 | @DbusAttr(produces=Playback_Rate) 240 | def Rate(self): 241 | ''' 242 | **Returns** 243 | 244 | Read/Write 245 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 246 | 247 | The current playback rate. 248 | 249 | The value must fall in the range described by MinimumRate and MaximumRate, and must not be 0.0. If playback is paused, the PlaybackStatus property should be used to indicate this. A value of 0.0 should not be set by the client. If it is, the media player should act as though Pause was called. 250 | 251 | If the media player has no ability to play at speeds other than the normal playback rate, this must still be implemented, and must return 1.0. The MinimumRate and MaximumRate properties must also be set to 1.0. 252 | 253 | Not all values may be accepted by the media player. It is left to media player implementations to decide how to deal with values they cannot use; they may either ignore them or pick a 'best fit' value. Clients are recommended to only use sensible fractions or multiples of 1 (eg: 0.5, 0.25, 1.5, 2.0, etc). 254 | ''' 255 | 256 | 257 | @DbusAttr 258 | def Shuffle(self): 259 | ''' 260 | **Returns** 261 | 262 | Read/Write 263 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 264 | 265 | A value of false indicates that playback is progressing linearly through a playlist, while true means playback is progressing through a playlist in some other order. 266 | 267 | This property is optional, and clients should deal with NotSupported errors gracefully. 268 | 269 | If CanControl is false, attempting to set this property should have no effect and raise an error. 270 | ''' 271 | 272 | 273 | @DbusAttr(produces=Metadata_Map) 274 | def Metadata(self): 275 | ''' 276 | **Returns** 277 | 278 | Read only 279 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 280 | 281 | The metadata of the current element. 282 | 283 | If there is a current track, this must have a 'mpris:trackid' entry at the very least, which contains a string that uniquely identifies this track. 284 | 285 | See the type documentation for more details. 286 | ''' 287 | 288 | 289 | @DbusAttr(produces=Volume) 290 | def Volume(self): 291 | ''' 292 | **Returns** 293 | 294 | Read/Write 295 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 296 | 297 | The volume level. 298 | 299 | When setting, if a negative value is passed, the volume should be set to 0.0. 300 | 301 | If CanControl is false, attempting to set this property should have no effect and raise an error. 302 | ''' 303 | 304 | 305 | @DbusAttr 306 | def Position(self): 307 | ''' 308 | **Returns** 309 | 310 | Read only 311 | The org.freedesktop.DBus.Properties.PropertiesChanged signal is not emitted when this property changes. 312 | 313 | The current track position in microseconds, between 0 and the 'mpris:length' metadata entry (see Metadata). 314 | 315 | .. note:: 316 | If the media player allows it, the current playback position can be changed either the SetPosition method or the Seek method on this interface. If this is not the case, the CanSeek property is false, and setting this property has no effect and can raise an error. 317 | 318 | If the playback progresses in a way that is inconstistant with the Rate property, the Seeked signal is emited. 319 | ''' 320 | 321 | 322 | @DbusAttr 323 | def MinimumRate(self): 324 | ''' 325 | **Returns** 326 | 327 | Read only 328 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 329 | 330 | The minimum value which the Rate property can take. Clients should not attempt to set the Rate property below this value. 331 | 332 | Note that even if this value is 0.0 or negative, clients should not attempt to set the Rate property to 0.0. 333 | 334 | This value should always be 1.0 or less. 335 | ''' 336 | 337 | 338 | @DbusAttr 339 | def MaximumRate(self): 340 | ''' 341 | **Returns** 342 | 343 | Read only 344 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 345 | 346 | The maximum value which the Rate property can take. Clients should not attempt to set the Rate property above this value. 347 | 348 | This value should always be 1.0 or greater. 349 | ''' 350 | 351 | 352 | @DbusAttr 353 | def CanGoNext(self): 354 | ''' 355 | **Returns** 356 | 357 | Read only 358 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 359 | 360 | Whether the client can call the Next method on this interface and expect the current track to change. 361 | 362 | If CanControl is false, this property should also be false. 363 | ''' 364 | 365 | 366 | @DbusAttr 367 | def CanGoPrevious(self): 368 | ''' 369 | **Returns** 370 | 371 | Read only 372 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 373 | 374 | Whether the client can call the Previous method on this interface and expect the current track to change. 375 | 376 | If CanControl is false, this property should also be false. 377 | ''' 378 | 379 | 380 | @DbusAttr 381 | def CanPlay(self): 382 | ''' 383 | **Returns** 384 | 385 | Read only 386 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 387 | 388 | Whether playback can be started using Play or PlayPause. 389 | 390 | Note that this is related to whether there is a 'current track': the value should not depend on whether the track is currently paused or playing. In fact, if a track is currently playing CanControl is true), this should be true. 391 | 392 | If CanControl is false, this property should also be false. 393 | ''' 394 | 395 | 396 | @DbusAttr 397 | def CanPause(self): 398 | ''' 399 | **Returns** 400 | 401 | Read only 402 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 403 | 404 | Whether playback can be paused using Pause or PlayPause. 405 | 406 | Note that this is an intrinsic property of the current track: its value should not depend on whether the track is currently paused or playing. In fact, if playback is currently paused (and CanControl is true), this should be true. 407 | 408 | If CanControl is false, this property should also be false. 409 | ''' 410 | 411 | @DbusAttr 412 | def CanSeek(self): 413 | ''' 414 | **Returns** 415 | 416 | Read only 417 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 418 | 419 | Whether the client can control the playback position using Seek and SetPosition. This may be different for different tracks. 420 | 421 | If CanControl is false, this property should also be false. 422 | ''' 423 | 424 | @DbusAttr 425 | def CanControl(self): 426 | ''' 427 | **Returns** 428 | 429 | Read only 430 | The org.freedesktop.DBus.Properties.PropertiesChanged signal is not emitted when this property changes. 431 | 432 | Whether the media player may be controlled over this interface. 433 | 434 | This property is not expected to change, as it describes an intrinsic capability of the implementation. 435 | 436 | If this is false, clients should assume that all properties on this interface are read-only (and will raise errors if writing to them is attempted); all methods are not implemented and all other properties starting with 'Can' are also false. 437 | ''' 438 | 439 | 440 | if __name__ == '__main__': 441 | from .utils import get_players_uri, implements 442 | for uri in get_players_uri(): 443 | if implements(uri, Interfaces.PLAYER): 444 | mp2 = Player(dbus_interface_info={'dbus_uri': uri}) 445 | print( mp2.LoopStatus ) 446 | print( mp2.Shuffle ) 447 | mp2.Shuffle = not mp2.Shuffle 448 | print( mp2.Shuffle ) 449 | break 450 | else: 451 | print('no player with player interface found') -------------------------------------------------------------------------------- /mpris2/playlists.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/latest/Playlists_Interface.html 5 | ''' 6 | 7 | from .decorator import DbusAttr 8 | from .decorator import DbusInterface 9 | from .decorator import DbusMethod 10 | from .decorator import DbusSignal 11 | from .interfaces import Interfaces 12 | from .types import Playlist, Maybe_Playlist 13 | from dbus import UInt32 14 | 15 | 16 | class Playlists(Interfaces): 17 | ''' 18 | Provides access to the media player's playlists. 19 | 20 | Since D-Bus does not provide an easy way to check for what interfaces are exported on an object, clients should attempt to get one of the properties on this interface to see if it is implemented. 21 | 22 | ''' 23 | 24 | @DbusInterface(Interfaces.PLAYLISTS, Interfaces.OBJECT_PATH) 25 | def __init__(self): 26 | '''Constructor''' 27 | pass 28 | 29 | @DbusMethod 30 | def ActivatePlaylist(self, PlaylistId): 31 | ''' 32 | **Parameters:** 33 | 34 | * PlaylistId - o 35 | The id of the playlist to activate. 36 | 37 | Starts playing the given playlist. 38 | 39 | Note that this must be implemented. If the media player does not allow clients to change the playlist, it should not implement this interface at all. 40 | 41 | It is up to the media player whether this completely replaces the current tracklist, or whether it is merely inserted into the tracklist and the first track starts. For example, if the media player is operating in a 'jukebox' mode, it may just append the playlist to the list of upcoming tracks, and skip to the first track in the playlist. 42 | ''' 43 | pass 44 | 45 | @DbusMethod(produces=lambda playlist_list: \ 46 | [Playlist(playlist) for playlist in playlist_list], 47 | args_to_dbus=[UInt32, UInt32, str, bool]) 48 | def GetPlaylists(self, Index, MaxCount, Order, ReverseOrder=False): 49 | ''' 50 | **Parameters:** 51 | 52 | * Index - u 53 | The index of the first playlist to be fetched (according to the ordering). 54 | * MaxCount - u 55 | The maximum number of playlists to fetch. 56 | * Order - s (Playlist_Ordering) 57 | The ordering that should be used. 58 | * ReverseOrder - b 59 | Whether the order should be reversed. 60 | 61 | **Returns** 62 | 63 | * Playlists - a(oss) (Playlist_List) 64 | A list of (at most MaxCount) playlists. 65 | 66 | Gets a set of playlists. 67 | ''' 68 | pass 69 | 70 | @DbusSignal 71 | def PlaylistChanged(self, Playlist): 72 | ''' 73 | **Parameters** 74 | 75 | * Playlist - (oss) (Playlist) 76 | The playlist whose details have changed. 77 | 78 | Indicates that the name or icon for a playlist has changed. 79 | 80 | Note that, for this signal to operate correctly, the id of the playlist must not change when the name changes. 81 | ''' 82 | pass 83 | 84 | @DbusAttr 85 | def PlaylistCount(self): 86 | ''' 87 | **Returns** 88 | 89 | Read only 90 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 91 | 92 | The number of playlists available. 93 | ''' 94 | pass 95 | 96 | @DbusAttr 97 | def Orderings(self): 98 | ''' 99 | **Returns** 100 | 101 | Read only 102 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 103 | 104 | The avaislable orderings. At least one must be offered. 105 | ''' 106 | pass 107 | 108 | @DbusAttr(produces=Maybe_Playlist) 109 | def ActivePlaylist(self): 110 | ''' 111 | **Returns** 112 | 113 | Read only 114 | When this property changes, the org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted with the new value. 115 | 116 | The currently-active playlist. 117 | 118 | If there is no currently-active playlist, the structure's Valid field will be false, and the Playlist details are undefined. 119 | 120 | Note that this may not have a value even after ActivatePlaylist is called with a valid playlist id as ActivatePlaylist implementations have the option of simply inserting the contents of the playlist into the current tracklist. 121 | ''' 122 | pass 123 | 124 | 125 | if __name__ == '__main__': 126 | from .utils import get_players_uri, implements 127 | for uri in get_players_uri(): 128 | if implements(uri, Interfaces.PLAYLISTS): 129 | mp2 = Playlists(dbus_interface_info={'dbus_uri': uri}) 130 | print( mp2.ActivePlaylist ) 131 | print( 'Active is valid playlist: ', bool(mp2.ActivePlaylist) ) 132 | if mp2.ActivePlaylist: 133 | print( 'Active playlist name:', mp2.ActivePlaylist.Playlist.Name ) 134 | from mpris2.types import Playlist_Ordering 135 | print( hasattr('anystring', 'eusequenaotem') ) 136 | print( 'bla', mp2.GetPlaylists(0, 20, 137 | Playlist_Ordering.ALPHABETICAL, False) ) 138 | break 139 | else: 140 | print('no player with playlist interface found') 141 | -------------------------------------------------------------------------------- /mpris2/some_players.py: -------------------------------------------------------------------------------- 1 | class Some_Players(object): 2 | ''' 3 | Not defined in documentation 4 | 5 | Maybe this player (and other) implement mpris2 6 | 7 | **Some players** 8 | 9 | * AUDACIOUS 10 | 'audacious' 11 | * BANSHEE 12 | 'banshee' 13 | * BEATBOX 14 | 'beatbox' 15 | * BMP 16 | 'bmp' 17 | * CLEMENTINE 18 | 'clementine' 19 | * DRAGONPLAYER 20 | 'dragonplayer' 21 | * EXAILE 22 | 'exaile' 23 | * GMUSICBROWSER 24 | 'gmusicbrowser' 25 | * GMPC 26 | 'gmpc' 27 | * GUAYADEQUE 28 | 'guayadeque' 29 | * MOPIDY 30 | 'mopidy' 31 | * MPDRIS 32 | 'mpDris' 33 | * QUODLIBET 34 | 'quodlibet' 35 | * RAVEND 36 | 'ravend' 37 | * RHYTHMBOX 38 | 'rhythmbox' 39 | * SPOTIFY 40 | 'spotify' 41 | * VLC 42 | 'vlc' 43 | * XBMC 44 | 'xbmc' 45 | * XMMS2 46 | 'xmms2' 47 | * XNOISE 48 | 'xnoise' 49 | ''' 50 | AUDACIOUS = 'audacious' 51 | BANSHEE = 'banshee' 52 | BEATBOX = 'beatbox' 53 | BMP = 'bmp' 54 | CLEMENTINE = 'clementine' 55 | DRAGONPLAYER = 'dragonplayer' 56 | EXAILE = 'exaile' 57 | GMUSICBROWSER = 'gmusicbrowser' 58 | GMPC = 'gmpc' 59 | GUAYADEQUE = 'guayadeque' 60 | MOPIDY = 'mopidy' 61 | MPDRIS = 'mpDris' 62 | QUODLIBET = 'quodlibet' 63 | RAVEND = 'ravend' 64 | RHYTHMBOX = 'rhythmbox' 65 | SPOTIFY = 'spotify' 66 | VLC = 'vlc' 67 | XBMC = 'xbmc' 68 | XMMS2 = 'xmms2' 69 | XNOISE = 'xnoise' 70 | 71 | @staticmethod 72 | def get_dict(): 73 | result = {} 74 | for key in dir(Some_Players): 75 | if key[0] not in ('_', 'g'): 76 | result[key] = getattr(Some_Players, key) 77 | return result 78 | 79 | 80 | if __name__ == '__main__': 81 | print('Well know players:', Some_Players.get_dict().values()) 82 | -------------------------------------------------------------------------------- /mpris2/tracklist.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/2.2/Track_List_Interface.html 5 | ''' 6 | 7 | from .decorator import DbusAttr 8 | from .decorator import DbusMethod 9 | from .decorator import DbusSignal 10 | from .decorator import DbusInterface 11 | from .interfaces import Interfaces 12 | from .types import Metadata_Map 13 | 14 | 15 | class TrackList(Interfaces): 16 | ''' 17 | Interface for TrackList (org.mpris.MediaPlayer2.TrackList) 18 | 19 | Provides access to a short list of tracks which were recently played or 20 | will be played shortly. This is intended to provide context to the 21 | currently-playing track, rather than giving complete access to the media 22 | player's playlist. 23 | 24 | Example use cases are the list of tracks from the same album as the 25 | currently playing song or the Rhythmbox play queue. 26 | 27 | Each track in the tracklist has a unique identifier. The intention is that 28 | this uniquely identifies the track within the scope of the tracklist. In 29 | particular, if a media item (a particular music file, say) occurs twice in 30 | the track list, each occurrence should have a different identifier. If a 31 | track is removed from the middle of the playlist, it should not affect the 32 | track ids of any other tracks in the tracklist. 33 | 34 | As a result, the traditional track identifiers of URLs and position in the 35 | playlist cannot be used. Any scheme which satisfies the uniqueness 36 | requirements is valid, as clients should not make any assumptions about the 37 | value of the track id beyond the fact that it is a unique identifier. 38 | 39 | Note that the (memory and processing) burden of implementing the TrackList 40 | interface and maintaining unique track ids for the playlist can be 41 | mitigated by only exposing a subset of the playlist when it is very long 42 | (the 20 or so tracks around the currently playing track, for example). This 43 | is a recommended practice as the tracklist interface is not designed to 44 | enable browsing through a large list of tracks, but rather to provide 45 | clients with context about the currently playing track. 46 | ''' 47 | PROPERTIES_TACKS = 'Tracks' 48 | PROPERTIES_CAN_EDIT_TRACKS = 'CanEditTracks' 49 | SIGNALS_TRACK_LIST_REPLACED = 'TrackListReplaced' 50 | SIGNALS_TRACK_ADDED = 'TrackAdded' 51 | SIGNALS_TRACK_REMOVED = 'TrackRemoved' 52 | SIGNALS_TRACK_METADATA_CHANGED = 'TrackMetadataChanged' 53 | SIGNALS_PROPERTIES_CHANGED = 'PropertiesChanged' 54 | 55 | @DbusInterface(Interfaces.TRACK_LIST, Interfaces.OBJECT_PATH) 56 | def __init__(self): 57 | '''Constructor''' 58 | 59 | @DbusMethod(produces=lambda map_list:\ 60 | [Metadata_Map(metadata_map) for metadata_map in map_list]) 61 | def GetTracksMetadata(self, TrackIds): 62 | ''' 63 | **Parameters:** 64 | 65 | * TrackIds - ao (Track_Id_List) 66 | The list of track ids for which metadata is requested. 67 | 68 | **Returns** 69 | 70 | * Metadata - aa{sv} (Metadata_Map_List) 71 | Metadata of the set of tracks given as input. 72 | 73 | See the type documentation for more details. 74 | 75 | Gets all the metadata available for a set of tracks. 76 | 77 | Each set of metadata must have a 'mpris:trackid' entry at the very 78 | least, which contains a string that uniquely identifies this track 79 | within the scope of the tracklist. 80 | ''' 81 | pass 82 | 83 | @DbusMethod() 84 | def AddTrack(self, Uri, AfterTrack='', SetAsCurrent=False): 85 | ''' 86 | **Parameters:** 87 | 88 | * Uri - s (Uri) 89 | The uri of the item to add. Its uri scheme should be an element of 90 | the org.mpris.MediaPlayer2.SupportedUriSchemes property and the 91 | mime-type should match one of the elements of the 92 | org.mpris.MediaPlayer2.SupportedMimeTypes 93 | * AfterTrack - o (Track_Id) 94 | The identifier of the track after which the new item should be 95 | inserted. The path /org/mpris/MediaPlayer2/TrackList/NoTrack 96 | indicates that the track should be inserted at the start of the 97 | track list. 98 | * SetAsCurrent - b 99 | Whether the newly inserted track should be considered as the 100 | current track. Setting this to trye has the same effect as calling 101 | GoTo afterwards. 102 | 103 | Adds a URI in the TrackList. 104 | 105 | If the CanEditTracks property is false, this has no effect. 106 | 107 | .. note:: 108 | Clients should not assume that the track has been added at the time 109 | when this method returns. They should wait for a TrackAdded (or 110 | TrackListReplaced) signal. 111 | ''' 112 | pass 113 | 114 | @DbusMethod 115 | def RemoveTrack(self, TrackId): 116 | ''' 117 | 118 | **Parameters:** 119 | 120 | * TrackId - o (TrackId) 121 | Identifier of the track to be removed. 122 | /org/mpris/MediaPlayer2/TrackList/NoTrack is not a valid value for this argument. 123 | 124 | Removes an item from the TrackList. 125 | 126 | If the track is not part of this tracklist, this has no effect. 127 | 128 | If the CanEditTracks property is false, this has no effect. 129 | 130 | .. note:: 131 | Clients should not assume that the track has been removed at the 132 | time when this method returns. They should wait for a TrackRemoved 133 | (or TrackListReplaced) signal. 134 | ''' 135 | pass 136 | 137 | @DbusMethod 138 | def GoTo(self, TrackId): 139 | ''' 140 | **Parameters:** 141 | 142 | * TrackId - o (Track_Id) 143 | Identifier of the track to skip to. 144 | 145 | /org/mpris/MediaPlayer2/TrackList/NoTrack is not a valid value for this argument. 146 | 147 | Skip to the specified TrackId. 148 | 149 | If the track is not part of this tracklist, this has no effect. 150 | 151 | If this object is not /org/mpris/MediaPlayer2, the current TrackList's 152 | tracks should be replaced with the contents of this TrackList, and the 153 | TrackListReplaced signal should be fired from /org/mpris/MediaPlayer2. 154 | ''' 155 | 156 | @DbusSignal 157 | def TrackListReplaced(self, Tracks, CurrentTrack): 158 | ''' 159 | **Parameters:** 160 | 161 | * Tracks - ao (Track_Id_List) 162 | The new content of the tracklist. 163 | * CurrentTrack - o (Track_Id) 164 | The identifier of the track to be considered as current. 165 | 166 | /org/mpris/MediaPlayer2/TrackList/NoTrack indicates that there is no current track. 167 | 168 | This should correspond to the mpris:trackid field of the Metadata property of 169 | the org.mpris.MediaPlayer2.Player interface. 170 | 171 | Indicates that the entire tracklist has been replaced. 172 | 173 | It is left up to the implementation to decide when a change to the 174 | track list is invasive enough that this signal should be emitted 175 | instead of a series of TrackAdded and TrackRemoved signals. 176 | ''' 177 | pass 178 | 179 | @DbusSignal 180 | def TrackAdded(self, Metadata, AfterTrack=''): 181 | ''' 182 | **Parameters:** 183 | 184 | * Metadata - a{sv} (Metadata_Map) 185 | The metadata of the newly added item. 186 | 187 | This must include a mpris:trackid entry. 188 | 189 | See the type documentation for more details. 190 | * AfterTrack - o (Track_Id) 191 | The identifier of the track after which the new track was inserted. 192 | The path /org/mpris/MediaPlayer2/TrackList/NoTrack indicates that 193 | the track was inserted at the start of the track list. 194 | 195 | 196 | Indicates that a track has been added to the track list. 197 | ''' 198 | pass 199 | 200 | @DbusSignal 201 | def TrackRemoved(self, TrackId): 202 | ''' 203 | **Parameters:** 204 | 205 | * TrackId - o (Track_Id) 206 | The identifier of the track being removed. 207 | 208 | /org/mpris/MediaPlayer2/TrackList/NoTrack is not a valid value for this argument. 209 | 210 | Indicates that a track has been removed from the track list. 211 | ''' 212 | pass 213 | 214 | @DbusSignal 215 | def TrackMetadataChanged(self, TrackId, Metadata): 216 | ''' 217 | **Parameters:** 218 | 219 | * TrackId - o (Track_Id) 220 | The id of the track which metadata has changed. 221 | 222 | If the track id has changed, this will be the old value. 223 | 224 | /org/mpris/MediaPlayer2/TrackList/NoTrack is not a valid value for this argument. 225 | 226 | * Metadata - a{sv} (Metadata_Map) 227 | The new track metadata. 228 | 229 | This must include a mpris:trackid entry. 230 | 231 | See the type documentation for more details. 232 | 233 | Indicates that the metadata of a track in the tracklist has changed. 234 | 235 | This may indicate that a track has been replaced, in which case the 236 | mpris:trackid metadata entry is different from the TrackId argument. 237 | ''' 238 | pass 239 | 240 | @DbusAttr 241 | def Tracks(self): 242 | ''' 243 | **Returns:** 244 | 245 | Read only 246 | When this property changes, the 247 | org.freedesktop.DBus.Properties.PropertiesChanged signal is 248 | emitted, but the new value is not sent. 249 | 250 | An array which contains the identifier of each track in the tracklist, 251 | in order. 252 | 253 | The org.freedesktop.DBus.Properties.PropertiesChanged signal is emited 254 | every time this property changes, but the signal message does not 255 | contain the new value. Client implementations should rather rely on the 256 | TrackAdded, TrackRemoved and TrackListReplaced signals to keep their 257 | representation of the tracklist up to date. 258 | ''' 259 | pass 260 | 261 | @DbusAttr 262 | def CanEditTracks(self): 263 | ''' 264 | **Returns:** 265 | 266 | Read only 267 | When this property changes, the 268 | org.freedesktop.DBus.Properties.PropertiesChanged signal is emitted 269 | with the new value. 270 | 271 | If false, calling AddTrack or RemoveTrack will have no effect, and may 272 | raise a NotSupported error. 273 | ''' 274 | pass 275 | 276 | 277 | if __name__ == '__main__': 278 | from .utils import get_players_uri, implements 279 | for uri in get_players_uri(): 280 | if implements(uri, Interfaces.TRACK_LIST): 281 | # this is optional, maybe running player don't implement it 282 | # VLC does if you want try 283 | mp2 = TrackList(dbus_interface_info={'dbus_uri': uri}) 284 | #print(dir(mp2)) 285 | print('Tracks:', mp2.Tracks) 286 | print('CanEditTracks:', bool(mp2.CanEditTracks)) 287 | break 288 | else: 289 | print('no player with playlist interface found') 290 | -------------------------------------------------------------------------------- /mpris2/types/__init__.py: -------------------------------------------------------------------------------- 1 | from .loop_status import Loop_Status 2 | from .metadata_map import Metadata_Map 3 | from .playback_rate import Playback_Rate 4 | from .playback_status import Playback_Status 5 | from .playlist import Playlist 6 | from .playlist import Maybe_Playlist 7 | from .playlist_id import Playlist_Id 8 | from .playlist_ordering import Playlist_Ordering 9 | from .time_in_us import Time_In_Us 10 | from .uri import Uri 11 | from .volume import Volume 12 | -------------------------------------------------------------------------------- /mpris2/types/__main__.py: -------------------------------------------------------------------------------- 1 | from . import Loop_Status 2 | from . import Metadata_Map 3 | from . import Playback_Rate 4 | from . import Playback_Status 5 | from . import Playlist 6 | from . import Maybe_Playlist 7 | from . import Playlist_Id 8 | from . import Playlist_Ordering 9 | from . import Time_In_Us 10 | from . import Uri 11 | from . import Volume 12 | 13 | 14 | if __name__ == '__main__': 15 | print(Loop_Status) 16 | print(Metadata_Map) 17 | print(Playback_Rate) 18 | print(Playback_Status) 19 | print(Playlist) 20 | print(Playlist_Id) 21 | print(Playlist_Ordering) 22 | print(Maybe_Playlist) 23 | print(Time_In_Us) 24 | print(Uri) 25 | print(Volume) -------------------------------------------------------------------------------- /mpris2/types/loop_status.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html#Enum:Loop_Status 5 | ''' 6 | 7 | 8 | class Loop_Status(str): 9 | ''' 10 | A repeat / loop status 11 | 12 | * None (None) 13 | The playback will stop when there are no more tracks to play 14 | * Track (Track) 15 | The current track will start again from the begining once it has finished playing 16 | * Playlist (Playlist) 17 | The playback loops through a list of tracks 18 | ''' 19 | NONE = 'None' 20 | TRACK = 'Track' 21 | PLAYLIST = 'Playlist' 22 | VALUES = (NONE, TRACK, PLAYLIST) 23 | def __init__(self, status): 24 | self._status = status 25 | 26 | def __int__(self): 27 | return Loop_Status.VALUES.index(self._status) 28 | 29 | def __str__(self): 30 | return self._status 31 | 32 | 33 | if __name__ == '__main__': 34 | assert Loop_Status.PLAYLIST != 'None' 35 | assert Loop_Status.PLAYLIST == 'Playlist' 36 | assert Loop_Status.TRACK == 'Track' 37 | assert Loop_Status.NONE == 'None' 38 | -------------------------------------------------------------------------------- /mpris2/types/metadata_map.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/latest/Track_List_Interface.html#Mapping:Metadata_Map 5 | ''' 6 | 7 | 8 | class Metadata_Map(dict): 9 | ''' 10 | A mapping from metadata attribute names to values. 11 | 12 | The mpris:trackid attribute must always be present. This contains a string that uniquely identifies the track within the scope of the playlist. 13 | 14 | If the length of the track is known, it should be provided in the metadata property with the 'mpris:length' key. The length must be given in microseconds, and be represented as a signed 64-bit integer. 15 | 16 | If there is an image associated with the track, a URL for it may be provided using the 'mpris:artUrl' key. For other metadata, fields defined by the Xesam ontology should be used, prefixed by 'xesam:'. See http://wiki.xmms2.xmms.se/wiki/MPRIS_Metadata for a list of common fields. 17 | 18 | Lists of strings should be passed using the array-of-string ('as') D-Bus type. Dates should be passed as strings using the ISO 8601 extended format (eg: 2007-04-29T14:35:51). If the timezone is known, RFC 3339's internet profile should be used (eg: 2007-04-29T14:35:51+02:00). 19 | 20 | * Attribute - s 21 | The name of the attribute; see http://wiki.xmms2.xmms.se/wiki/MPRIS_Metadata for guidelines on names to use. 22 | * Value - v 23 | The value of the attribute, in the most appropriate format. 24 | ''' 25 | ART_URI = 'mpris:artUrl' 26 | TRACKID = 'mpris:trackid' 27 | LENGTH = 'mpris:length' 28 | ALBUM = 'xesam:album' 29 | ALBUM_ARTIST = 'xesam:albumArtist' 30 | ARTIST = 'xesam:artist' 31 | AS_TEXT = 'xesam:asText' 32 | AUDIO_BPM = 'xesam:audioBPM' 33 | AUTO_RATING = 'xesam:autoRating' 34 | COMMENT = 'xesam:comment' 35 | COMPOSER = 'xesam:composer' 36 | CONTENT_CREATED = 'xesam:contentCreated' 37 | DISC_NUMBER = 'xesam:discNumber' 38 | FIRST_USED = 'xesam:firstUsed' 39 | GENRE = 'xesam:genre' 40 | LAST_USED = 'xesam:lastUsed' 41 | LYRICIST = 'xesam:lyricist' 42 | TITLE = 'xesam:title' 43 | TRACK_NUMBER = 'xesam:trackNumber' 44 | URL = 'xesam:url' 45 | USE_COUNT = 'xesam:useCount' 46 | USER_RATING = 'xesam:userRating' 47 | 48 | def __init__(self, metadata): 49 | super(Metadata_Map, self).__init__(**metadata) 50 | 51 | 52 | if __name__ == '__main__': 53 | mdm = Metadata_Map({Metadata_Map.ALBUM : 'Marcelo Nova Ao Vivo'}) 54 | assert mdm[Metadata_Map.ALBUM] == 'Marcelo Nova Ao Vivo' 55 | -------------------------------------------------------------------------------- /mpris2/types/playback_rate.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html#Simple-Type:Playback_Rate 5 | ''' 6 | 7 | 8 | class Playback_Rate(float): 9 | ''' 10 | A playback rate 11 | 12 | This is a multiplier, 13 | so a value of 0.5 indicates that playback is happening at half speed, 14 | while 1.5 means that 1.5 seconds of 'track time' is consumed every second. 15 | ''' 16 | 17 | def __init__(self, rate=1.0): 18 | self._rate = rate 19 | 20 | def __float__(self): 21 | return self._rate 22 | 23 | def __str__(self): 24 | return str(self._rate) 25 | 26 | 27 | if __name__ == '__main__': 28 | pr = Playback_Rate(1.2) 29 | assert pr == 1.2 30 | -------------------------------------------------------------------------------- /mpris2/types/playback_status.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html#Enum:Playback_Status 5 | ''' 6 | 7 | 8 | class Playback_Status(str): 9 | ''' 10 | A playback state. 11 | 12 | * Playing (Playing) 13 | A track is currently playing. 14 | * Paused (Paused) 15 | A track is currently paused. 16 | * Stopped (Stopped) 17 | There is no track currently playing. 18 | 19 | ''' 20 | 21 | PLAYING = 'Playing' 22 | PAUSED = 'Paused' 23 | STOPPED = 'Stopped' 24 | VALUES = (PLAYING, PAUSED, STOPPED) 25 | 26 | def __init__(self, status): 27 | self._status = status 28 | 29 | def __int__(self): 30 | return Playback_Status.VALUES.index(self._status) 31 | 32 | def __str__(self): 33 | return self._status 34 | 35 | 36 | if __name__ == '__main__': 37 | assert Playback_Status.PLAYING == 'Playing' 38 | assert Playback_Status.PAUSED == 'Paused' 39 | assert Playback_Status.STOPPED == 'Stopped' 40 | assert Playback_Status.STOPPED != 'Playing' 41 | -------------------------------------------------------------------------------- /mpris2/types/playlist.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/2.2/Playlists_Interface.html#Struct:Playlist 5 | ''' 6 | 7 | from dbus import Struct 8 | 9 | 10 | class Playlist(Struct): 11 | ''' 12 | A data structure describing a playlist. 13 | 14 | * Id - o (Playlist_Id) 15 | A unique identifier for the playlist. 16 | 17 | This should remain the same if the playlist is renamed. 18 | * Name - s 19 | The name of the playlist, typically given by the user. 20 | * Icon - s (Uri) 21 | The URI of an (optional) icon. 22 | 23 | ''' 24 | def __init__(self, playlist): 25 | Struct.__init__( 26 | self, 27 | iter(playlist), 28 | signature=playlist.signature, 29 | variant_level=playlist.variant_level 30 | ) 31 | 32 | @property 33 | def Id(self): 34 | return self[0] 35 | 36 | @property 37 | def Name(self): 38 | return self[1] 39 | 40 | @property 41 | def Icon(self): 42 | return self[2] 43 | 44 | 45 | class Maybe_Playlist(Struct): 46 | ''' 47 | * Valid - b 48 | Whether this structure refers to a valid playlist. 49 | * Playlist - (oss) (Playlist) 50 | The playlist, providing Valid is true, otherwise undefined. 51 | 52 | When constructing this type, it should be noted that the playlist ID must 53 | be a valid object path, or D-Bus implementations may reject it. This is 54 | true even when Valid is false. It is suggested that '/' is used as the 55 | playlist ID in this case. 56 | ''' 57 | 58 | def __init__(self, maybe_playlist=None): 59 | Struct.__init__( 60 | self, 61 | (maybe_playlist[0], maybe_playlist[1]), 62 | signature=maybe_playlist.signature, 63 | variant_level=maybe_playlist.variant_level 64 | ) 65 | 66 | @property 67 | def Valid(self): 68 | return self[0] 69 | 70 | @property 71 | def Playlist(self): 72 | return Playlist(self[1]) 73 | 74 | def __nonzero__(self): 75 | return bool(self.Valid) 76 | 77 | def __bool__(self): 78 | return self.__nonzero__() 79 | 80 | 81 | if __name__ == '__main__': 82 | expect = Struct((1, 'My Playlist', None), signature='iss') 83 | p = Playlist(expect) 84 | assert p.Id == 1 85 | assert p.Name == 'My Playlist' 86 | assert p.Icon == None 87 | m_expect = Struct((False, p)) 88 | mp = Maybe_Playlist(m_expect) 89 | assert not mp.Valid 90 | assert not mp 91 | -------------------------------------------------------------------------------- /mpris2/types/playlist_id.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/2.2/Playlists_Interface.html#Simple-Type:Playlist_Id 5 | ''' 6 | 7 | 8 | class Playlist_Id(str): 9 | ''' 10 | Unique playlist identifier. 11 | ''' 12 | 13 | def __init__(self, playlist_id): 14 | self._playlist_id = playlist_id 15 | 16 | def __str__(self): 17 | return self._status 18 | 19 | 20 | if __name__ == '__main__': 21 | pid = Playlist_Id('id of a playlist') 22 | assert pid == 'id of a playlist' 23 | -------------------------------------------------------------------------------- /mpris2/types/playlist_ordering.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/2.2/Playlists_Interface.html#Enum:Playlist_Ordering 5 | ''' 6 | 7 | 8 | class Playlist_Ordering(str): 9 | ''' 10 | Specifies the ordering of returned playlists. 11 | 12 | * Alphabetical (Alphabetical) 13 | Alphabetical ordering by name, ascending. 14 | * CreationDate (Created) 15 | Ordering by creation date, oldest first. 16 | * ModifiedDate (Modified) 17 | Ordering by last modified date, oldest first. 18 | * LastPlayDate (Played) 19 | Ordering by date of last playback, oldest first. 20 | * UserDefined (User) 21 | A user-defined ordering. 22 | ''' 23 | 24 | ALPHABETICAL = 'Alphabetical' 25 | CREATION_DATE = 'CreationDate' 26 | MODIFIED_DATE = 'ModifiedDate' 27 | LAST_PLAY_DATE = 'LastPlayDate' 28 | USER_DEFINE = 'UserDefined' 29 | VALUES = (ALPHABETICAL,CREATION_DATE,MODIFIED_DATE,LAST_PLAY_DATE,USER_DEFINE) 30 | 31 | def __init__(self, ordering): 32 | self._ordering = ordering 33 | 34 | def __str__(self): 35 | return self._ordering 36 | 37 | 38 | if __name__ == '__main__': 39 | po = Playlist_Ordering('Alphabetical') 40 | assert po == 'Alphabetical' 41 | assert po == Playlist_Ordering.ALPHABETICAL 42 | assert po != Playlist_Ordering.CREATION_DATE 43 | -------------------------------------------------------------------------------- /mpris2/types/time_in_us.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html#Simple-Type:Time_In_Us 5 | ''' 6 | 7 | 8 | class Time_In_Us(int): 9 | '''Time in microseconds.''' 10 | 11 | def __init__(self, time=0): 12 | self._time = time 13 | 14 | def __int__(self): 15 | return int(self._time) 16 | 17 | def __str__(self): 18 | return str(self._time) 19 | 20 | 21 | if __name__ == '__main__': 22 | assert Time_In_Us(10) == 10 23 | -------------------------------------------------------------------------------- /mpris2/types/uri.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/2.2/Playlists_Interface.html#Simple-Type:Uri 5 | ''' 6 | 7 | 8 | class Uri(str): 9 | '''A unique resource identifier.''' 10 | 11 | def __init__(self, uri): 12 | self._uri = uri 13 | 14 | def __str__(self): 15 | return str(self._uri) 16 | 17 | if __name__ == '__main__': 18 | assert Uri('http://www.com.br') == 'http://www.com.br' 19 | -------------------------------------------------------------------------------- /mpris2/types/volume.py: -------------------------------------------------------------------------------- 1 | ''' 2 | From mprisV2.2 documentation 3 | 4 | http://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html#Simple-Type:Volume 5 | ''' 6 | 7 | 8 | class Volume(float): 9 | ''' 10 | Audio volume level 11 | 12 | * 0.0 means mute. 13 | * 1.0 is a sensible maximum volume level (ex: 0dB). 14 | 15 | Note that the volume may be higher than 1.0, although generally clients should not attempt to set it above 1.0. 16 | ''' 17 | 18 | MIN = 0.0 19 | MAX = 1.0 20 | RANGE = set([n/10.0 for n in range(11)]) 21 | 22 | def __init__(self, volume=1.0): 23 | assert volume <= 1 24 | self._volume = float(volume) 25 | 26 | def __float__(self): 27 | return self._volume 28 | 29 | def __str__(self): 30 | return str(self._volume) 31 | 32 | 33 | if __name__ == '__main__': 34 | assert Volume(1) == 1 35 | assert Volume(0.1) == 0.1 36 | assert Volume(1) == 1.0 37 | assert Volume(1.0) == 1 38 | assert Volume(0.1) != 1.2 39 | 40 | -------------------------------------------------------------------------------- /mpris2/utils.py: -------------------------------------------------------------------------------- 1 | ''' 2 | utils functions not defined in espec 3 | ''' 4 | 5 | import re 6 | 7 | from .interfaces import Interfaces 8 | from .decorator.utils import get_session, get_uri, get_mainloop # @UnusedImport 9 | from .decorator.utils import list_interfaces as _list_interfaces 10 | from .decorator.utils import implements as _implements 11 | 12 | def get_players_uri(pattern=''): 13 | ''' 14 | Return string of player bus name 15 | @param pattern=None: string RegEx that filter response 16 | @return: array string of players bus name 17 | ''' 18 | return get_uri(Interfaces.MEDIA_PLAYER + '.*' + pattern) 19 | 20 | 21 | def get_player_id_from_uri(uri): 22 | ''' 23 | Returns player mpris2 id from uri 24 | @param uri: string mpris2 player dbus uri 25 | @return: string mrpis2 id 26 | ''' 27 | mateched = re.match(Interfaces.MEDIA_PLAYER + '\.(.+)', uri or '') 28 | return mateched.groups()[0]\ 29 | if mateched\ 30 | else '' 31 | 32 | def get_players_id(pattern=None): 33 | ''' 34 | Return string of player mpris2 id 35 | @param pattern=None: string RegEx that filter response 36 | @return: array string of players bus name 37 | ''' 38 | for item in get_uri(Interfaces.MEDIA_PLAYER + '.*' + pattern): 39 | yield get_player_id_from_uri(item) 40 | 41 | def list_interfaces(uri, path=None, bus=None): 42 | return _list_interfaces(uri, path or Interfaces.OBJECT_PATH, bus) 43 | 44 | def implements(uri, interface, path=Interfaces.OBJECT_PATH, bus=None): 45 | return _implements(uri, interface, path or Interfaces.OBJECT_PATH, bus) 46 | 47 | 48 | if __name__ == '__main__': 49 | uris = get_players_uri() 50 | if not uris: 51 | print('No running players') 52 | for uri in uris: 53 | print(uri, ':') 54 | for interface in list_interfaces(uri): 55 | print('\t', interface) 56 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.rst -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | Created on Nov 6, 2011 4 | 5 | @author: hugosenari 6 | ''' 7 | 8 | from distutils.core import setup 9 | 10 | with open('README.rst') as readme_file: 11 | readme = readme_file.read() 12 | 13 | setup(name='mpris2', 14 | version='1.0.1', 15 | description='Python mpris2 definition', 16 | author='hugosenari', 17 | author_email='hugosenari@gmail.com', 18 | url='https://github.com/hugosenari/mpris2', 19 | keywords = ["dbus", "mpris2"], 20 | packages=('mpris2', 'mpris2.decorator', 'mpris2.types'), 21 | requires=(), 22 | license = "GPL", 23 | classifiers=[ 24 | "Development Status :: 3 - Alpha", 25 | "Intended Audience :: Developers", 26 | "Topic :: Multimedia :: Sound/Audio", 27 | "Topic :: Software Development :: Libraries :: Python Modules", 28 | "License :: OSI Approved :: GNU General Public License (GPL)", 29 | "Programming Language :: Python :: 2.7", 30 | "Programming Language :: Python :: 3.4" 31 | ], 32 | long_description = readme 33 | ) -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugosenari/mpris2/67356f29d68e29c7f3f0ddda5aaad6f68899b2bd/test/__init__.py -------------------------------------------------------------------------------- /test/decorator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugosenari/mpris2/67356f29d68e29c7f3f0ddda5aaad6f68899b2bd/test/decorator/__init__.py -------------------------------------------------------------------------------- /test/decorator/test_method.py: -------------------------------------------------------------------------------- 1 | from mpris2.decorator import DbusMethod 2 | 3 | def test_merge_args_get_all_from_std_args(): 4 | args = () 5 | std_args = (4, 5, 6) 6 | assert (4, 5, 6) == DbusMethod.merge_args(args, std_args) 7 | 8 | def test_merge_args_get_all_from_args(): 9 | args = (1, 2, 3) 10 | std_args = () 11 | assert (1, 2, 3) == DbusMethod.merge_args(args, std_args) 12 | 13 | std_args = (4, 5, 6) 14 | assert (1, 2, 3) == DbusMethod.merge_args(args, std_args) 15 | 16 | def test_merge_args_complete_with_std(): 17 | args = (1, 2) 18 | std_args = (4, 5, 6) 19 | assert (1, 2, 6) == DbusMethod.merge_args(args, std_args) 20 | 21 | def test_merge_args_both_empty(): 22 | args = () 23 | std_args = () 24 | assert () == DbusMethod.merge_args(args, std_args) 25 | 26 | def test_merge_kwds_get_all_from_std_kwd(): 27 | kwds = {} 28 | std_kwds = {"a": 4, "b": 5, "c": 6} 29 | assert std_kwds == DbusMethod.merge_kwds(kwds, std_kwds) 30 | 31 | def test_merge_kwds_get_all_from_kwds(): 32 | kwds = {"a": 1, "b": 2, "c": 3} 33 | std_kwds = {} 34 | assert {"a": 1, "b": 2, "c": 3} == DbusMethod.merge_kwds(kwds, std_kwds) 35 | 36 | std_kwds = {"a": 4, "b": 5, "c": 6} 37 | assert {"a": 1, "b": 2, "c": 3} == DbusMethod.merge_kwds(kwds, std_kwds) 38 | 39 | def test_merge_kwds_complete_with_std(): 40 | kwds = {"a": 1, "b": 2} 41 | std_kwds = {"a": 4, "b": 5, "c": 6} 42 | assert {"a": 1, "b": 2, "c": 6} == DbusMethod.merge_kwds(kwds, std_kwds) 43 | 44 | def test_merge_kwds_both_empty(): 45 | kwds = {} 46 | std_kwds = {} 47 | assert {} == DbusMethod.merge_kwds(kwds, std_kwds) --------------------------------------------------------------------------------