summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Ryan <mark.d.ryan@intel.com>2012-06-08 18:17:54 +0200
committerMark Ryan <mark.d.ryan@intel.com>2012-06-08 18:17:54 +0200
commit15573d375b983f8cc2d3021733e629e4ef4c4f16 (patch)
tree327cf771151b061fdc1e68f1804b12096722d705
downloaddleyna-control-15573d375b983f8cc2d3021733e629e4ef4c4f16.tar.gz
[General] Initial Commit
Signed-off-by: Mark Ryan <mark.d.ryan@intel.com>
-rw-r--r--.gitignore4
-rw-r--r--AUTHORS1
-rw-r--r--COPYING502
-rw-r--r--ChangeLog1
-rw-r--r--README38
-rwxr-xr-xsetup.py43
-rwxr-xr-xsrc/media-service-demo.py42
-rw-r--r--src/msd/__init__.py19
-rw-r--r--src/msd/msd_browse.py234
-rw-r--r--src/msd/msd_main_window.py320
-rw-r--r--src/msd/msd_player.py257
-rw-r--r--src/msd/msd_search.py195
-rw-r--r--src/msd/msd_sort_order.py38
-rw-r--r--src/msd/msd_upnp.py109
-rw-r--r--src/msd/msd_utils.py41
15 files changed, 1844 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d7423af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*~
+*.pyc
+build
+dist
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..3bd81c5
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Mark Ryan <mark.d.ryan@intel.com>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ 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.
+
+ 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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..21a8d9d
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1 @@
+Initial Version.
diff --git a/README b/README
new file mode 100644
index 0000000..4812e08
--- /dev/null
+++ b/README
@@ -0,0 +1,38 @@
+Introduction:
+-------------
+
+Media-service-demo is a Digital Media Player written in python to
+demonstrate how to use the media-service-upnp APIs. It detects
+Digital Media Servers on the local area network, and allows users to
+browse, search and play their contents.
+
+Compilation:
+------------
+
+Media-service-demo has a number of pre-requisites, notably
+media-service-upnp, to identify and browse Digital Media Servers, and
+GStreamer to play audio and video content.
+
+Media-service-upnp can be downloaded from
+git://github.com/01org/media-service-upnp.git. Please follow the
+instructions in the README document to compile and install this
+component.
+
+GStreamer can be installed using the package management systems of
+your Linux distribution. You may need to install the various plugins
+to ensure that your media plays correctly.
+
+The source can be downloaded from github as follows:
+
+git clone git://github.com/01org/media-service-upnp.git
+
+Media-service-demo can be run directly from the source repository
+simply by typing.
+
+src/media-service-upnp
+
+It can be installed on your system via the supplied setup.py script.
+
+sudo ./setup.py install
+
+
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..9ef1058
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+# media-service-demo
+#
+# Copyright (C) 2012 Intel Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU Lesser General Public License,
+# version 2.1, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope 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.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Mark Ryan <mark.d.ryan@intel.com>
+#
+
+from distutils.core import setup
+from distutils.command.install_data import install_data
+
+class MSDInstallData(install_data):
+
+ def finalize_options(self):
+ self.set_undefined_options('install', ('install_lib', 'install_dir'))
+ install_data.finalize_options(self)
+
+setup(name = "msd",
+ version = "0.0.1",
+ description = "Test DMP to demonstrate features of media-service-upnp",
+ author = "Mark Ryan",
+ author_email = "mark.d.ryan@intel.com",
+ url = "https://01.org/dleyna/about",
+ license = "http://www.gnu.org/licenses/lgpl-2.1.html",
+ scripts = ['src/media-service-demo.py'],
+ package_dir={'msd': 'src/msd'},
+ packages = [ "msd" ],
+ data_files = [ ("msd" , ["AUTHORS", "README", "ChangeLog", "COPYING"]) ],
+ cmdclass = { 'install_data' : MSDInstallData })
diff --git a/src/media-service-demo.py b/src/media-service-demo.py
new file mode 100755
index 0000000..3901c45
--- /dev/null
+++ b/src/media-service-demo.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+# media-service-demo
+#
+# Copyright (C) 2012 Intel Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU Lesser General Public License,
+# version 2.1, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope 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.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Mark Ryan <mark.d.ryan@intel.com>
+#
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import glib
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+from msd.msd_main_window import *
+
+if __name__ == "__main__":
+ gtk.gdk.threads_init()
+ try:
+ del os.environ["http_proxy"];
+ except Exception, e:
+ pass
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+ state = State()
+ main_window = MainWindow(state)
+ gtk.main()
diff --git a/src/msd/__init__.py b/src/msd/__init__.py
new file mode 100644
index 0000000..64ce8ef
--- /dev/null
+++ b/src/msd/__init__.py
@@ -0,0 +1,19 @@
+# media-service-demo
+#
+# Copyright (C) 2012 Intel Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU Lesser General Public License,
+# version 2.1, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope 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.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Mark Ryan <mark.d.ryan@intel.com>
+#
diff --git a/src/msd/msd_browse.py b/src/msd/msd_browse.py
new file mode 100644
index 0000000..d0d42dc
--- /dev/null
+++ b/src/msd/msd_browse.py
@@ -0,0 +1,234 @@
+# media-service-demo
+#
+# Copyright (C) 2012 Intel Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU Lesser General Public License,
+# version 2.1, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope 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.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Mark Ryan <mark.d.ryan@intel.com>
+#
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import dateutil.parser
+import datetime
+
+from msd_sort_order import *
+from msd_upnp import *
+
+class TreeNode(object):
+
+ filter = ["Artist", "DisplayName", "URLs", "Date", "Path",
+ "Type"]
+ buffer_size = 50
+
+ def __init__(self, props, parent, sort_order):
+ self.__props = props
+ self.__container = None
+ self.__max_items = 0
+ self.__parent = parent
+ self.__sort_order = sort_order
+ if self.is_container():
+ self.__container = Container(props["Path"])
+ try:
+ self.__max_items = self.__container.get_prop("ChildCount")
+ except Exception:
+ pass
+ self.__children = [None] * self.__max_items
+
+ def is_container(self):
+ return self.__props["Type"] == "container"
+
+ def reset_children(self):
+ self.__children = [None] * self.__max_items
+
+ def get_num_children(self):
+ return self.__max_items
+
+ def get_props(self):
+ return self.__props
+
+ def get_parent(self):
+ return self.__parent
+
+ def flush(self):
+ self.__flush_down()
+ if self.__parent:
+ self.__parent.__flush_up(self)
+
+ def __flush_down(self):
+ i = 0;
+ while i < self.__max_items:
+ if self.__children[i]:
+ self.__children[i].__flush_down()
+ self.__children[i] = None
+ i = i + 1
+
+ def __flush_up(self, child):
+ i = 0;
+ while i < self.__max_items:
+ if self.__children[i]:
+ if child != self.__children[i]:
+ self.__children[i].__flush_down()
+ self.__children[i] = None
+ i = i + 1
+ if self.__parent:
+ self.__parent.__flush_up(self)
+
+ def get_child(self, child):
+ retval = None
+ if child < self.__max_items:
+ retval = self.__children[child]
+ if not retval:
+ i = child + 1
+ while (i < self.__max_items and (i - child) <
+ TreeNode.buffer_size and not self.__children[i]):
+ i = i + 1
+ try:
+ sort_descriptor = self.__sort_order.get_upnp_sort_order()
+ try:
+ result = self.__container.list_children(child,
+ i - child,
+ TreeNode.filter,
+ sort_descriptor)
+ except Exception:
+ result = self.__container.list_children(child,
+ i - child,
+ TreeNode.filter)
+ i = child
+ for props in result:
+ self.__children[i] = TreeNode(props, self,
+ self.__sort_order)
+ i = i + 1
+ retval = self.__children[child]
+ except Exception:
+ pass
+
+ return retval
+
+class BrowseModel(gtk.GenericTreeModel):
+ columns = (("DisplayName", str), ("Artist", str), ("Date", str),
+ ("Type",str), ("Path", str), ("URLs", str))
+
+ def __init__(self, root):
+ gtk.GenericTreeModel.__init__(self)
+
+ self.__root = root
+
+ def flush(self):
+ self.__root.flush()
+
+ def on_get_flags(self):
+ return gtk.TREE_MODEL_LIST_ONLY | gtk.TREE_MODEL_ITERS_PERSIST
+
+ def on_get_n_columns(self):
+ return len(BrowseModel.columns)
+
+ def on_get_column_type(self, n):
+ return BrowseModel.columns[n][1]
+
+ def __get_num_of_children(self):
+ num = self.__root.get_num_children()
+ if self.__root.get_parent():
+ num = num + 1
+ return num
+
+ def on_get_iter(self, path):
+ if path[0] >= self.__get_num_of_children():
+ raise ValueError("Invalid Path")
+ return path[0]
+
+ def on_get_path(self, rowref):
+ return (rowref,)
+
+ def adjusted_on_get_value(self, rowref, col):
+ retval = None
+ key = BrowseModel.columns[col][0]
+ node = self.__root.get_child(rowref)
+ props = node.get_props()
+ if key in props:
+ data = props[key]
+ if node.is_container():
+ if col == 0:
+ retval = data
+ else:
+ if col == 2:
+ date = dateutil.parser.parse(data)
+ retval = date.strftime("%x")
+ elif col == 3:
+ data = data[0].upper() + data[1:]
+ period = data.find('.')
+ if period >=0:
+ retval = data[:period]
+ else:
+ retval = data
+ elif col == 5:
+ retval = data[0]
+ else:
+ retval = data
+ elif not node.is_container():
+ if col == 1:
+ retval = "Unknown"
+ elif col == 2:
+ retval = datetime.date.today().strftime("%x")
+ else:
+ retval = ""
+
+ return retval
+
+ def on_get_value(self, rowref, col):
+ if self.__root.get_parent():
+ if rowref == 0:
+ if col == 0:
+ retval = ".."
+ else:
+ retval = ""
+ else:
+ retval = self.adjusted_on_get_value(rowref - 1, col)
+ else:
+ retval = self.adjusted_on_get_value(rowref, col)
+
+ return retval
+
+ def on_iter_next(self, rowref):
+ retval = None
+ rowref = rowref + 1
+ if rowref < self.__get_num_of_children():
+ retval = rowref
+
+ return retval
+
+ def on_iter_children(self, rowref):
+ retval = 0
+ if rowref:
+ retval = None
+ return retval
+
+ def on_iter_has_child(self, rowref):
+ return False
+
+ def on_iter_n_children(self, rowref):
+ retval = 0
+ if not rowref:
+ retval = self.__get_num_of_children()
+ return retval
+
+ def on_iter_nth_child(self, rowref, child):
+ retval = None
+ if not rowref and child < self.__get_num_of_children():
+ retval = child
+ return retval
+
+ def on_iter_parent(self, rowref):
+ return None
diff --git a/src/msd/msd_main_window.py b/src/msd/msd_main_window.py
new file mode 100644
index 0000000..62c3b29
--- /dev/null
+++ b/src/msd/msd_main_window.py
@@ -0,0 +1,320 @@
+# media-service-demo
+#
+# Copyright (C) 2012 Intel Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU Lesser General Public License,
+# version 2.1, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope 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.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Mark Ryan <mark.d.ryan@intel.com>
+#
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import glib
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import os
+
+from msd_utils import *
+from msd_upnp import *
+from msd_search import *
+from msd_browse import *
+from msd_player import *
+
+class MainWindow(object):
+
+ container_padding = 2
+
+ def delete_event(self, widget, event, data=None):
+ return False
+
+ def destroy(self, widget, data=None):
+ if self.__overlay:
+ self.__overlay.cancel_playback()
+ gtk.main_quit()
+
+ def __append_server_list_row(self, list_store, key, value):
+ name, image = value
+ if image:
+ image = image.get_pixbuf()
+ image = image.scale_simple(32, 32, gtk.gdk.INTERP_BILINEAR)
+ return list_store.append([image, name, key])
+
+ def __create_server_list_store(self):
+ list_store = gtk.ListStore(gtk.gdk.Pixbuf, str, str)
+ for key, value in self.__state.get_server_list().iteritems():
+ self.__append_server_list_row(list_store, key, value)
+ return list_store
+
+ def __change_server(self, page, sel):
+ model, row = sel.get_selected()
+ if row != None:
+ path = model.get_value(row, 2)
+ if page == 0:
+ if self.__search_path != path:
+ search_model = SearchModel(Container(path),
+ self.__search_entry.get_text(),
+ self.__images.get_active(),
+ self.__videos.get_active(),
+ self.__music.get_active(),
+ self.__sort_order)
+ self.__search_view.set_model(search_model)
+ self.__search_path = path
+ elif self.__browse_path != path:
+ props = { "Path" : path, "Type": "container" }
+ self.__browse_tree = TreeNode(props, None, self.__sort_order)
+ browse_model = BrowseModel(self.__browse_tree)
+ self.__browse_node = self.__browse_tree
+ self.__browse_view.set_model(browse_model)
+ self.__browse_path = path
+
+ def __server_selected(self, sel):
+ page = self.__notebook.get_current_page()
+ self.__change_server(page, sel)
+
+ def __select_server(self, rowref):
+ selection = self.__server_view.get_selection()
+ if selection.count_selected_rows() == 0:
+ liststore = self.__server_view.get_model()
+ selection = self.__server_view.get_selection()
+ selection.select_iter(rowref)
+ self.__server_view.set_cursor(liststore.get_path(rowref))
+
+ def __create_server_list(self, table):
+ liststore = self.__create_server_list_store()
+ treeview = gtk.TreeView(liststore)
+ treeview.set_headers_visible(True)
+
+ column = gtk.TreeViewColumn()
+ column.set_title("Servers")
+ renderer = gtk.CellRendererPixbuf()
+ column.pack_start(renderer, expand=False)
+ column.add_attribute(renderer, 'pixbuf', 0)
+ renderer = gtk.CellRendererText()
+ column.pack_start(renderer, expand=False)
+ column.add_attribute(renderer, 'text', 1)
+ treeview.append_column(column)
+
+ treeview.set_headers_clickable(True)
+
+ treeview.get_selection().connect("changed", self.__server_selected)
+
+ scrollwin = gtk.ScrolledWindow()
+ scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC);
+ scrollwin.add(treeview)
+ table.attach(scrollwin, left_attach=0, right_attach=1,
+ top_attach=0, bottom_attach=1)
+ self.__server_view = treeview;
+
+ def __column_clicked(self, column, sort_by):
+ self.__sort_order.set_sort_by(sort_by)
+ tv = column.get_tree_view()
+ model = tv.get_model()
+ model.flush()
+ tv.set_model(None)
+ tv.set_model(model)
+
+ def __create_column(self, treeview, name, col, width, sort_by):
+ renderer = gtk.CellRendererText()
+ column = gtk.TreeViewColumn(name, renderer)
+ column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+ column.set_fixed_width(width)
+ column.add_attribute(renderer, 'text', col)
+ column.connect("clicked", self.__column_clicked, sort_by)
+ treeview.append_column(column)
+
+ def __close_overlay(self):
+ self.__window.remove(self.__overlay.get_container())
+ self.__window.add(self.__main_view)
+ self.__overlay = None
+
+ def __content_clicked(self, treeview, path, col):
+ model = treeview.get_model()
+ rowref = model.get_iter(path)
+ name = model.get_value(rowref, 0)
+ ctype = model.get_value(rowref, 3)
+ path = model.get_value(rowref, 4)
+ url = model.get_value(rowref, 5)
+
+ if url != "":
+ if ctype == "Image":
+ self.__window.remove(self.__main_view)
+ self.__overlay = PlayWindowImage(name, url,
+ self.__close_overlay)
+ self.__window.add(self.__overlay.get_container())
+ elif ctype == "Video":
+ self.__window.remove(self.__main_view)
+ self.__overlay = PlayWindowVideo(name, url,
+ self.__close_overlay)
+ self.__window.add(self.__overlay.get_container())
+ elif ctype == "Audio":
+ try:
+ album_art_url = MediaObject(path).get_prop("AlbumArtURL")
+ except Exception:
+ album_art_url = None
+ self.__window.remove(self.__main_view)
+ self.__overlay = PlayWindowAudio(name, url, album_art_url,
+ self.__close_overlay)
+ self.__window.add(self.__overlay.get_container())
+
+ def __browse_content_clicked(self, treeview, path, col):
+ if self.__browse_node != self.__browse_tree and path[0] == 0:
+ self.__browse_node.reset_children()
+ self.__browse_node = self.__browse_node.get_parent()
+ browse_model = BrowseModel(self.__browse_node)
+ self.__browse_view.set_model(browse_model)
+ else:
+ child = path[0]
+ if self.__browse_node != self.__browse_tree:
+ child = child - 1
+ node = self.__browse_node.get_child(child)
+ if node.is_container():
+ self.__browse_node = node
+ browse_model = BrowseModel(self.__browse_node)
+ self.__browse_view.set_model(browse_model)
+ else:
+ self.__content_clicked(treeview, path, col)
+
+ def __create_common_list(self, store):
+ treeview = gtk.TreeView(store)
+ treeview.set_headers_visible(True)
+ treeview.set_fixed_height_mode(True)
+
+ self.__create_column(treeview, "Title", 0, 300, "DisplayName")
+ self.__create_column(treeview, "Date", 2, 100, "Date")
+ self.__create_column(treeview, "Type", 3, 75, "Type")
+ self.__create_column(treeview, "Author", 1, 100, "Artist")
+
+ treeview.set_headers_clickable(True)
+ treeview.set_rules_hint(True)
+
+ scrollwin = gtk.ScrolledWindow()
+ scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC);
+ scrollwin.add(treeview)
+ return (scrollwin, treeview)
+
+ def __create_browse_view(self, notebook):
+ tree_store = gtk.TreeStore(str, str, str, str)
+ scrollwin, treeview = self.__create_common_list(tree_store)
+ treeview.connect("row-activated", self.__browse_content_clicked)
+ self.__browse_view = treeview;
+ notebook.append_page(scrollwin, gtk.Label("Browse"))
+
+ def __create_search_list(self, container):
+ list_store = gtk.ListStore(str, str, str, str)
+ scrollwin, treeview = self.__create_common_list(list_store)
+ container.pack_start(scrollwin, True, True, 0)
+ treeview.connect("row-activated", self.__content_clicked)
+ self.__search_view = treeview;
+
+ def __search_criteria_changed(self, obj):
+ self.__search_path = None
+ self.__server_selected(self.__server_view.get_selection())
+
+ def __create_check_box(self, title, container):
+ cb = gtk.CheckButton(title)
+ cb.set_active(True)
+ cb.connect("toggled", self.__search_criteria_changed)
+ container.pack_start(cb, True, True, MainWindow.container_padding)
+ return cb
+
+ def __create_search_controls(self, container):
+ label = gtk.Label("Search: ")
+ self.__search_entry = gtk.Entry()
+ self.__search_entry.set_text("")
+ self.__search_entry.connect("activate", self.__search_criteria_changed)
+ container.pack_start(label, True, True, MainWindow.container_padding)
+ container.pack_start(self.__search_entry, True, True,
+ MainWindow.container_padding)
+ self.__music = self.__create_check_box("Music", container)
+ self.__images = self.__create_check_box("Images", container)
+ self.__videos = self.__create_check_box("Video", container)
+
+ def __create_search_view(self, notebook):
+ vbox = gtk.VBox(False, 0)
+ hbox = gtk.HBox(True, 0)
+ self.__create_search_list(vbox)
+ self.__create_search_controls(hbox)
+ vbox.pack_start(hbox, False, True, MainWindow.container_padding)
+ notebook.append_page(vbox, gtk.Label("Search"))
+
+ def __page_changed(self, notebook, page, page_number):
+ sel = self.__server_view.get_selection()
+ if sel:
+ self.__change_server(page_number, sel)
+
+ def __create_notebook(self, table):
+ notebook = gtk.Notebook()
+ self.__create_search_view(notebook)
+ self.__create_browse_view(notebook)
+ notebook.connect("switch-page", self.__page_changed)
+ table.attach(notebook, left_attach=1, right_attach=4,
+ top_attach=0, bottom_attach=1)
+ self.__notebook = notebook
+
+ def __create_widgets(self, window):
+ table = gtk.Table(rows=1, columns=4, homogeneous=True)
+ self.__create_server_list(table)
+ self.__create_notebook(table)
+ window.add(table)
+ self.__main_view = table
+
+ def __create_window(self):
+ window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ window.set_title("Media Service Demo")
+ window.set_resizable(True)
+ window.set_default_size(640, 480)
+ window.connect("delete_event", self.delete_event)
+ window.connect("destroy", self.destroy)
+ self.__create_widgets(window)
+ window.show_all()
+ self.__window = window
+
+ def __found_server(self, path):
+ liststore = self.__server_view.get_model()
+ value = self.__state.get_server_list()[path]
+ rowref = self.__append_server_list_row(liststore, path, value)
+ self.__select_server(rowref)
+
+ def __lost_server(self, path):
+ liststore = self.__server_view.get_model()
+ rowref = liststore.get_iter_first()
+ while rowref and liststore.get_value(rowref, 2) != path:
+ rowref = liststore.iter_next(rowref)
+ if rowref:
+ path_to_delete = liststore.get_path(rowref)
+ selection = self.__server_view.get_selection()
+ selected_path = liststore.get_path(selection.get_selected()[1])
+ liststore.remove(rowref)
+ if path_to_delete == selected_path:
+ rowref = liststore.get_iter_first()
+ if rowref:
+ selection.select_iter(rowref)
+ self.__server_view.set_cursor(liststore.get_path(rowref))
+
+ def __init__(self, state):
+ self.__search_path = None
+ self.__browse_path = None
+ self.__state = state
+ self.__state.set_lost_server_cb(self.__lost_server)
+ self.__state.set_found_server_cb(self.__found_server)
+ self.__create_window()
+ self.__overlay = None
+ self.__sort_order = SortOrder()
+
+ liststore = self.__server_view.get_model()
+ rowref = liststore.get_iter_first()
+ if rowref:
+ self.__select_server(rowref)
diff --git a/src/msd/msd_player.py b/src/msd/msd_player.py
new file mode 100644
index 0000000..50c6572
--- /dev/null
+++ b/src/msd/msd_player.py
@@ -0,0 +1,257 @@
+# media-service-demo
+#
+# Copyright (C) 2012 Intel Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU Lesser General Public License,
+# version 2.1, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope 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.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Mark Ryan <mark.d.ryan@intel.com>
+#
+
+import pygtk
+pygtk.require('2.0')
+import glib
+import gtk
+import pygst
+pygst.require("0.10")
+import gst
+import datetime
+
+from msd_utils import *
+
+class PlayWindowBase(object):
+
+ def __init__(self, name, url, close_window):
+ self.__name = name
+ self.__url = url
+ self.__close_window = close_window
+
+ self.__container = gtk.VBox(False, 0)
+ self.drawing_area = gtk.DrawingArea()
+ self.private_area = gtk.VBox(True, 0)
+ self.ok_button = gtk.Button("Close")
+ self.ok_button.connect("clicked", self.quit)
+ self.__container.pack_start(self.drawing_area, True, True, 0)
+ self.__container.pack_start(self.private_area, False, False, 0)
+ self.__container.pack_start(self.ok_button, False, False, 0)
+
+ def quit(self, button):
+ self.__close_window()
+
+ def get_container(self):
+ return self.__container
+
+ def cancel_playback(self):
+ pass
+
+ def draw_image(self, image):
+ x = 0
+ y = 0
+ gc = self.drawing_area.get_style().fg_gc[gtk.STATE_NORMAL]
+ rect = self.drawing_area.get_allocation()
+ width_scale = image.get_width() / float(rect.width)
+ height_scale = image.get_height() / float(rect.height)
+ if ((width_scale < 1.0 and height_scale < 1.0) or
+ (width_scale >= 1.0 and height_scale >= 1.0)):
+ if width_scale < height_scale:
+ divisor = height_scale
+ x = (rect.width - int(image.get_width() / divisor)) / 2
+ else:
+ divisor = width_scale
+ y = (rect.height - int(image.get_height() / divisor)) / 2
+ elif width_scale > 1.0:
+ divisor = width_scale
+ y = (rect.height - int(image.get_height() / divisor)) / 2
+ else:
+ divisor = height_scale
+ x = (rect.width - int(image.get_width() / divisor)) / 2
+
+ scaled_image = image.scale_simple(int(image.get_width() / divisor),
+ int(image.get_height() / divisor),
+ gtk.gdk.INTERP_BILINEAR)
+ self.drawing_area.window.draw_pixbuf(gc, scaled_image, 0, 0, x,
+ y, -1, -1)
+
+
+class PlayWindowImage(PlayWindowBase):
+
+ def __init__(self, name, url, close_window):
+ PlayWindowBase.__init__(self, name, url, close_window)
+ try:
+ image = image_from_file(url)
+ self.__image = image.get_pixbuf()
+ self.drawing_area.connect("expose-event", self.__draw)
+ except Exception:
+ pass
+
+ self.get_container().show_all()
+
+ def __draw(self, area, event):
+ self.draw_image(self.__image)
+ return True
+
+class GStreamerWindow(PlayWindowBase):
+
+ def __init__(self, name, url, close_window):
+ PlayWindowBase.__init__(self, name, url, close_window)
+
+ self.player = gst.element_factory_make("playbin2", "player")
+ gsbus = self.player.get_bus()
+ gsbus.add_signal_watch()
+ gsbus.connect("message", self.gs_message_cb)
+
+ self.player.set_property("uri", url)
+
+ button_bar = gtk.HBox(True, 0)
+
+ self.__stop_button = gtk.Button("Stop")
+ self.__stop_button.connect("clicked", self.__stop)
+ self.__pause_button = gtk.Button("Pause")
+ self.__pause_button.connect("clicked", self.__pause)
+ self.__start_button = gtk.Button("Play")
+ self.__start_button.connect("clicked", self.__start)
+ self.__start_button.set_sensitive(False)
+
+ self.__scale = gtk.HScale()
+
+ button_bar.pack_start(self.__start_button, True, True, 0)
+ button_bar.pack_start(self.__pause_button, True, True, 0)
+ button_bar.pack_start(self.__stop_button, True, True, 0)
+
+ self.private_area.pack_start(self.__scale, False, False, 0)
+ self.private_area.pack_start(button_bar, False, False, 0)
+
+ self.get_container().show_all()
+ self.__scale.hide()
+ self.__duration = -1
+ self.__update_pos_id = 0
+ self.__adjustment = None
+ self.player.set_state(gst.STATE_PLAYING)
+
+ def __stop_or_pause(self):
+ self.__pause_button.set_sensitive(False)
+ self.__start_button.set_sensitive(True)
+ self.__scale.set_sensitive(True)
+ if self.__update_pos_id != 0:
+ glib.source_remove(self.__update_pos_id)
+ self.__update_pos_id = 0
+
+ def __stop(self, button):
+ self.player.set_state(gst.STATE_NULL)
+ self.__stop_button.set_sensitive(False)
+ self.__stop_or_pause()
+ if self.__adjustment:
+ self.__adjustment.set_value(0)
+
+ def __start(self, button):
+ self.player.set_state(gst.STATE_PLAYING)
+ self.__scale.set_sensitive(False)
+ self.__start_button.set_sensitive(False)
+ self.__pause_button.set_sensitive(True)
+ self.__stop_button.set_sensitive(True)
+
+ def __pause(self, button):
+ self.player.set_state(gst.STATE_PAUSED)
+ self.__stop_or_pause()
+
+ def __update_pos(self, user_data):
+ try:
+ pos = self.player.query_position(gst.FORMAT_TIME, None)[0]
+ if pos != -1:
+ pos = pos / 1000000000.0
+ self.__adjustment.set_value(pos)
+ except Exception:
+ pass
+ return True
+
+ def cancel_playback(self):
+ self.player.set_state(gst.STATE_NULL)
+
+ def quit(self, button):
+ self.player.set_state(gst.STATE_NULL)
+ PlayWindowBase.quit(self, button)
+
+ def __adjusted(self, adjustment):
+ (ret, state, pending) = self.player.get_state()
+ if state != gst.STATE_PLAYING:
+ seek_pos = adjustment.get_value() * 1000000000
+ self.player.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH,
+ seek_pos)
+
+ def __format_time(self, scale, value):
+ pos = int(self.__adjustment.get_value())
+ return str(datetime.timedelta(seconds=pos))
+
+ def gs_message_cb(self, bus, message):
+ if message.type == gst.MESSAGE_EOS or message.type == gst.MESSAGE_ERROR:
+ self.__stop(None)
+ elif message.type == gst.MESSAGE_STATE_CHANGED:
+ (old, state, pending) = message.parse_state_changed()
+ if self.__duration == -1 and (state == gst.STATE_PLAYING or
+ state == gst.STATE_PAUSED):
+ try:
+ duration = self.player.query_duration(gst.FORMAT_TIME,
+ None)[0]
+ if duration != -1:
+ self.__duration = duration / 1000000000
+ self.__adjustment = gtk.Adjustment(0, 0,
+ self.__duration,
+ .5, .5, 0)
+ self.__scale.set_adjustment(self.__adjustment)
+ self.__scale.set_sensitive(False)
+ self.__adjustment.connect("value-changed",
+ self.__adjusted)
+ self.__scale.connect("format-value", self.__format_time)
+ self.__scale.show()
+ except Exception:
+ pass
+
+ if state == gst.STATE_PLAYING and self.__update_pos_id == 0:
+ self.__update_pos_id = glib.timeout_add(500,
+ self.__update_pos,
+ None)
+
+class PlayWindowAudio(GStreamerWindow):
+
+ def __init__(self, name, url, album_art_url, close_window):
+ GStreamerWindow.__init__(self, name, url, close_window)
+
+ if album_art_url:
+ try:
+ image = image_from_file(album_art_url)
+ self.__image = image.get_pixbuf()
+ self.drawing_area.connect("expose-event", self.__draw)
+ except Exception:
+ pass
+
+ def __draw(self, area, event):
+ self.draw_image(self.__image)
+ return True
+
+class PlayWindowVideo(GStreamerWindow):
+
+ def __init__(self, name, url, close_window):
+ GStreamerWindow.__init__(self, name, url, close_window)
+
+ gsbus = self.player.get_bus()
+ gsbus.enable_sync_message_emission()
+ gsbus.connect("sync-message::element", self.gs_sync_message_cb)
+
+
+ def gs_sync_message_cb(self, bus, message):
+ if message.structure != None and (message.structure.get_name() ==
+ "prepare-xwindow-id"):
+ message.src.set_property("force-aspect-ratio", True)
+ gtk.gdk.threads_enter()
+ message.src.set_xwindow_id(self.drawing_area.window.xid)
+ gtk.gdk.threads_leave()
diff --git a/src/msd/msd_search.py b/src/msd/msd_search.py
new file mode 100644
index 0000000..f5aa0bf
--- /dev/null
+++ b/src/msd/msd_search.py
@@ -0,0 +1,195 @@
+# media-service-demo
+#
+# Copyright (C) 2012 Intel Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU Lesser General Public License,
+# version 2.1, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope 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.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Mark Ryan <mark.d.ryan@intel.com>
+#
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import cStringIO
+import dateutil.parser
+import datetime
+
+from msd_sort_order import *
+from msd_upnp import *
+
+class SearchModel(gtk.GenericTreeModel):
+ columns = (("DisplayName", str), ("Artist", str), ("Date", str),
+ ("Type",str), ("Path", str), ("URLs", str))
+ filter = ["Artist", "DisplayName", "URLs", "Date", "Path",
+ "Type"]
+
+ buffer_size = 50
+
+ @staticmethod
+ def __create_query_string(query, images, videos, music):
+ search_string = None
+
+ if images or videos or music:
+ q_buffer = cStringIO.StringIO()
+ try:
+ if query != "":
+ q_buffer.write('(Artist contains "{0}"\
+ or DisplayName contains "{0}")'.format(query))
+ q_buffer.write(' and ')
+ q_buffer.write(' ( ')
+ if images:
+ q_buffer.write('Type derivedfrom "image" ')
+ if videos:
+ if images:
+ q_buffer.write(' or ')
+ q_buffer.write('Type derivedfrom "video" ')
+ if music:
+ if images or videos:
+ q_buffer.write(' or ')
+ q_buffer.write('Type derivedfrom "audio" ')
+ q_buffer.write(' )')
+ search_string = q_buffer.getvalue()
+ finally:
+ q_buffer.close()
+
+ return search_string
+
+ def __get_search_items(self, start, count):
+ if self.__items:
+ end = start
+ while (end < start + SearchModel.buffer_size and
+ end < self.__max_items and not self.__items[end]):
+ end = end + 1
+ else:
+ end = count
+
+ if start < end:
+ count = end - start
+ try:
+ sort_descriptor = self.__sort_order.get_upnp_sort_order()
+ items, max_items = self.__root.search(self.__search_string,
+ start, count,
+ SearchModel.filter,
+ sort_descriptor)
+
+ max_items = max(max_items, len(items))
+
+ # TODO: I need to inform list view if max item has changed?
+
+ if max_items != self.__max_items:
+ self.__max_items = max_items
+ self.__items = [None] * self.__max_items
+ for item in items:
+ self.__items[start] = item
+ start = start + 1
+ except Exception:
+ pass
+
+ def __init__(self, root, query, images, videos, music, sort_order):
+ gtk.GenericTreeModel.__init__(self)
+
+ self.__items = None
+ self.__max_items = 0
+ self.__root = root
+ self.__sort_order = sort_order
+ self.__search_string = SearchModel.__create_query_string(query, images,
+ videos, music)
+ if self.__search_string:
+ self.__get_search_items(0, SearchModel.buffer_size)
+
+ def flush(self):
+ i = 0
+ while i < self.__max_items:
+ self.__items[i] = None
+ i = i + 1
+
+ def on_get_flags(self):
+ return gtk.TREE_MODEL_LIST_ONLY | gtk.TREE_MODEL_ITERS_PERSIST
+
+ def on_get_n_columns(self):
+ return len(SearchModel.columns)
+
+ def on_get_column_type(self, n):
+ return SearchModel.columns[n][1]
+
+ def on_get_iter(self, path):
+ if path[0] >= self.__max_items:
+ raise ValueError("Invalid Path")
+ return path[0]
+
+ def on_get_path(self, rowref):
+ return (rowref, )
+
+ def on_get_value(self, rowref, col):
+ retval = None
+ self.__get_search_items(rowref, SearchModel.buffer_size)
+ if rowref < self.__max_items and self.__items and self.__items[rowref]:
+ key = SearchModel.columns[col][0]
+ if key in self.__items[rowref]:
+ data = self.__items[rowref][key]
+ if col == 2:
+ date = dateutil.parser.parse(data)
+ retval = date.strftime("%x")
+ elif col == 3:
+ data = data[0].upper() + data[1:]
+ period = data.find('.')
+ if period >=0:
+ retval = data[:period]
+ else:
+ retval = data
+ elif col == 5:
+ retval = data[0]
+ else:
+ retval = data
+ elif col == 1:
+ retval = "Unknown"
+ elif col == 2:
+ retval = datetime.date.today().strftime("%x")
+ else:
+ retval = ""
+ else:
+ retval = ""
+
+ return retval
+
+ def on_iter_next(self, rowref):
+ retval = None
+ rowref = rowref + 1
+ if rowref < self.__max_items:
+ retval = rowref
+ return retval
+
+ def on_iter_children(self, rowref):
+ retval = 0
+ if rowref:
+ retval = None
+ return retval
+
+ def on_iter_has_child(self, rowref):
+ return False
+
+ def on_iter_n_children(self, rowref):
+ retval = 0
+ if not rowref:
+ retval = self.__max_items
+ return retval
+
+ def on_iter_nth_child(self, rowref, child):
+ retval = None
+ if not rowref and child < self.__max_items:
+ retval = child
+ return retval
+
+ def on_iter_parent(self, child):
+ return None
diff --git a/src/msd/msd_sort_order.py b/src/msd/msd_sort_order.py
new file mode 100644
index 0000000..5b94bca
--- /dev/null
+++ b/src/msd/msd_sort_order.py
@@ -0,0 +1,38 @@
+# media-service-demo
+#
+# Copyright (C) 2012 Intel Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU Lesser General Public License,
+# version 2.1, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope 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.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Mark Ryan <mark.d.ryan@intel.com>
+#
+
+class SortOrder(object):
+
+ def __init__(self):
+ self.sort_by = "DisplayName"
+ self.ascending = False
+
+ def set_sort_by(self, sort_by):
+ if sort_by == self.sort_by:
+ self.ascending = not self.ascending
+ else:
+ self.sort_by = sort_by
+
+ def get_upnp_sort_order(self):
+ if self.ascending:
+ retval = "+"
+ else:
+ retval = "-"
+ return retval + self.sort_by
diff --git a/src/msd/msd_upnp.py b/src/msd/msd_upnp.py
new file mode 100644
index 0000000..c2e83cc
--- /dev/null
+++ b/src/msd/msd_upnp.py
@@ -0,0 +1,109 @@
+# media-service-demo
+#
+# Copyright (C) 2012 Intel Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU Lesser General Public License,
+# version 2.1, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope 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.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Mark Ryan <mark.d.ryan@intel.com>
+#
+
+from msd_utils import *
+import dbus
+import os
+
+class MediaObject(object):
+
+ def __init__(self, path):
+ bus = dbus.SessionBus()
+ self.__propsIF = dbus.Interface(bus.get_object(
+ 'com.intel.media-service-upnp', path),
+ 'org.freedesktop.DBus.Properties')
+ def get_prop(self, prop_name, iface = ""):
+ return self.__propsIF.Get(iface, prop_name)
+
+class Container(MediaObject):
+
+ def __init__(self, path):
+ MediaObject.__init__(self, path)
+ bus = dbus.SessionBus()
+ self.__containerIF = dbus.Interface(bus.get_object(
+ 'com.intel.media-service-upnp', path),
+ 'org.gnome.UPnP.MediaContainer2')
+
+ def search(self, query, offset, count, fltr, sort=""):
+ return self.__containerIF.SearchObjectsEx(query, offset, count, fltr,
+ sort)
+
+ def list_children(self, offset, count, fltr, sort=""):
+ return self.__containerIF.ListChildrenEx(offset, count, fltr, sort)
+
+class State(object):
+
+ @staticmethod
+ def __create_server_tuple(path):
+ server = MediaObject(path)
+ folderName = server.get_prop("FriendlyName");
+
+ try:
+ icon_url = server.get_prop("IconURL");
+ image = image_from_file(icon_url)
+ except Exception:
+ image = None
+
+ return (folderName, image)
+
+ def __init_servers(self):
+ for i in self.__manager.GetServers():
+ try:
+ self.__servers[i] = State.__create_server_tuple(i)
+ except dbus.exceptions.DBusException:
+ pass
+
+ def found_server(self, path):
+ if not path in self.__servers:
+ try:
+ self.__servers[path] = State.__create_server_tuple(path)
+ if self.__found_server_cb:
+ self.__found_server_cb(path)
+ finally:
+ pass
+
+ def lost_server(self, path):
+ if path in self.__servers:
+ del self.__servers[path]
+ if self.__lost_server_cb:
+ self.__lost_server_cb(path)
+
+ def __init__(self):
+ bus = dbus.SessionBus()
+ self.__manager = dbus.Interface(bus.get_object(
+ 'com.intel.media-service-upnp',
+ '/com/intel/MediaServiceUPnP'),
+ 'com.intel.MediaServiceUPnP.Manager')
+ self.__servers = {}
+ self.__found_server_cb = None
+ self.__lost_server_cb = None
+
+ self.__manager.connect_to_signal("FoundServer", self.found_server)
+ self.__manager.connect_to_signal("LostServer", self.lost_server)
+ self.__init_servers()
+
+ def set_lost_server_cb(self, callback):
+ self.__lost_server_cb = callback
+
+ def set_found_server_cb(self, callback):
+ self.__found_server_cb = callback
+
+ def get_server_list(self):
+ return self.__servers
diff --git a/src/msd/msd_utils.py b/src/msd/msd_utils.py
new file mode 100644
index 0000000..4a9b703
--- /dev/null
+++ b/src/msd/msd_utils.py
@@ -0,0 +1,41 @@
+# media-service-demo
+#
+# Copyright (C) 2012 Intel Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU Lesser General Public License,
+# version 2.1, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope 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.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Mark Ryan <mark.d.ryan@intel.com>
+#
+
+import tempfile
+import pygtk
+pygtk.require('2.0')
+import gtk
+import urllib2
+import os
+
+def image_from_file(url):
+ tmpfile = tempfile.NamedTemporaryFile(delete=False)
+ tmpFileName = tmpfile.name
+ image = None
+ try:
+ with tmpfile:
+ message = urllib2.urlopen(url, None, 1)
+ tmpfile.write(message.read())
+ image = gtk.Image()
+ image.set_from_file(tmpfile.name)
+ finally:
+ os.unlink(tmpFileName)
+
+ return image