summaryrefslogtreecommitdiff
path: root/plugin.video.mediathek
diff options
context:
space:
mode:
authorChristian Kölpin <raptor2101@mykolab.com>2016-12-16 19:22:24 +0100
committerPhilipp Temminghoff <phil65@kodi.tv>2016-12-16 19:22:24 +0100
commit7bafd9290195ce39adc83531d103a4a2a97d5b7b (patch)
tree67ec57f7d5834fe94f25bcf7a57d0674f4230f8e /plugin.video.mediathek
parent82884ecc755da2186f1724e54501b4ac73e573b4 (diff)
[plugin.video.mediathek] 0.7.5 (#756)
[plugin.video.mediathek] 0.7.5
Diffstat (limited to 'plugin.video.mediathek')
-rwxr-xr-xplugin.video.mediathek/.gitignore1
-rw-r--r--plugin.video.mediathek/LICENSE.txt533
-rw-r--r--plugin.video.mediathek/addon.xml40
-rw-r--r--plugin.video.mediathek/changelog.txt70
-rw-r--r--plugin.video.mediathek/default.py110
-rw-r--r--plugin.video.mediathek/icon.pngbin0 -> 93751 bytes
-rw-r--r--plugin.video.mediathek/mediathek/__init__.py134
-rw-r--r--plugin.video.mediathek/mediathek/ard.py168
-rw-r--r--plugin.video.mediathek/mediathek/arte.py262
-rw-r--r--plugin.video.mediathek/mediathek/dreisat.py177
-rw-r--r--plugin.video.mediathek/mediathek/factory.py42
-rw-r--r--plugin.video.mediathek/mediathek/kika.py140
-rw-r--r--plugin.video.mediathek/mediathek/ndr.py274
-rw-r--r--plugin.video.mediathek/mediathek/orf.py162
-rw-r--r--plugin.video.mediathek/mediathek/wdr.py191
-rw-r--r--plugin.video.mediathek/mediathek/zdf.py152
-rw-r--r--plugin.video.mediathek/resources/language/English/strings.po68
-rw-r--r--plugin.video.mediathek/resources/language/German/strings.po68
-rw-r--r--plugin.video.mediathek/resources/language/Italian/strings.po68
-rw-r--r--plugin.video.mediathek/resources/logos/3Sat.jpgbin0 -> 5364 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/ARD.jpgbin0 -> 3418 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/ARTE.jpgbin0 -> 3962 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/BayernFS.jpgbin0 -> 8524 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/ORF.jpgbin0 -> 3012 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/ZDF.jpgbin0 -> 8180 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/phoenix.jpgbin0 -> 3803 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/png/3Sat.pngbin0 -> 29504 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/png/ARD.pngbin0 -> 53574 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/png/ARTE.pngbin0 -> 33969 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/png/BayernFS.pngbin0 -> 43019 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/png/KI.KA-Plus.pngbin0 -> 24681 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/png/LICENSE118
-rw-r--r--plugin.video.mediathek/resources/logos/png/NDR.pngbin0 -> 16778 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/png/ORF.pngbin0 -> 24064 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/png/ZDF.pngbin0 -> 47590 bytes
-rw-r--r--plugin.video.mediathek/resources/logos/png/phoenix.pngbin0 -> 25061 bytes
-rw-r--r--plugin.video.mediathek/resources/settings.xml10
-rw-r--r--plugin.video.mediathek/simplexbmc.py216
38 files changed, 3004 insertions, 0 deletions
diff --git a/plugin.video.mediathek/.gitignore b/plugin.video.mediathek/.gitignore
new file mode 100755
index 0000000..539da74
--- /dev/null
+++ b/plugin.video.mediathek/.gitignore
@@ -0,0 +1 @@
+*.py[co]
diff --git a/plugin.video.mediathek/LICENSE.txt b/plugin.video.mediathek/LICENSE.txt
new file mode 100644
index 0000000..48c6196
--- /dev/null
+++ b/plugin.video.mediathek/LICENSE.txt
@@ -0,0 +1,533 @@
+ GNU GENERAL PUBLIC LICENSE
+
+Version 3, 29 June 2007
+
+Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+Preamble
+
+The GNU General Public License is a free, copyleft license for software and
+other kinds of works.
+
+The licenses for most software and other practical works are designed to take
+away your freedom to share and change the works. By contrast, the GNU General
+Public License is intended to guarantee your freedom to share and change all
+versions of a program--to make sure it remains free software for all its users.
+We, the Free Software Foundation, use the GNU General Public License for most of
+our software; it applies also to any other work released this way by its
+authors. You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for them if you wish), that you
+receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs, and that you know you can do
+these things.
+
+To protect your rights, we need to prevent others from denying you these rights
+or asking you to surrender the rights. Therefore, you have certain
+responsibilities if you distribute copies of the software, or if you modify it:
+responsibilities to respect the freedom of others.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must pass on to the recipients the same freedoms that you received. You
+must make sure that they, too, receive or can get the source code. And you must
+show them these terms so they know their rights.
+
+Developers that use the GNU GPL protect your rights with two steps: (1) assert
+copyright on the software, and (2) offer you this License giving you legal
+permission to copy, distribute and/or modify it.
+
+For the developers' and authors' protection, the GPL clearly explains that there
+is no warranty for this free software. For both users' and authors' sake, the
+GPL requires that modified versions be marked as changed, so that their problems
+will not be attributed erroneously to authors of previous versions.
+
+Some devices are designed to deny users access to install or run modified
+versions of the software inside them, although the manufacturer can do so. This
+is fundamentally incompatible with the aim of protecting users' freedom to
+change the software. The systematic pattern of such abuse occurs in the area of
+products for individuals to use, which is precisely where it is most
+unacceptable. Therefore, we have designed this version of the GPL to prohibit
+the practice for those products. If such problems arise substantially in other
+domains, we stand ready to extend this provision to those domains in future
+versions of the GPL, as needed to protect the freedom of users.
+
+Finally, every program is threatened constantly by software patents. States
+should not allow patents to restrict development and use of software on
+general-purpose computers, but in those that do, we wish to avoid the special
+danger that patents applied to a free program could make it effectively
+proprietary. To prevent this, the GPL assures that patents cannot be used to
+render the program non-free.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+TERMS AND CONDITIONS
+0. Definitions.
+
+“This License” refers to version 3 of the GNU General Public License.
+
+“Copyright” also means copyright-like laws that apply to other kinds of works,
+such as semiconductor masks.
+
+“The Program” refers to any copyrightable work licensed under this License. Each
+licensee is addressed as “you”. “Licensees” and “recipients” may be individuals
+or organizations.
+
+To “modify” a work means to copy from or adapt all or part of the work in a
+fashion requiring copyright permission, other than the making of an exact copy.
+The resulting work is called a “modified version” of the earlier work or a work
+“based on” the earlier work.
+
+A “covered work” means either the unmodified Program or a work based on the
+Program.
+
+To “propagate” a work means to do anything with it that, without permission,
+would make you directly or secondarily liable for infringement under applicable
+copyright law, except executing it on a computer or modifying a private copy.
+Propagation includes copying, distribution (with or without modification),
+making available to the public, and in some countries other activities as well.
+
+To “convey” a work means any kind of propagation that enables other parties to
+make or receive copies. Mere interaction with a user through a computer network,
+with no transfer of a copy, is not conveying.
+
+An interactive user interface displays “Appropriate Legal Notices” to the extent
+that it includes a convenient and prominently visible feature that (1) displays
+an appropriate copyright notice, and (2) tells the user that there is no
+warranty for the work (except to the extent that warranties are provided), that
+licensees may convey the work under this License, and how to view a copy of this
+License. If the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+1. Source Code.
+
+The “source code” for a work means the preferred form of the work for making
+modifications to it. “Object code” means any non-source form of a work.
+
+A “Standard Interface” means an interface that either is an official standard
+defined by a recognized standards body, or, in the case of interfaces specified
+for a particular programming language, one that is widely used among developers
+working in that language.
+
+The “System Libraries” of an executable work include anything, other than the
+work as a whole, that (a) is included in the normal form of packaging a Major
+Component, but which is not part of that Major Component, and (b) serves only to
+enable use of the work with that Major Component, or to implement a Standard
+Interface for which an implementation is available to the public in source code
+form. A “Major Component”, in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system (if any) on
+which the executable work runs, or a compiler used to produce the work, or an
+object code interpreter used to run it.
+
+The “Corresponding Source” for a work in object code form means all the source
+code needed to generate, install, and (for an executable work) run the object
+code and to modify the work, including scripts to control those activities.
+However, it does not include the work's System Libraries, or general-purpose
+tools or generally available free programs which are used unmodified in
+performing those activities but which are not part of the work. For example,
+Corresponding Source includes interface definition files associated with source
+files for the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require, such as by
+intimate data communication or control flow between those subprograms and other
+parts of the work.
+
+The Corresponding Source need not include anything that users can regenerate
+automatically from other parts of the Corresponding Source.
+
+The Corresponding Source for a work in source code form is that same work.
+2. Basic Permissions.
+
+All rights granted under this License are granted for the term of copyright on
+the Program, and are irrevocable provided the stated conditions are met. This
+License explicitly affirms your unlimited permission to run the unmodified
+Program. The output from running a covered work is covered by this License only
+if the output, given its content, constitutes a covered work. This License
+acknowledges your rights of fair use or other equivalent, as provided by
+copyright law.
+
+You may make, run and propagate covered works that you do not convey, without
+conditions so long as your license otherwise remains in force. You may convey
+covered works to others for the sole purpose of having them make modifications
+exclusively for you, or provide you with facilities for running those works,
+provided that you comply with the terms of this License in conveying all
+material for which you do not control copyright. Those thus making or running
+the covered works for you must do so exclusively on your behalf, under your
+direction and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+Conveying under any other circumstances is permitted solely under the conditions
+stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
+3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+No covered work shall be deemed part of an effective technological measure under
+any applicable law fulfilling obligations under article 11 of the WIPO copyright
+treaty adopted on 20 December 1996, or similar laws prohibiting or restricting
+circumvention of such measures.
+
+When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention is
+effected by exercising rights under this License with respect to the covered
+work, and you disclaim any intention to limit operation or modification of the
+work as a means of enforcing, against the work's users, your or third parties'
+legal rights to forbid circumvention of technological measures.
+4. Conveying Verbatim Copies.
+
+You may convey verbatim copies of the Program's source code as you receive it,
+in any medium, provided that you conspicuously and appropriately publish on each
+copy an appropriate copyright notice; keep intact all notices stating that this
+License and any non-permissive terms added in accord with section 7 apply to the
+code; keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+You may charge any price or no price for each copy that you convey, and you may
+offer support or warranty protection for a fee.
+5. Conveying Modified Source Versions.
+
+You may convey a work based on the Program, or the modifications to produce it
+from the Program, in the form of source code under the terms of section 4,
+provided that you also meet all of these conditions:
+a) The work must carry prominent notices stating that you modified it, and
+giving a relevant date.
+b) The work must carry prominent notices stating that it is released under this
+License and any conditions added under section 7. This requirement modifies the
+requirement in section 4 to “keep intact all notices”.
+c) You must license the entire work, as a whole, under this License to anyone
+who comes into possession of a copy. This License will therefore apply, along
+with any applicable section 7 additional terms, to the whole of the work, and
+all its parts, regardless of how they are packaged. This License gives no
+permission to license the work in any other way, but it does not invalidate such
+permission if you have separately received it.
+d) If the work has interactive user interfaces, each must display Appropriate
+Legal Notices; however, if the Program has interactive interfaces that do not
+display Appropriate Legal Notices, your work need not make them do so.
+
+A compilation of a covered work with other separate and independent works, which
+are not by their nature extensions of the covered work, and which are not
+combined with it such as to form a larger program, in or on a volume of a
+storage or distribution medium, is called an “aggregate” if the compilation and
+its resulting copyright are not used to limit the access or legal rights of the
+compilation's users beyond what the individual works permit. Inclusion of a
+covered work in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+6. Conveying Non-Source Forms.
+
+You may convey a covered work in object code form under the terms of sections 4
+and 5, provided that you also convey the machine-readable Corresponding Source
+under the terms of this License, in one of these ways:
+a) Convey the object code in, or embodied in, a physical product (including a
+physical distribution medium), accompanied by the Corresponding Source fixed on
+a durable physical medium customarily used for software interchange.
+b) Convey the object code in, or embodied in, a physical product (including a
+physical distribution medium), accompanied by a written offer, valid for at
+least three years and valid for as long as you offer spare parts or customer
+support for that product model, to give anyone who possesses the object code
+either (1) a copy of the Corresponding Source for all the software in the
+product that is covered by this License, on a durable physical medium
+customarily used for software interchange, for a price no more than your
+reasonable cost of physically performing this conveying of source, or (2) access
+to copy the Corresponding Source from a network server at no charge.
+c) Convey individual copies of the object code with a copy of the written offer
+to provide the Corresponding Source. This alternative is allowed only
+occasionally and noncommercially, and only if you received the object code with
+such an offer, in accord with subsection 6b.
+d) Convey the object code by offering access from a designated place (gratis or
+for a charge), and offer equivalent access to the Corresponding Source in the
+same way through the same place at no further charge. You need not require
+recipients to copy the Corresponding Source along with the object code. If the
+place to copy the object code is a network server, the Corresponding Source may
+be on a different server (operated by you or a third party) that supports
+equivalent copying facilities, provided you maintain clear directions next to
+the object code saying where to find the Corresponding Source. Regardless of
+what server hosts the Corresponding Source, you remain obligated to ensure that
+it is available for as long as needed to satisfy these requirements.
+e) Convey the object code using peer-to-peer transmission, provided you inform
+other peers where the object code and Corresponding Source of the work are being
+offered to the general public at no charge under subsection 6d.
+
+A separable portion of the object code, whose source code is excluded from the
+Corresponding Source as a System Library, need not be included in conveying the
+object code work.
+
+A “User Product” is either (1) a “consumer product”, which means any tangible
+personal property which is normally used for personal, family, or household
+purposes, or (2) anything designed or sold for incorporation into a dwelling. In
+determining whether a product is a consumer product, doubtful cases shall be
+resolved in favor of coverage. For a particular product received by a particular
+user, “normally used” refers to a typical or common use of that class of
+product, regardless of the status of the particular user or of the way in which
+the particular user actually uses, or expects or is expected to use, the
+product. A product is a consumer product regardless of whether the product has
+substantial commercial, industrial or non-consumer uses, unless such uses
+represent the only significant mode of use of the product.
+
+“Installation Information” for a User Product means any methods, procedures,
+authorization keys, or other information required to install and execute
+modified versions of a covered work in that User Product from a modified version
+of its Corresponding Source. The information must suffice to ensure that the
+continued functioning of the modified object code is in no case prevented or
+interfered with solely because modification has been made.
+
+If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as part of a
+transaction in which the right of possession and use of the User Product is
+transferred to the recipient in perpetuity or for a fixed term (regardless of
+how the transaction is characterized), the Corresponding Source conveyed under
+this section must be accompanied by the Installation Information. But this
+requirement does not apply if neither you nor any third party retains the
+ability to install modified object code on the User Product (for example, the
+work has been installed in ROM).
+
+The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates for a
+work that has been modified or installed by the recipient, or for the User
+Product in which it has been modified or installed. Access to a network may be
+denied when the modification itself materially and adversely affects the
+operation of the network or violates the rules and protocols for communication
+across the network.
+
+Corresponding Source conveyed, and Installation Information provided, in accord
+with this section must be in a format that is publicly documented (and with an
+implementation available to the public in source code form), and must require no
+special password or key for unpacking, reading or copying.
+7. Additional Terms.
+
+“Additional permissions” are terms that supplement the terms of this License by
+making exceptions from one or more of its conditions. Additional permissions
+that are applicable to the entire Program shall be treated as though they were
+included in this License, to the extent that they are valid under applicable
+law. If additional permissions apply only to part of the Program, that part may
+be used separately under those permissions, but the entire Program remains
+governed by this License without regard to the additional permissions.
+
+When you convey a copy of a covered work, you may at your option remove any
+additional permissions from that copy, or from any part of it. (Additional
+permissions may be written to require their own removal in certain cases when
+you modify the work.) You may place additional permissions on material, added by
+you to a covered work, for which you have or can give appropriate copyright
+permission.
+
+Notwithstanding any other provision of this License, for material you add to a
+covered work, you may (if authorized by the copyright holders of that material)
+supplement the terms of this License with terms:
+a) Disclaiming warranty or limiting liability differently from the terms of
+sections 15 and 16 of this License; or
+b) Requiring preservation of specified reasonable legal notices or author
+attributions in that material or in the Appropriate Legal Notices displayed by
+works containing it; or
+c) Prohibiting misrepresentation of the origin of that material, or requiring
+that modified versions of such material be marked in reasonable ways as
+different from the original version; or
+d) Limiting the use for publicity purposes of names of licensors or authors of
+the material; or
+e) Declining to grant rights under trademark law for use of some trade names,
+trademarks, or service marks; or
+f) Requiring indemnification of licensors and authors of that material by anyone
+who conveys the material (or modified versions of it) with contractual
+assumptions of liability to the recipient, for any liability that these
+contractual assumptions directly impose on those licensors and authors.
+
+All other non-permissive additional terms are considered “further restrictions”
+within the meaning of section 10. If the Program as you received it, or any part
+of it, contains a notice stating that it is governed by this License along with
+a term that is a further restriction, you may remove that term. If a license
+document contains a further restriction but permits relicensing or conveying
+under this License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does not survive
+such relicensing or conveying.
+
+If you add terms to a covered work in accord with this section, you must place,
+in the relevant source files, a statement of the additional terms that apply to
+those files, or a notice indicating where to find the applicable terms.
+
+Additional terms, permissive or non-permissive, may be stated in the form of a
+separately written license, or stated as exceptions; the above requirements
+apply either way.
+8. Termination.
+
+You may not propagate or modify a covered work except as expressly provided
+under this License. Any attempt otherwise to propagate or modify it is void, and
+will automatically terminate your rights under this License (including any
+patent licenses granted under the third paragraph of section 11).
+
+However, if you cease all violation of this License, then your license from a
+particular copyright holder is reinstated (a) provisionally, unless and until
+the copyright holder explicitly and finally terminates your license, and (b)
+permanently, if the copyright holder fails to notify you of the violation by
+some reasonable means prior to 60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is reinstated
+permanently if the copyright holder notifies you of the violation by some
+reasonable means, this is the first time you have received notice of violation
+of this License (for any work) from that copyright holder, and you cure the
+violation prior to 30 days after your receipt of the notice.
+
+Termination of your rights under this section does not terminate the licenses of
+parties who have received copies or rights from you under this License. If your
+rights have been terminated and not permanently reinstated, you do not qualify
+to receive new licenses for the same material under section 10.
+9. Acceptance Not Required for Having Copies.
+
+You are not required to accept this License in order to receive or run a copy of
+the Program. Ancillary propagation of a covered work occurring solely as a
+consequence of using peer-to-peer transmission to receive a copy likewise does
+not require acceptance. However, nothing other than this License grants you
+permission to propagate or modify any covered work. These actions infringe
+copyright if you do not accept this License. Therefore, by modifying or
+propagating a covered work, you indicate your acceptance of this License to do
+so.
+10. Automatic Licensing of Downstream Recipients.
+
+Each time you convey a covered work, the recipient automatically receives a
+license from the original licensors, to run, modify and propagate that work,
+subject to this License. You are not responsible for enforcing compliance by
+third parties with this License.
+
+An “entity transaction” is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered work results
+from an entity transaction, each party to that transaction who receives a copy
+of the work also receives whatever licenses to the work the party's predecessor
+in interest had or could give under the previous paragraph, plus a right to
+possession of the Corresponding Source of the work from the predecessor in
+interest, if the predecessor has it or can get it with reasonable efforts.
+
+You may not impose any further restrictions on the exercise of the rights
+granted or affirmed under this License. For example, you may not impose a
+license fee, royalty, or other charge for exercise of rights granted under this
+License, and you may not initiate litigation (including a cross-claim or
+counterclaim in a lawsuit) alleging that any patent claim is infringed by
+making, using, selling, offering for sale, or importing the Program or any
+portion of it.
+11. Patents.
+
+A “contributor” is a copyright holder who authorizes use under this License of
+the Program or a work on which the Program is based. The work thus licensed is
+called the contributor's “contributor version”.
+
+A contributor's “essential patent claims” are all patent claims owned or
+controlled by the contributor, whether already acquired or hereafter acquired,
+that would be infringed by some manner, permitted by this License, of making,
+using, or selling its contributor version, but do not include claims that would
+be infringed only as a consequence of further modification of the contributor
+version. For purposes of this definition, “control” includes the right to grant
+patent sublicenses in a manner consistent with the requirements of this License.
+
+Each contributor grants you a non-exclusive, worldwide, royalty-free patent
+license under the contributor's essential patent claims, to make, use, sell,
+offer for sale, import and otherwise run, modify and propagate the contents of
+its contributor version.
+
+In the following three paragraphs, a “patent license” is any express agreement
+or commitment, however denominated, not to enforce a patent (such as an express
+permission to practice a patent or covenant not to sue for patent infringement).
+To “grant” such a patent license to a party means to make such an agreement or
+commitment not to enforce a patent against the party.
+
+If you convey a covered work, knowingly relying on a patent license, and the
+Corresponding Source of the work is not available for anyone to copy, free of
+charge and under the terms of this License, through a publicly available network
+server or other readily accessible means, then you must either (1) cause the
+Corresponding Source to be so available, or (2) arrange to deprive yourself of
+the benefit of the patent license for this particular work, or (3) arrange, in a
+manner consistent with the requirements of this License, to extend the patent
+license to downstream recipients. “Knowingly relying” means you have actual
+knowledge that, but for the patent license, your conveying the covered work in a
+country, or your recipient's use of the covered work in a country, would
+infringe one or more identifiable patents in that country that you have reason
+to believe are valid.
+
+If, pursuant to or in connection with a single transaction or arrangement, you
+convey, or propagate by procuring conveyance of, a covered work, and grant a
+patent license to some of the parties receiving the covered work authorizing
+them to use, propagate, modify or convey a specific copy of the covered work,
+then the patent license you grant is automatically extended to all recipients of
+the covered work and works based on it.
+
+A patent license is “discriminatory” if it does not include within the scope of
+its coverage, prohibits the exercise of, or is conditioned on the non-exercise
+of one or more of the rights that are specifically granted under this License.
+You may not convey a covered work if you are a party to an arrangement with a
+third party that is in the business of distributing software, under which you
+make payment to the third party based on the extent of your activity of
+conveying the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory patent
+license (a) in connection with copies of the covered work conveyed by you (or
+copies made from those copies), or (b) primarily for and in connection with
+specific products or compilations that contain the covered work, unless you
+entered into that arrangement, or that patent license was granted, prior to 28
+March 2007.
+
+Nothing in this License shall be construed as excluding or limiting any implied
+license or other defenses to infringement that may otherwise be available to you
+under applicable patent law.
+12. No Surrender of Others' Freedom.
+
+If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not excuse
+you from the conditions of this License. If you cannot convey a covered work so
+as to satisfy simultaneously your obligations under this License and any other
+pertinent obligations, then as a consequence you may not convey it at all. For
+example, if you agree to terms that obligate you to collect a royalty for
+further conveying from those to whom you convey the Program, the only way you
+could satisfy both those terms and this License would be to refrain entirely
+from conveying the Program.
+13. Use with the GNU Affero General Public License.
+
+Notwithstanding any other provision of this License, you have permission to link
+or combine any covered work with a work licensed under version 3 of the GNU
+Affero General Public License into a single combined work, and to convey the
+resulting work. The terms of this License will continue to apply to the part
+which is the covered work, but the special requirements of the GNU Affero
+General Public License, section 13, concerning interaction through a network
+will apply to the combination as such.
+14. Revised Versions of this License.
+
+The Free Software Foundation may publish revised and/or new versions of the GNU
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems
+or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies
+that a certain numbered version of the GNU General Public License “or any later
+version” applies to it, you have the option of following the terms and
+conditions either of that numbered version or of any later version published by
+the Free Software Foundation. If the Program does not specify a version number
+of the GNU General Public License, you may choose any version ever published by
+the Free Software Foundation.
+
+If the Program specifies that a proxy can decide which future versions of the
+GNU General Public License can be used, that proxy's public statement of
+acceptance of a version permanently authorizes you to choose that version for
+the Program.
+
+Later license versions may give you additional or different permissions.
+However, no additional obligations are imposed on any author or copyright holder
+as a result of your choosing to follow a later version.
+15. Disclaimer of Warranty.
+
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
+PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
+QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
+DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+16. Limitation of Liability.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
+COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
+INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
+PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY
+HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+17. Interpretation of Sections 15 and 16.
+
+If the disclaimer of warranty and limitation of liability provided above cannot
+be given local legal effect according to their terms, reviewing courts shall
+apply local law that most closely approximates an absolute waiver of all civil
+liability in connection with the Program, unless a warranty or assumption of
+liability accompanies a copy of the Program in return for a fee. \ No newline at end of file
diff --git a/plugin.video.mediathek/addon.xml b/plugin.video.mediathek/addon.xml
new file mode 100644
index 0000000..975df8a
--- /dev/null
+++ b/plugin.video.mediathek/addon.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<addon
+ id="plugin.video.mediathek"
+ name="Mediathek"
+ version="0.7.5"
+ provider-name="Raptor 2101">
+ <requires>
+ <import addon="xbmc.python" version="2.25.0"/>
+ <import addon="script.module.beautifulsoup4" version="4.3.2"/>
+ </requires>
+ <extension
+ point="xbmc.python.pluginsource"
+ library="default.py">
+ <provides>video</provides>
+ </extension>
+ <extension point="xbmc.addon.metadata">
+ <summary lang="de">Ermöglicht den Zugriff auf alle deutschen Mediatheken der öffentlich Rechtlichen</summary>
+ <summary lang="en">Gives access to most video-platforms from German public service broadcasters</summary>
+ <summary lang="it">Fornisce l'accesso a gran parte delle piattaforme video operate dalle emittenti pubbliche tedesche</summary>
+ <description lang="de">Ermöglicht den Zugriff auf alle deutschen Mediatheken der öffentlich Rechtlichen.
+Aktuell implementiert sind:
+ 3Sat, ARD, ZDF, ARTE, ORF, NDR, KiKa (ohne Kikaninchen)
+</description>
+ <description lang="en">Gives access to most video-platforms from German public service broadcasters.
+Currently implemented:
+ 3Sat, ARD, ZDF, ARTE, ORF, NDR, KiKa (without Kikaninchen)
+</description>
+ <description lang="it">Fornisce l'accesso a gran parte delle piattaforme video operate dalle emittenti pubbliche tedesche.
+Al momento sono supportate le seguenti emittenti:
+ 3Sat, ARD, ZDF, ARTE, ORF, NDR, KiKa (senza Kikaninchen)
+ </description>
+ <language>de</language>
+ <platform>all</platform>
+ <license>GNU GENERAL PUBLIC LICENSE. Version 3, 29 June 2007</license>
+ <forum>https://github.com/raptor2101/Mediathek/issues</forum>
+ <website>https://github.com/raptor2101/Mediathek</website>
+ <source>https://github.com/raptor2101/Mediathek</source>
+ <email>raptor2101@gmx.de</email>
+ </extension>
+</addon>
diff --git a/plugin.video.mediathek/changelog.txt b/plugin.video.mediathek/changelog.txt
new file mode 100644
index 0000000..f0ea03b
--- /dev/null
+++ b/plugin.video.mediathek/changelog.txt
@@ -0,0 +1,70 @@
+0.7.5 - CHG: reimplement ZDF Mediathak (important: ZDF enforces TLSv1 with SNI enabled, what is only supported by python 2.7.9.)
+ Temporaily fix ARTE
+0.7.4 - CHG: Repearing NDR und 3SAT
+0.7.3 - CHG: Change to BeautifulSoup instead of lxml
+0.7.2 - FIX: Multiple bugfixes for ard (by p-friedrich)
+0.7.1 - FIX: Minor bugfixes for ard
+0.7.0 - FIX: Minor bugfixes for arte,Kika,ZDF
+0.6.9 - FIX: Fixes for NDR (by
+0.6.8 - FIX: Fixes for KIKA
+0.6.7 - FIX: Fixes for ARTE and NDR
+0.6.6 - FIX: Minor Fixes for ARTE and ARD
+0.6.5 - FIX: Minor Fixes for ARTE and NDR
+0.6.4 - FIX: ARTE and NDR
+0.6.3 - FIX: extend ARD plugin
+0.6.2 - FIX: adopt ARD infrastructure change
+0.6.1 - FIX: adopt NDR infrastructure change
+0.6.0 - FIX: Regexp for crawling the ARD-Mediathek
+0.5.9 - FIX: adopt NDR infrastructure change (by dethfee)
+0.5.8 - FIX: adopt NDR link change (by Toerless Eckert)
+0.5.7 - CHG: Temporaily disable BR, BR Alpha
+ FIX: 3Sat offers webem, will used as fallback
+ REM: WDR Support - they moves to a creepy format and i'am unable to handle this in xbmc correctly
+0.5.6 - FIX: NDR Update by dethfeet
+0.5.5 - FIX: More NDR fixes
+0.5.4 - FIX: NDR Fixed by dethfeet
+0.5.2 - FIX: adopt some specialities in the ARD Format
+0.5.2 - FIX: Get all ARD features back working
+0.5.1 - FIX: Get ARD working again (without "Sendungen A-Z")
+0.5.0 - FIX: ORF-Program listing is parsed from Webseite instead of being hardcoded
+0.4.9 - FIX: KiKa Plus naming
+0.4.8 - ADD: show http://www.3sat.de/mediathek/rss/mediathek_makro.xml to 3sat show list.
+ FIX: BR Alpha
+0.4.7 - Fix syntax error
+0.4.6 - Minor fix for rasp-xbmc
+ added new channels for kika
+0.4.5 - FIX: ARTE and KIKA - adopt format changes
+0.4.4 - FIX: Get ARD working again (base code from soern)
+0.4.3 - FIX: NDR streams / NDR changed their streaming adresses by soern
+0.4.2 - FIX: WDR add search function, add stream duration, add "pageSize" optioni (Soern)
+ FIX: bayrisches FS podcasts eingefügt, Auswahl nach Sendungen, Suche (Soern)
+0.4.1 - FIX: Soern completed the NDR implementation
+0.4.0 - FIX: Commit FIX from soern
+0.3.9 - ADD: KIKA (Without Kikaninchen)
+0.3.8 - FIX: 3SAT Broken Link
+ ADD: NDR
+0.3.7 - FIX: ORF
+0.3.6 - FIX: WDR
+0.3.5 - FIX: BayernFS
+0.3.4 - FIX: ARTE
+0.3.3 - ADD: BR-Alpha
+0.3.2 - Version bump for eden pre
+0.2.2 - ADD: WDR Mediathek
+ FIX: ZDF Mediathek will now ignore mp3 files
+ FIX: OfflineCache removed
+0.2.1 - ADD: PageNavigation for ZDF
+ FIX: ChannelLogos fixed
+0.2.0 - FIX: Minor Bugfixes
+0.1.9 - FIX: ORF Playback
+ FIX: Progressbar is displayed corectly
+0.1.8 - ZDF Changes their Interface, reimplement it
+0.1.7 - Adding ORF
+0.1.6 - FIXES: Arty playback now functional, RTMP set as default, Sorting of Items in Bayrisches Fernsehen
+ ADD: Searching for ARTE, ARD, ZDF, 3Sat
+0.1.5 - Bypassing Arte-FalshVersion check
+ - Adding Bayrisches Fernsehen (by Benedikt Elser)
+0.1.4 - Some improvements for ARD
+0.1.3 - Adjust ARD Parsing - ard now works fine
+0.1.2 - Now RTMP playback is avaible under Windows !!!
+0.1.1 - added script to extract Media-Information, Added timeout while downloading something.
+0.1.0 - initial release
diff --git a/plugin.video.mediathek/default.py b/plugin.video.mediathek/default.py
new file mode 100644
index 0000000..c09863f
--- /dev/null
+++ b/plugin.video.mediathek/default.py
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+#-------------LicenseHeader--------------
+# plugin.video.mediathek - Gives access to most video-platforms from German public service broadcasters
+# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de]
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import urllib,xbmc,os,xbmcaddon
+from simplexbmc import SimpleXbmcGui
+from mediathek.factory import MediathekFactory
+__plugin__ = "mediathek"
+
+settings = xbmcaddon.Addon(id='plugin.video.mediathek');
+
+gui = SimpleXbmcGui(settings);
+
+def get_params():
+ paramDict = {}
+ try:
+ print "get_params() argv=", sys.argv
+ if sys.argv[2]:
+ paramPairs=sys.argv[2][1:].split( "&" )
+ for paramsPair in paramPairs:
+ paramSplits = paramsPair.split('=')
+ if (len(paramSplits))==2:
+ paramDict[paramSplits[0]] = paramSplits[1]
+ except:
+ errorOK()
+ return paramDict
+
+
+params = get_params();
+mediathekName = params.get("type", "")
+action=params.get("action", "")
+
+DIR_HOME = xbmc.translatePath(settings.getAddonInfo("profile"))
+if not os.path.exists(DIR_HOME):
+ os.mkdir(DIR_HOME);
+
+gui.log("Quality: %s"%gui.quality);
+gui.log("argv[0]: %s"%sys.argv[0]);
+gui.log("argv[1]: %s"%sys.argv[1]);
+gui.openMenuContext();
+factory = MediathekFactory();
+
+
+
+
+if(mediathekName == ""):
+ if(action == ""):
+ gui.addSearchButton(None);
+ gui.listAvailableMediathekes(factory.getAvaibleMediathekTypes());
+ else:
+ result = gui.keyboardInput();
+ if (result.isConfirmed()):
+ searchText = unicode(result.getText().decode('UTF-8'));
+ for name in factory.getAvaibleMediathekTypes():
+ mediathek = factory.getMediathek(name, gui);
+ if(mediathek.isSearchable()):
+ mediathek.searchVideo(searchText);
+ else:
+ gui.back();
+
+else:
+ cat=int(params.get("cat", 0))
+ mediathek = factory.getMediathek(mediathekName,gui);
+
+ if(action == "openTopicPage"):
+ link = urllib.unquote_plus(params.get("link", ""));
+ gui.log(link);
+ mediathek.buildPageMenu(link, 0);
+ elif(action == "openPlayList"):
+ link = urllib.unquote_plus(params.get("link", ""));
+ gui.log(link);
+ remotePlaylist = mediathek.loadPage(link);
+ gui.playPlaylist(remotePlaylist);
+ elif(action == "openMenu"):
+ path = params.get("path", "0");
+ mediathek.buildMenu(path)
+ elif(action == "search"):
+ result = gui.keyboardInput();
+ if (result.isConfirmed()):
+ searchText = unicode(result.getText().decode('UTF-8'));
+ mediathek.searchVideo(searchText);
+ else:
+ gui.back();
+ elif(action == "openJsonPath"):
+ path = params.get("path", "0");
+ callhash = params.get("callhash", "0");
+ mediathek.buildJsonMenu(path,callhash,0)
+ elif(action == "openJsonLink"):
+ link = urllib.unquote_plus(params.get("link", ""));
+ mediathek.playVideoFromJsonLink(link);
+ else:
+ if(mediathek.isSearchable()):
+ gui.addSearchButton(mediathek);
+ mediathek.displayCategories();
+gui.closeMenuContext();
+
diff --git a/plugin.video.mediathek/icon.png b/plugin.video.mediathek/icon.png
new file mode 100644
index 0000000..4f91188
--- /dev/null
+++ b/plugin.video.mediathek/icon.png
Binary files differ
diff --git a/plugin.video.mediathek/mediathek/__init__.py b/plugin.video.mediathek/mediathek/__init__.py
new file mode 100644
index 0000000..b3aedef
--- /dev/null
+++ b/plugin.video.mediathek/mediathek/__init__.py
@@ -0,0 +1,134 @@
+# -*- coding: utf-8 -*-
+#-------------LicenseHeader--------------
+# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters
+# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de]
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import sys, urllib2,urllib, time;
+import socket
+socket.setdefaulttimeout(1);
+
+class SimpleLink(object):
+ def __init__(self, basePath, size):
+ self.basePath = basePath;
+ self.size = size;
+
+class ComplexLink(object):
+ def __init__(self, basePath, playPath, size):
+ self.basePath = basePath;
+ self.playPath = playPath;
+ self.size = size;
+
+class TreeNode(object):
+ def __init__(self,path,name,link,displayElements,childNodes = []):
+ self.name = name;
+ self.path = path;
+ self.link = link;
+ self.displayElements = displayElements;
+ self.childNodes = childNodes;
+
+class DisplayObject(object):
+ def __init__(self,title,subTitle,picture,description,link=[],isPlayable = True, date = None, duration = None):
+ self.title = title
+ self.subTitle = subTitle
+ self.link = link
+ self.picture = picture
+ self.isPlayable = isPlayable
+ self.description = description
+ self.date = date;
+ self.duration = duration;
+
+class Mediathek(object):
+ def loadPage(self,url, values = None, maxTimeout = None):
+ try:
+ safe_url = url.replace( " ", "%20" ).replace("&amp;","&")
+
+ if(values is not None):
+ data = urllib.urlencode(values)
+ req = urllib2.Request(safe_url, data)
+ else:
+ req = urllib2.Request(safe_url)
+ req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20100101 Firefox/15.0.1')
+ req.add_header('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8')
+ req.add_header('Accept-Language', 'de-de,de;q=0.8,en-us;q=0.5,en;q=0.3')
+ req.add_header('Accept-Charset', 'utf-8')
+
+ if maxTimeout == None:
+ maxTimeout = 60;
+
+ waittime = 0;
+ doc = False;
+ while not doc and waittime < maxTimeout:
+ try:
+ if waittime > 0:
+ time.sleep(waittime);
+ self.gui.log("download %s %d"%(safe_url,waittime));
+ sock = urllib2.urlopen( req )
+ doc = sock.read();
+ sock.close()
+ except:
+ if(waittime == 0):
+ waittime = 1;
+ else:
+ waittime *= 2;
+
+ if doc:
+ try:
+ return doc.encode('utf-8');
+ except:
+ return doc;
+ else:
+ return ''
+ except:
+ return ''
+
+ def buildMenu(self, path, treeNode = None):
+ if(isinstance(path, (str,unicode))):
+ path = path.split('.');
+ if(len(path) > 0):
+ index = int(path.pop(0));
+
+ if(treeNode == None):
+ treeNode = self.menuTree[index];
+ else:
+ treeNode = treeNode.childNodes[index];
+ self.buildMenu(path,treeNode);
+ else:
+ if(treeNode == None):
+ treeNode = self.menuTree[0];
+ self.gui.log(treeNode.name);
+ for childNode in treeNode.childNodes:
+ self.gui.buildMenuLink(childNode,self, len(treeNode.childNodes));
+ if(treeNode.displayElements):
+ self.buildPageMenu(treeNode.link,len(treeNode.childNodes));
+
+ def displayCategories(self):
+ if(len(self.menuTree)>1 or not self.menuTree[0].displayElements):
+ for treeNode in self.menuTree:
+ self.gui.buildMenuLink(treeNode,self,len(self.menuTree))
+ else:
+ self.buildPageMenu(self.menuTree[0].link, 0);
+
+ def walkJson(self, path, jsonObject):
+ path = path.split('.');
+ i = 0
+ while(i < len(path)):
+ if(isinstance(jsonObject,list)):
+ index = int(path.pop(0));
+ else:
+ index = path.pop(0);
+ jsonObject = jsonObject[index];
+
+ return jsonObject;
+
diff --git a/plugin.video.mediathek/mediathek/ard.py b/plugin.video.mediathek/mediathek/ard.py
new file mode 100644
index 0000000..6c66ce2
--- /dev/null
+++ b/plugin.video.mediathek/mediathek/ard.py
@@ -0,0 +1,168 @@
+# -*- coding: utf-8 -*-
+#-------------LicenseHeader--------------
+# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters
+# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de]
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import re, time, datetime;
+from mediathek import *
+
+class ARDMediathek(Mediathek):
+ def __init__(self, simpleXbmcGui):
+ self.gui = simpleXbmcGui;
+ self.rootLink = "http://www.ardmediathek.de"
+ self.menuTree = (
+ TreeNode("0","Neuste Videos",self.rootLink+"/tv/Neueste-Videos/mehr?documentId=21282466",True),
+ TreeNode("1","Sendungen von A-Z","",False,(
+ TreeNode("1.0","0-9",self.rootLink+"/tv/sendungen-a-z?buchstabe=0-9",True),
+ TreeNode("1.1","A",self.rootLink+"/tv/sendungen-a-z?buchstabe=A",True),
+ TreeNode("1.2","B",self.rootLink+"/tv/sendungen-a-z?buchstabe=B",True),
+ TreeNode("1.3","C",self.rootLink+"/tv/sendungen-a-z?buchstabe=C",True),
+ TreeNode("1.4","D",self.rootLink+"/tv/sendungen-a-z?buchstabe=D",True),
+ TreeNode("1.5","E",self.rootLink+"/tv/sendungen-a-z?buchstabe=E",True),
+ TreeNode("1.6","F",self.rootLink+"/tv/sendungen-a-z?buchstabe=F",True),
+ TreeNode("1.7","G",self.rootLink+"/tv/sendungen-a-z?buchstabe=G",True),
+ TreeNode("1.8","H",self.rootLink+"/tv/sendungen-a-z?buchstabe=H",True),
+ TreeNode("1.9","I",self.rootLink+"/tv/sendungen-a-z?buchstabe=I",True),
+ TreeNode("1.10","J",self.rootLink+"/tv/sendungen-a-z?buchstabe=J",True),
+ TreeNode("1.11","K",self.rootLink+"/tv/sendungen-a-z?buchstabe=K",True),
+ TreeNode("1.12","L",self.rootLink+"/tv/sendungen-a-z?buchstabe=L",True),
+ TreeNode("1.13","M",self.rootLink+"/tv/sendungen-a-z?buchstabe=M",True),
+ TreeNode("1.14","N",self.rootLink+"/tv/sendungen-a-z?buchstabe=N",True),
+ TreeNode("1.15","O",self.rootLink+"/tv/sendungen-a-z?buchstabe=O",True),
+ TreeNode("1.16","P",self.rootLink+"/tv/sendungen-a-z?buchstabe=P",True),
+ TreeNode("1.17","Q",self.rootLink+"/tv/sendungen-a-z?buchstabe=Q",True),
+ TreeNode("1.18","R",self.rootLink+"/tv/sendungen-a-z?buchstabe=R",True),
+ TreeNode("1.19","S",self.rootLink+"/tv/sendungen-a-z?buchstabe=S",True),
+ TreeNode("1.20","T",self.rootLink+"/tv/sendungen-a-z?buchstabe=T",True),
+ TreeNode("1.21","U",self.rootLink+"/tv/sendungen-a-z?buchstabe=U",True),
+ TreeNode("1.22","V",self.rootLink+"/tv/sendungen-a-z?buchstabe=V",True),
+ TreeNode("1.23","W",self.rootLink+"/tv/sendungen-a-z?buchstabe=W",True),
+ TreeNode("1.24","X",self.rootLink+"/tv/sendungen-a-z?buchstabe=X",True),
+ TreeNode("1.25","Y",self.rootLink+"/tv/sendungen-a-z?buchstabe=Y",True),
+ TreeNode("1.26","Z",self.rootLink+"/tv/sendungen-a-z?buchstabe=Z",True),
+ )),
+ TreeNode("2","Ausgewählte Dokus".decode("utf-8"),self.rootLink+"/tv/Ausgew%C3%A4hlte-Dokus/mehr?documentId=33649086",True),
+ TreeNode("3","Ausgewählte Filme".decode("utf-8"),self.rootLink+"/tv/Ausgew%C3%A4hlte-Filme/mehr?documentId=33649088",True),
+ TreeNode("4","Alle Reportagen und Dokus",self.rootLink+"/tv/Alle-Dokus-Reportagen/mehr?documentId=29897596",True),
+ TreeNode("5","Alle Filme",self.rootLink+"/tv/Alle-Filme/mehr?documentId=33594630",True),
+ TreeNode("6","Alle Serien",self.rootLink+"/tv/Serien/mehr?documentId=26402940",True),
+ TreeNode("7","Themen",self.rootLink+"/tv/Themen/mehr?documentId=21301810",True),
+ TreeNode("8","Rubriken","",False,(
+ TreeNode("8.0","Kinder",self.rootLink+"/tv/Kinder/Tipps?documentId=21282542",True),
+ TreeNode("8.1","Unterhaltung & Comedy",self.rootLink+"/tv/Unterhaltung-Comedy/mehr?documentId=21282544",True),
+ TreeNode("8.2","Kultur",self.rootLink+"/tv/Kultur/mehr?documentId=21282546",True),
+ TreeNode("8.3","Wissen",self.rootLink+"/tv/Wissen/mehr?documentId=21282530",True),
+ TreeNode("8.4","Politik",self.rootLink+"/tv/Politik/mehr?documentId=29684598",True),
+ TreeNode("8.5","Ratgeber",self.rootLink+"/tv/Ratgeber/mehr?documentId=27112994",True),
+ TreeNode("8.6","Krimi",self.rootLink+"/tv/Krimi/mehr?documentId=27258656",True),
+ TreeNode("8.7","Reise",self.rootLink+"/tv/Reise/mehr?documentId=29769608",True),
+ )),
+ )
+ self.configLink = self.rootLink+"/play/media/%s?devicetype=pc&feature=flash"
+ #.*Video\?bcastId=\d+&amp;documentId=(\d+)\" class=\"textLink\">\s+?<p class=\"dachzeile\">(.*?)</p>\s+?<h4 class=\"headline\">(.*?)</h4>
+ self.regex_VideoPageLink = re.compile("<a href=\".*Video\?.*?documentId=(\d+).*?\" class=\"textLink\">\s+?<p class=\"dachzeile\">(.*?)<\/p>\s+?<h4 class=\"headline\">(.*?)<\/h4>\s+?<p class=\"subtitle\">(?:(\d+.\d+.\d+) \| )?(\d*) Min.")
+ self.regex_CategoryPageLink = re.compile("<a href=\"(.*(?:Sendung|Thema)\?.*?documentId=\d+.*?)\" class=\"textLink\">(?:.|\n)+?<h4 class=\"headline\">(.*?)<\/h4>")
+ self.pageSelectString = "&mcontent%s=page.%s"
+ self.regex_DetermineSelectedPage = re.compile("&mcontents{0,1}=page.(\d+)");
+
+ self.regex_videoLinks = re.compile("\"_quality\":(\d).*?\"_stream\":\[?\"(.*?)\"");
+ self.regex_pictureLink = re.compile("_previewImage\":\"(.*?)\"");
+
+
+ self.regex_Date = re.compile("\\d{2}\\.\\d{2}\\.\\d{2}");
+
+
+ self.replace_html = re.compile("<.*?>");
+
+ @classmethod
+ def name(self):
+ return "ARD";
+ def isSearchable(self):
+ return False;
+
+ def buildPageMenu(self, link, initCount, subLink = False):
+ self.gui.log("Build Page Menu: %s SubLink: %d"%(link,subLink));
+ mainPage = self.loadPage(link);
+
+ elementCount = 0;
+
+ elementCount = self.extractElements(mainPage);
+
+
+ self.generateNextPageElement(link, elementCount);
+ return elementCount;
+ def generateNextPageElement(self, link, elementCount):
+ marker = "";
+ if("Sendung?documentId" in link):
+ marker = "s";
+
+ numberElement = self.regex_DetermineSelectedPage.search(link);
+ if(numberElement is not None):
+ oldNumber = int(numberElement.group(1));
+ newNumber = oldNumber + 1;
+ link = link.replace(self.pageSelectString%(marker,oldNumber),self.pageSelectString%(marker,newNumber));
+
+ self.gui.buildVideoLink(DisplayObject("Weiter","","","",link,False),self,elementCount);
+ else:
+ link += self.pageSelectString%(marker,2)
+
+ self.gui.buildVideoLink(DisplayObject("Weiter","","","",link,False),self,elementCount);
+
+ def extractElements(self,mainPage):
+ videoElements = list(self.regex_VideoPageLink.finditer(mainPage));
+ if len(videoElements) == 0:
+ linkElements = list(self.regex_CategoryPageLink.finditer(mainPage));
+ else:
+ linkElements = []
+
+ counter = len(videoElements) + len(linkElements);
+ for element in linkElements:
+ link = self.rootLink+element.group(1);
+ title = element.group(2).decode('utf-8');
+ # subTitle = element.group(3).decode('utf-8');
+ subTitle = ""
+ self.gui.buildVideoLink(DisplayObject(title,subTitle,"","",link,False),self,counter);
+ for element in videoElements:
+ videoId = element.group(1);
+ title = element.group(2).decode('utf-8');
+ subTitle = element.group(3).decode('utf-8');
+ if element.group(4):
+ datestring = element.group(4).decode('utf-8');
+ date = datetime.date(*[int(x) for x in datestring.split('.')[::-1]]).timetuple()
+ else:
+ date = None
+ durationstring = element.group(5).decode('utf-8');
+ duration = int(durationstring) * 60;
+ self.decodeVideoInformation(videoId, title, subTitle, counter, date, duration);
+ return counter;
+
+ def decodeVideoInformation(self, videoId, title, subTitle, nodeCount, date, duration):
+ link = self.configLink%videoId;
+ self.gui.log("VideoLink: "+link);
+ videoPage = self.loadPage(link);
+ videoLinks = {}
+ for match in self.regex_videoLinks.finditer(videoPage):
+ quality = int(match.group(1));
+ link = SimpleLink(match.group(2),0);
+
+ if(quality > 0):
+ quality -= 1
+ videoLinks[quality] = link
+ match = self.regex_pictureLink.search(videoPage)
+ picture = None
+ if(match is not None):
+ picture = match.group(1);
+ if(len(videoLinks)>0):
+ self.gui.buildVideoLink(DisplayObject(title, subTitle,picture,"",videoLinks,True,date,duration),self,nodeCount);
diff --git a/plugin.video.mediathek/mediathek/arte.py b/plugin.video.mediathek/mediathek/arte.py
new file mode 100644
index 0000000..e0a158d
--- /dev/null
+++ b/plugin.video.mediathek/mediathek/arte.py
@@ -0,0 +1,262 @@
+## -*- coding: utf-8 -*-
+#-------------LicenseHeader--------------
+# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters
+# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de]
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import re, traceback, urllib,json;
+from mediathek import *
+from xml.dom import minidom
+from xml.dom import Node;
+from bs4 import BeautifulSoup;
+
+regex_dateString = re.compile("\\d{1,2} ((\\w{3})|(\\d{2})) \\d{4}");
+month_replacements = {
+ "Jan":"01",
+ "Feb":"02",
+ "Mar":"03",
+ "Apr":"04",
+ "May":"05",
+ "Jun":"06",
+ "Jul":"07",
+ "Aug":"08",
+ "Sep":"09",
+ "Oct":"10",
+ "Nov":"11",
+ "Dec":"12",
+ };
+
+class ARTEMediathek(Mediathek):
+ @classmethod
+ def name(self):
+ return "ARTE";
+ @classmethod
+ def isSearchable(self):
+ return False;
+
+ def __init__(self, simpleXbmcGui):
+ self.gui = simpleXbmcGui;
+ self.rootLink = "http://www.arte.tv";
+ self.basePage = self.rootLink+"/guide/de/plus7";
+ self.jsonLink = "https://api.arte.tv/api/player/v1/config/de/%s"
+
+ self.menuTree = (
+ TreeNode("0","Arte+7","mainPage",True),
+ TreeNode("1","Sendungen von A-Z","showCluster",True),
+ TreeNode("2","Kategorien","showCategories",True),
+ );
+
+ self.selector_videoPages = "li.video > a";
+
+ self.regex_VideoPageLinksHTML = re.compile("href=[\"'](http:\\\\/\\\\/www\\.arte\\.tv\\\\/guide\\\\/de\\\\/\d{6}-\d{3}/.+?)[\"']");
+ self.regex_VideoPageLinksJSON = re.compile("\"url\":\"((http:\\\\/\\\\/www\\.arte\\.tv){0,1}\\\\/guide\\\\/de\\\\/\d{6}-\d{3}\\\\/.+?)\"");
+ self.regex_findVideoIds = re.compile("(\d{6}-\d{3})(-A)");
+ self.regex_JSONPageLink = re.compile("http://arte.tv/papi/tvguide/videos/stream/player/D/\d{6}-\d{3}.+?/ALL/ALL.json");
+ self.regex_JSON_VideoLink = re.compile("\"HTTP_MP4_.+?\":{.*?\"bitrate\":(\d+),.*?\"url\":\"(http://.*?.mp4)\".*?\"versionShortLibelle\":\"([a-zA-Z]{2})\".*?}");
+ self.regex_JSON_ImageLink = re.compile("\"IUR\":\"(http://.*?\\.arte\\.tv/papi/tvguide/images/.*?\\..{3})\"");
+ self.regex_JSON_Detail = re.compile("\"VDE\":\"(.*?)\"");
+ self.regex_JSON_Titel = re.compile("\"VTI\":\"(.*?)\"");
+
+ regexSourceString="%s=\"([\[{].*?[}\]])\"";
+ self.regex_cluster=re.compile(regexSourceString%"data-clusters");
+ self.regex_categories = re.compile(regexSourceString%"data-categoriesVideos");
+ self.regex_playlists = re.compile(regexSourceString%"data-highlightedPlaylists");
+
+ self.categories = {
+ "DailyMostViewed":re.compile(regexSourceString%"data-dailyMostViewedVideos"),
+ "Most Viewed": re.compile(regexSourceString%"data-mostViewedVideos"),
+ "ExpiringVideos":re.compile(regexSourceString%"data-nextExpiringVideos"),
+ }
+
+ self.regex_extractVideoSources = (
+ re.compile(regexSourceString%"data-highlightedVideos"),
+ re.compile(regexSourceString%"data-latestVideos"),
+ re.compile(regexSourceString%"data-categoryVideoSet"),
+ );
+
+
+
+
+ def buildPageMenu(self, link, initCount):
+ if(link == "showCluster"):
+ self.showCluster();
+ elif (link == "mainPage"):
+ self.showMainPage();
+ elif (link == "showCategories"):
+ self.showCategories();
+ else:
+ if(not link.startswith("http")):
+ link = self.rootLink+link;
+ pageContent = self.loadPage(link).decode('UTF-8');
+ linkFound = self.extractVideoLinksFromHtml(pageContent)
+ if(not linkFound):
+ self.extractVideoLinks(pageContent,0);
+
+ def buildJsonMenu(self, path,callhash, initCount):
+ jsonContent=self.gui.loadJsonFile(callhash);
+ if(path == "init"):
+ jsonObject = jsonContent;
+ else:
+ jsonObject=self.walkJson(path,jsonContent);
+ self.gui.log(json.dumps(jsonObject));
+ if("videos" in jsonObject):
+ self.extractVideoLinksFromJson(jsonObject);
+ if(isinstance(jsonObject,list)):
+ for counter,jsonObject in enumerate(jsonObject):
+ if("day" in jsonObject):
+ self.gui.buildJsonLink(self,jsonObject["day"],"%d"%counter,callhash,0)
+
+ def buildJsonLink(self,name,content):
+ content = BeautifulSoup(content);
+ jsonContent = json.loads(content.prettify(formatter=None))
+ callhash = self.gui.storeJsonFile(jsonContent,name);
+ self.gui.buildJsonLink(self,name,"init",callhash,0)
+
+ def showMainPage(self):
+ self.gui.log("buildPageMenu: "+self.basePage);
+ pageContent = self.loadPage(self.basePage).decode('UTF-8');
+
+ for name,regex in self.categories.iteritems():
+ match = regex.search(pageContent);
+ if(match is not None):
+ self.buildJsonLink(name,match.group(1));
+
+ self.extractVideoLinksFromHtml(pageContent)
+
+ def extractVideoLinksFromHtml(self, htmlPage):
+ someMatch = False;
+ for regex in self.regex_extractVideoSources:
+ match = regex.search(htmlPage);
+ if(match is not None):
+ someMatch = True;
+ content = BeautifulSoup(match.group(1));
+ jsonContent = json.loads(content.prettify(formatter=None))
+ self.extractVideoLinksFromJson(jsonContent)
+ return someMatch;
+
+
+ def extractVideoLinksFromJson(self,jsonContent):
+ for jsonObject in jsonContent["videos"]:
+ self.buildVideoEntry(jsonObject);
+
+ def showCluster(self):
+ pageContent = self.loadPage(self.basePage).decode('UTF-8');
+ content = BeautifulSoup(self.regex_cluster.search(pageContent).group(1));
+ jsonContent = json.loads(content.prettify(formatter=None))
+ for menuItem in jsonContent:
+ self.buildMenuEntry(menuItem);
+
+ def showCategories(self):
+ pageContent = self.loadPage(self.basePage).decode('UTF-8');
+ content = BeautifulSoup(self.regex_categories.search(pageContent).group(1));
+ jsonContent = json.loads(content.prettify(formatter=None))
+ for jsonObject in jsonContent:
+ jsonCategorie = jsonObject["category"]
+ title = unicode(jsonCategorie["title"]);
+ link=jsonCategorie["url"];
+ self.gui.buildVideoLink(DisplayObject(title,"","","",link,False,None),self,0);
+
+ def buildMenuEntry(self, menuItem):
+ title = menuItem["title"];
+ subTitle = menuItem["subtitle"];
+ link=menuItem["permalink"];
+ self.gui.buildVideoLink(DisplayObject(title,subTitle,"","",link,False,None),self,0);
+
+ def buildVideoEntry(self, jsonObject):
+ title = jsonObject["title"];
+ subTitle = jsonObject["subtitle"];
+ detail = jsonObject["teaser"];
+ picture = None;
+ for pictureItem in jsonObject["thumbnails"]:
+ if(picture is None or picture["width"]<pictureItem["width"]):
+ picture = pictureItem;
+ link=self.jsonLink%jsonObject["id"];
+ self.gui.buildVideoLink(DisplayObject(title,"",picture["url"],detail,link,"JsonLink", None),self,0);
+
+
+ def playVideoFromJsonLink(self,link):
+ jsonObject = json.loads(self.loadPage(link));
+ links = self.extractLinks(jsonObject["videoJsonPlayer"]);
+ self.gui.play(links);
+
+
+ def extractLinks(self,jsonObject):
+ links={};
+
+ vsrObjects = jsonObject["VSR"];
+ if(not isinstance(vsrObjects,dict)):
+ return links;
+
+ for videoObject in jsonObject["VSR"].itervalues():
+ if videoObject["mediaType"] == "mp4":
+ url = videoObject["url"];
+ quality = videoObject["quality"];
+ self.gui.log("%s %s"%(quality,url))
+ if quality == "MQ":
+ links[0] = SimpleLink(url, -1);
+ if quality == "HQ":
+ links[1] = SimpleLink(url, -1);
+ if quality == "EQ":
+ links[2] = SimpleLink(url, -1);
+ if quality == "SQ":
+ links[3] = SimpleLink(url, -1);
+ return links;
+
+ def extractVideoLinks(self, htmlPage, initCount):
+ links = set();
+ jsonLinks = set();
+ for videoPageLink in self.regex_findVideoIds.finditer(htmlPage):
+ link = self.jsonLink%videoPageLink.group(1)
+ self.gui.log(link);
+ if(link not in jsonLinks):
+ jsonLinks.add(link);
+
+ for videoPageLink in self.regex_VideoPageLinksJSON.finditer(htmlPage):
+ link = videoPageLink.group(1).replace("\\/","/");
+ if(link not in links):
+ links.add(link);
+ for link in self.regex_JSONPageLink.finditer(htmlPage):
+ jsonLinks.add(link.group(0));
+ linkCount = initCount + len(links);
+ for link in links:
+ if(not link.startswith(self.rootLink)):
+ videoPage = self.loadPage(self.rootLink+link);
+ else:
+ videoPage = self.loadPage(link);
+ match = self.regex_JSONPageLink.search(videoPage);
+ if(match is not None):
+ jsonLinks.add(match.group(0));
+
+ linkCount = linkCount + len(jsonLinks);
+
+ self.gui.log("Found %s unique links"%len(jsonLinks));
+ for link in jsonLinks:
+ jsonPage = json.loads(self.loadPage(link));
+ self.extractVideoLinksFromJSONPage(jsonPage["videoJsonPlayer"],linkCount)
+
+ def extractVideoLinksFromJSONPage(self, jsonPage, linkCount):
+
+ videoLinks = self.extractLinks (jsonPage);
+ if(len(videoLinks) == 0):
+ return;
+
+ picture = jsonPage["VTU"]["IUR"];
+ title = jsonPage["VTI"];
+
+ subTitle = ""
+ if("subtitle" in jsonPage):
+ subTitle = jsonPage["subtitle"];
+
+
+ self.gui.buildVideoLink(DisplayObject(title,subTitle,picture,"",videoLinks,True, None),self,linkCount);
diff --git a/plugin.video.mediathek/mediathek/dreisat.py b/plugin.video.mediathek/mediathek/dreisat.py
new file mode 100644
index 0000000..615f88d
--- /dev/null
+++ b/plugin.video.mediathek/mediathek/dreisat.py
@@ -0,0 +1,177 @@
+# -*- coding: utf-8 -*-
+#-------------LicenseHeader--------------
+# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters
+# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de]
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import re,time
+from mediathek import *
+from xml.dom import minidom;
+
+
+regex_dateString = re.compile("\\d{2} ((\\w{3})|(\\d{2})) \\d{4}");
+month_replacements = {
+ "Jan":"01",
+ "Feb":"02",
+ "Mar":"03",
+ "Apr":"04",
+ "May":"05",
+ "Jun":"06",
+ "Jul":"07",
+ "Aug":"08",
+ "Sep":"09",
+ "Oct":"10",
+ "Nov":"11",
+ "Dec":"12",
+ };
+
+class DreiSatMediathek(Mediathek):
+ @classmethod
+ def name(self):
+ return "3Sat";
+ def isSearchable(self):
+ return True;
+ def __init__(self, simpleXbmcGui):
+ self.gui = simpleXbmcGui;
+ if(self.gui.preferedStreamTyp == 0):
+ self.baseType = "video/x-ms-asf";
+ elif (self.gui.preferedStreamTyp == 1):
+ self.baseType = "video/x-ms-asf"
+ elif (self.gui.preferedStreamTyp == 2):
+ self.baseType ="video/x-ms-asf";
+ else:
+ self.baseType ="video/quicktime";
+ self.webEmType = "video/webm";
+ self.menuTree = (
+ TreeNode("0","Bauerfeind","http://www.3sat.de/mediathek/rss/mediathek_bauerfeind.xml",True),
+ TreeNode("1","Bookmark","http://www.3sat.de/mediathek/rss/mediathek_bookmark.xml",True),
+ TreeNode("2",u"Börse","http://www.3sat.de/mediathek/rss/mediathek_boerse.xml",True),
+ TreeNode("3","Buchzeit","http://www.3sat.de/mediathek/rss/mediathek_buchzeit.xml",True),
+ TreeNode("4","daVinci","http://www.3sat.de/mediathek/rss/mediathek_davinci.xml",True),
+ TreeNode("5","delta","http://www.3sat.de/mediathek/rss/mediathek_delta.xml",True),
+ TreeNode("6","Film","http://www.3sat.de/mediathek/rss/mediathek_film.xml",True),
+ TreeNode("7","Gero von Boehm","http://www.3sat.de/mediathek/rss/mediathek_gero.xml",True),
+ TreeNode("8","hessenreporter","http://www.3sat.de/mediathek/rss/mediathek_hessenreporter.xml",True),
+ TreeNode("9","hitec","http://www.3sat.de/mediathek/rss/mediathek_hitec.xml",True),
+ TreeNode("10","Kabarett","http://www.3sat.de/mediathek/rss/mediathek_kabarett.xml",True),
+ TreeNode("11","Kinomagazin","http://www.3sat.de/mediathek/rss/mediathek_kinomag.xml",True),
+ TreeNode("12","Kulturzeit","http://www.3sat.de/mediathek/rss/mediathek_Kulturzeit.xml",True),
+ TreeNode("13","makro","http://www.3sat.de/mediathek/rss/mediathek_makro.xml",True),
+ TreeNode("14","Musik","http://www.3sat.de/mediathek/rss/mediathek_musik.xml",True),
+ TreeNode("15","nano","http://www.3sat.de/mediathek/rss/mediathek_nano.xml",True),
+ TreeNode("16","neues","http://www.3sat.de/mediathek/rss/mediathek_neues.xml",True),
+ TreeNode("17",u"Peter Voß fragt","http://www.3sat.de/mediathek/rss/mediathek_begegnungen.xml",True),
+ TreeNode("18","Recht brisant","http://www.3sat.de/mediathek/rss/mediathek_Recht%20brisant.xml",True),
+ TreeNode("19","scobel","http://www.3sat.de/mediathek/rss/mediathek_scobel.xml",True),
+ TreeNode("20","SCHWEIZWEIT","http://www.3sat.de/mediathek/rss/mediathek_schweizweit.xml",True),
+ TreeNode("21","Theater","http://www.3sat.de/mediathek/rss/mediathek_theater.xml",True),
+ TreeNode("22","vivo","http://www.3sat.de/mediathek/rss/mediathek_vivo.xml",True),
+ );
+
+ self.rootLink = "http://www.3sat.de"
+ self.searchLink = 'http://www.3sat.de/mediathek/mediathek';
+ link = "/mediathek/mediathek.php\\?obj=\\d+";
+ self.regex_searchResult = re.compile("href=\""+link+"\" class=\"media_result_thumb\"");
+ self.regex_searchResultLink = re.compile(link)
+ self.regex_searchLink = re.compile("http://(w|f)streaming.zdf.de/.*?(\\.asx|\\.smil)")
+ self.regex_searchTitle = re.compile("<h2>.*</h2>");
+ self.regex_searchDetail = re.compile("<span class=\"text\">.*");
+ self.regex_searchDate = re.compile("\\d{2}.\\d{2}.\\d{4}");
+ self.regex_searchImage = re.compile("(/dynamic/mediathek/stills/|/mediaplayer/stills/)\\d*_big\\.jpg");
+ self.replace_html = re.compile("<.*?>");
+
+ def buildPageMenu(self, link, initCount):
+ self.gui.log("buildPageMenu: "+link);
+ rssFeed = self.loadConfigXml(link);
+ self.extractVideoObjects(rssFeed, initCount);
+ def searchVideo(self, searchText):
+ values ={'mode':'search',
+ 'query':searchText,
+ 'red': '',
+ 'query_time': '',
+ 'query_sort': '',
+ 'query_order':''
+ }
+ mainPage = self.loadPage(self.searchLink,values);
+ results = self.regex_searchResult.findall(mainPage);
+ for result in results:
+ objectLink = self.regex_searchResultLink.search(result).group();
+ infoLink = self.rootLink+objectLink
+ infoPage = self.loadPage(infoLink);
+ title = self.regex_searchTitle.search(infoPage).group();
+ detail = self.regex_searchDetail.search(infoPage).group();
+ image = self.regex_searchImage.search(infoPage).group();
+ title = self.replace_html.sub("", title);
+ detail = self.replace_html.sub("", detail);
+ try:
+ dateString = self.regex_searchDate.search(infoPage).group();
+ pubDate = time.strptime(dateString,"%d.%m.%Y");
+ except:
+ pubDate = time.gmtime();
+ videoLink = self.rootLink+objectLink+"&mode=play";
+ videoPage = self.loadPage(videoLink);
+ video = self.regex_searchLink.search(videoPage).group();
+ video = video.replace("fstreaming","wstreaming").replace(".smil",".asx");
+ links = {}
+ links[2] = SimpleLink(video,0)
+ self.gui.buildVideoLink(DisplayObject(title,"",self.rootLink + image,detail,links,True, pubDate),self,len(results));
+ def readText(self,node,textNode):
+ try:
+ node = node.getElementsByTagName(textNode)[0].firstChild;
+ return unicode(node.data);
+ except:
+ return "";
+ def loadConfigXml(self, link):
+ self.gui.log("load:"+link)
+ xmlPage = self.loadPage(link);
+ return minidom.parseString(xmlPage);
+ def extractVideoObjects(self, rssFeed, initCount):
+ nodes = rssFeed.getElementsByTagName("item");
+ nodeCount = initCount + len(nodes)
+ for itemNode in nodes:
+ try:
+ self.extractVideoInformation(itemNode,nodeCount);
+ except:
+ pass
+
+ def parseDate(self,dateString):
+ dateString = regex_dateString.search(dateString).group();
+ for month in month_replacements.keys():
+ dateString = dateString.replace(month,month_replacements[month]);
+ return time.strptime(dateString,"%d %m %Y");
+
+ def extractVideoInformation(self, itemNode, nodeCount):
+ title = self.readText(itemNode,"title");
+ self.gui.log(title)
+ dateString = self.readText(itemNode,"pubDate");
+ pubDate = self.parseDate(dateString);
+ descriptionNode = itemNode.getElementsByTagName("description")[0].firstChild.data;
+ description = unicode(descriptionNode);
+ picture = "";
+ pictureNodes = itemNode.getElementsByTagName("media:thumbnail");
+ if(len(pictureNodes) > 0):
+ picture = pictureNodes[0].getAttribute("url");
+ links = {};
+ for contentNode in itemNode.getElementsByTagName("media:content"):
+ height = int(contentNode.getAttribute("height"));
+ url = contentNode.getAttribute("url");
+ size = int(contentNode.getAttribute("fileSize"));
+ if(height < 300):
+ links[0] = SimpleLink(url, size);
+ elif (height < 480):
+ links[1] = SimpleLink(url, size);
+ else:
+ links[2] = SimpleLink(url, size);
+ if links:
+ self.gui.buildVideoLink(DisplayObject(title,"",picture,description,links,True, pubDate),self,nodeCount);
diff --git a/plugin.video.mediathek/mediathek/factory.py b/plugin.video.mediathek/mediathek/factory.py
new file mode 100644
index 0000000..7bcdb96
--- /dev/null
+++ b/plugin.video.mediathek/mediathek/factory.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+#-------------LicenseHeader--------------
+# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters
+# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de]
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from mediathek.wdr import *
+from mediathek.ard import *
+from mediathek.zdf import *
+from mediathek.arte import *
+from mediathek.dreisat import *
+from mediathek.orf import *
+from mediathek.ndr import *
+from mediathek.kika import *
+
+class MediathekFactory(object):
+ def __init__(self):
+ self.avaibleMediathekes = {
+ ARDMediathek.name():ARDMediathek,
+ ZDFMediathek.name():ZDFMediathek,
+ ARTEMediathek.name():ARTEMediathek,
+ DreiSatMediathek.name():DreiSatMediathek,
+ ORFMediathek.name():ORFMediathek,
+ NDRMediathek.name():NDRMediathek,
+ KIKA.name():KIKA
+ }
+ def getAvaibleMediathekTypes(self):
+ return sorted(self.avaibleMediathekes.keys())
+
+ def getMediathek(self,mediathekName, gui):
+ return self.avaibleMediathekes[mediathekName](gui);
diff --git a/plugin.video.mediathek/mediathek/kika.py b/plugin.video.mediathek/mediathek/kika.py
new file mode 100644
index 0000000..238ba9d
--- /dev/null
+++ b/plugin.video.mediathek/mediathek/kika.py
@@ -0,0 +1,140 @@
+# -*- coding: utf-8 -*-
+#-------------LicenseHeader--------------
+# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters
+# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de]
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import re, time;
+from bs4 import BeautifulSoup;
+from mediathek import *
+
+class KIKA(Mediathek):
+ def __init__(self, simpleXbmcGui):
+ self.gui = simpleXbmcGui;
+ self.rootLink = "http://www.kika.de";
+ self.menuTree = (
+ TreeNode("0","Videos",self.rootLink+"/videos/index.html",True),
+ TreeNode("1","Sendungen von A-Z","",False,
+ (
+ TreeNode("1.0","A",self.rootLink+"/sendungen/sendungenabisz100_page-A_zc-05fb1331.html",True),
+ TreeNode("1.1","B",self.rootLink+"/sendungen/sendungenabisz100_page-B_zc-1775e6d8.html",True),
+ TreeNode("1.2","C",self.rootLink+"/sendungen/sendungenabisz100_page-C_zc-6248eba0.html",True),
+ TreeNode("1.3","D",self.rootLink+"/sendungen/sendungenabisz100_page-D_zc-e090a8fb.html",True),
+ TreeNode("1.4","E",self.rootLink+"/sendungen/sendungenabisz100_page-E_zc-ec2376ed.html",True),
+ TreeNode("1.5","F",self.rootLink+"/sendungen/sendungenabisz100_page-F_zc-f76734a0.html",True),
+ TreeNode("1.6","G",self.rootLink+"/sendungen/sendungenabisz100_page-G_zc-34bda7c3.html",True),
+ TreeNode("1.7","H",self.rootLink+"/sendungen/sendungenabisz100_page-H_zc-7e25e70a.html",True),
+ TreeNode("1.8","I",self.rootLink+"/sendungen/sendungenabisz100_page-I_zc-b7f774f5.html",True),
+ TreeNode("1.9","J",self.rootLink+"/sendungen/sendungenabisz100_page-J_zc-3130680a.html",True),
+ TreeNode("1.10","K",self.rootLink+"/sendungen/sendungenabisz100_page-K_zc-c8f76ba1.html",True),
+ TreeNode("1.11","L",self.rootLink+"/sendungen/sendungenabisz100_page-L_zc-bbebc1a7.html",True),
+ TreeNode("1.12","M",self.rootLink+"/sendungen/sendungenabisz100_page-M_zc-00574a43.html",True),
+ TreeNode("1.13","N",self.rootLink+"/sendungen/sendungenabisz100_page-N_zc-b079366f.html",True),
+ TreeNode("1.14","O",self.rootLink+"/sendungen/sendungenabisz100_page-O_zc-febc55f5.html",True),
+ TreeNode("1.15","P",self.rootLink+"/sendungen/sendungenabisz100_page-P_zc-2c1a492f.html",True),
+ TreeNode("1.16","Q",self.rootLink+"/sendungen/sendungenabisz100_page-Q_zc-2cb019d6.html",True),
+ TreeNode("1.17","R",self.rootLink+"/sendungen/sendungenabisz100_page-R_zc-cab3e22b.html",True),
+ TreeNode("1.18","S",self.rootLink+"/sendungen/sendungenabisz100_page-S_zc-e7f420d0.html",True),
+ TreeNode("1.19","T",self.rootLink+"/sendungen/sendungenabisz100_page-T_zc-84a2709f.html",True),
+ TreeNode("1.20","U",self.rootLink+"/sendungen/sendungenabisz100_page-U_zc-a26c1157.html",True),
+ TreeNode("1.21","V",self.rootLink+"/sendungen/sendungenabisz100_page-V_zc-1fc26dc3.html",True),
+ TreeNode("1.22","W",self.rootLink+"/sendungen/sendungenabisz100_page-W_zc-25c5c777.html",True),
+ TreeNode("1.23","Y",self.rootLink+"/sendungen/sendungenabisz100_page-Y_zc-388beba7.html",True),
+ TreeNode("1.24","Z",self.rootLink+"/sendungen/sendungenabisz100_page-Z_zc-e744950d.html",True),
+ TreeNode("1.25","...",self.rootLink+"/sendungen/sendungenabisz100_page-1_zc-43c28d56.html",True)
+ )
+ )
+ )
+
+ self.regex_videoLinks=re.compile("<a href=\"(.*?/videos/video\\d+?)\\.html\"");
+ self.regex_configLinks=re.compile("{dataURL:'http://www.kika.de(/.*?-avCustom.xml)'}");
+ self.selector_videoPages = "div.mod > div.box > div.teaser > a.linkAll";
+ self.selector_seriesPages = "div.modCon > div.mod > div.boxCon > div.boxBroadcastSeries > div.teaser > a.linkAll";
+ self.regex_xml_channel=re.compile("<channelName>(.*?)</channelName>");
+ self.regex_xml_title=re.compile("<title>(.*?)</title>");
+ self.regex_xml_image=re.compile("<teaserimage>\\s*?<url>(.*?)</url>");
+ self.regex_xml_videoLink=re.compile("<asset>\\s*?<profileName>(.*?)</profileName>.*?<progressiveDownloadUrl>(.*?)</progressiveDownloadUrl>\\s*?</asset>",re.DOTALL)
+ self.regex_videoLink=re.compile("rtmp://.*?\.mp4");
+ @classmethod
+ def name(self):
+ return "KI.KA";
+
+ def isSearchable(self):
+ return False;
+
+ def searchVideo(self, searchText):
+ return;
+
+ def buildVideoLink(self,pageLink):
+ xmlPage = self.loadPage(self.rootLink+pageLink);
+ channel = self.regex_xml_channel.search(xmlPage);
+ if(channel is not None):
+ channel = unicode(channel.group(1),"UTF-8");
+ title = unicode(self.regex_xml_title.search(xmlPage).group(1),"UTF-8");
+ image = self.regex_xml_image.search(xmlPage).group(1).replace("**aspectRatio**","tlarge169").replace("**width**","1472");
+
+ self.gui.log("%s %s"%(title,image));
+ links = {};
+ for match in self.regex_xml_videoLink.finditer(xmlPage):
+ profile = match.group(1);
+ directLink = match.group(2);
+ self.gui.log("%s %s"%(profile,directLink));
+ if("MP4 Web S" in profile):
+ links[0] = SimpleLink(directLink, 0);
+ if("MP4 Web L" in profile):
+ links[1] = SimpleLink(directLink, 0);
+ if("MP4 Web L+" in profile):
+ links[2] = SimpleLink(directLink, 0);
+ if("MP4 Web XL" in profile):
+ links[3] = SimpleLink(directLink, 0);
+
+ if(channel is not None):
+ return DisplayObject(channel,title,image,"",links,True, None);
+ else:
+ return DisplayObject(title,"",image,"",links,True, None);
+
+ def buildPageMenu(self, link, initCount):
+ pageContent = self.loadPage(link);
+ htmlPage = BeautifulSoup(pageContent, 'html.parser')
+ htmlElements = htmlPage.select(self.selector_videoPages)
+ videoLinks = set()
+
+ for item in htmlElements:
+ link = self.rootLink+item['href'];
+ videoPage = self.loadPage(link);
+ for match in self.regex_videoLinks.finditer(videoPage):
+ link=match.group(1)+"-avCustom.xml";
+ if(link not in videoLinks):
+ videoLinks.add(link)
+ directLinks = list(self.regex_configLinks.finditer(pageContent));
+ for match in directLinks:
+ link = match.group(1);
+ if(link not in videoLinks):
+ videoLinks.add(link)
+ self.gui.log("found %d video links"%len(videoLinks))
+ count = initCount + len(videoLinks)
+ for link in videoLinks:
+ displayObject = self.buildVideoLink(link);
+ self.gui.buildVideoLink(displayObject,self, count);
+ if(len(videoLinks) > 0):
+ return;
+ htmlElements = htmlPage.select(self.selector_seriesPages);
+ count = count + len(htmlElements)
+ self.gui.log("found %d page links"%len(htmlElements))
+ for item in htmlElements:
+ self.gui.log(item.prettify());
+ link = self.rootLink+item['href'];
+ title = item['title'];
+ displayObject = DisplayObject(title,"",None,"",link,False, None);
+ self.gui.buildVideoLink(displayObject,self, count);
diff --git a/plugin.video.mediathek/mediathek/ndr.py b/plugin.video.mediathek/mediathek/ndr.py
new file mode 100644
index 0000000..cbe57df
--- /dev/null
+++ b/plugin.video.mediathek/mediathek/ndr.py
@@ -0,0 +1,274 @@
+# -*- coding: utf-8 -*-
+# -------------LicenseHeader--------------
+# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters
+# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de]
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import re
+import datetime
+import time
+import calendar
+from mediathek import *
+from xml.dom import minidom
+
+
+class NDRMediathek(Mediathek):
+ @classmethod
+ def name(self):
+ return "NDR"
+
+ def isSearchable(self):
+ return True
+
+ def __init__(self, simpleXbmcGui):
+ self.gui = simpleXbmcGui
+
+ self.rootLink = "http://www.ndr.de"
+
+ self.searchLink = self.rootLink+"/suche10.html?search_mediathek=1&"
+
+ # Hauptmenue
+ tmp_menu = []
+ extractBroadcasts = re.compile("<a href=\"/mediathek/mediatheksuche105_broadcast-(.*?).html\" class=\"link_arrow\">(.*?)</a>")
+ htmlPage = self.loadPage("http://www.ndr.de/mediathek/sendungen_a-z/index.html").decode('utf-8')
+
+ x = 0
+ for menuNode in extractBroadcasts.finditer(htmlPage):
+ menuId = menuNode.group(1)
+ menuItem = menuNode.group(2)
+ menuLink = self.rootLink+"/mediatheksuche105_broadcast-"+menuId+"_format-video_page-1.html"
+ tmp_menu.append(TreeNode("0."+str(x), menuItem, menuLink, True))
+ x = x+1
+
+ self.menuTree = [
+ TreeNode("0", "Sendungen von A-Z", "", False, tmp_menu),
+ TreeNode("1", "Sendung verpasst?", "sendungverpasst", True),
+ TreeNode("2","Live","livestream",True),#Livestream ruckelt zu stark :-(
+ ]
+
+ def buildPageMenuSendungVerpasst(self, action):
+ htmlPage = self.loadPage("http://www.ndr.de/mediathek/sendung_verpasst/epg1490_display-onlyvideo.html")
+
+ regex_verpasstNow = re.compile(
+ '<h1 class="viewdate">\n.*?(\\d{2})\.(\\d{2})<span class="notbelow30em">\.(\\d{4})</span>'
+ )
+ verpasstNow = ".".join(regex_verpasstNow.search(htmlPage).groups())
+ try:
+ dateTimeTmp = datetime.datetime.strptime(verpasstNow, "%d.%m.%Y")
+ except TypeError:
+ dateTimeTmp = datetime.datetime(*(time.strptime(verpasstNow, "%d.%m.%Y")[0:6]))
+
+ nodeCount = 0
+
+ if action == "":
+ verpasstHeute = dateTimeTmp.strftime("%Y-%m-%d")
+ dateTimeTmp = dateTimeTmp-datetime.timedelta(1)
+ verpasstGestern = dateTimeTmp.strftime("%Y-%m-%d")
+ dateTimeTmp = dateTimeTmp-datetime.timedelta(1)
+ verpasstVorGestern = dateTimeTmp.strftime("%Y-%m-%d")
+
+ self.gui.buildVideoLink(DisplayObject("Heute", "", "", "description", self.rootLink + "/mediathek/sendung_verpasst/epg1490_date-" + verpasstHeute + "_display-onlyvideo.html", False), self, 1)
+ self.gui.buildVideoLink(DisplayObject("Gestern", "", "", "description", self.rootLink + "/mediathek/sendung_verpasst/epg1490_date-" + verpasstGestern + "_display-onlyvideo.html", False), self, 2)
+ self.gui.buildVideoLink(DisplayObject("Vorgestern", "", "", "description", self.rootLink + "/mediathek/sendung_verpasst/epg1490_date-" + verpasstVorGestern + "_display-onlyvideo.html", False), self, 3)
+ self.gui.buildVideoLink(DisplayObject("Datum waehlen", "", "", "description", "sendungverpasstselect", False), self, 4)
+ elif action == "select":
+ dateTimeTmp = dateTimeTmp-datetime.timedelta(3)
+ verpasstStartYear = int(dateTimeTmp.strftime("%Y"))
+ for verpasstStart in reversed(range(1, int(dateTimeTmp.strftime("%m")))):
+ menu_title = str(verpasstStart)+"."+str(verpasstStartYear)
+ menu_action = "sendungverpasstselectmonth" + str(verpasstStartYear) + str(verpasstStart)
+ self.gui.buildVideoLink(DisplayObject(menu_title, "", "", "description", menu_action, False), self, nodeCount)
+ verpasstStart = verpasstStart - 1
+ nodeCount = nodeCount + 1
+
+ while verpasstStartYear > 2008:
+ verpasstStartYear = verpasstStartYear - 1
+ menu_title = str(verpasstStartYear)
+ self.gui.buildVideoLink(DisplayObject(menu_title, "", "", "description", "sendungverpasstselectyear" + str(verpasstStartYear), False), self, nodeCount)
+ elif action[0:11] == "selectmonth":
+ action = action[11:]
+ action_year = action[0:4]
+ action_month = action[4:]
+
+ try:
+ dateTimeTmp2 = datetime.datetime.strptime(action_year+action_month, "%Y%m")
+ except TypeError:
+ dateTimeTmp2 = datetime.datetime(*(time.strptime(action_year+action_month, "%Y%m")[0:6]))
+
+ if dateTimeTmp.strftime("%Y%m") == dateTimeTmp2.strftime("%Y%m"):
+ startDay = int(dateTimeTmp2.strftime("%d"))
+ else:
+ startDay = calendar.monthrange(int(action_year), int(action_month))[1]
+
+ try:
+ dateTimeTmp2 = datetime.datetime.strptime(action_year + action_month + str(startDay), "%Y%m%d")
+ except TypeError:
+ dateTimeTmp2 = datetime.datetime(*(time.strptime(action_year + action_month + str(startDay), "%Y%m%d")[0:6]))
+
+ for i in reversed(range(1, startDay)):
+ verpasstDatum = dateTimeTmp2.strftime("%Y-%m-%d")
+ menu_title = dateTimeTmp2.strftime("%d.%m.%Y")
+ menu_action = self.rootLink + "/mediathek/sendung_verpasst/epg1490_date-" + verpasstDatum + "_display-onlyvideo.html"
+ self.gui.buildVideoLink(DisplayObject(menu_title, "", "", "description", menu_action, False), self, nodeCount)
+ nodeCount = nodeCount + 1
+ dateTimeTmp2 = dateTimeTmp2-datetime.timedelta(1)
+ elif action[0:10] == "selectyear":
+ action = action[10:]
+ action_year = action[0:4]
+ for startMonth in reversed(range(1, 12)):
+ menu_title = str(startMonth) + "." + action_year
+ menu_action = "sendungverpasstselectmonth" + action_year + str(startMonth)
+ self.gui.buildVideoLink(DisplayObject(menu_title, "", "", "description", menu_action, False), self, nodeCount)
+ nodeCount = nodeCount + 1
+
+ def buildPageMenuLivestream(self):
+ nodeCount = 0
+
+ # Hamburg
+ nodeCount = nodeCount+1
+ links = {}
+ links[0] = SimpleLink("http://ndr_fs-lh.akamaihd.net/i/ndrfs_hh@119223/master.m3u8", 0)
+ self.gui.buildVideoLink(DisplayObject("Hamburg", "", "", "", links, True), self, nodeCount)
+
+ # Mecklenburg-Vorpommern
+ nodeCount = nodeCount+1
+ links = {}
+ links[0] = SimpleLink("http://ndr_fs-lh.akamaihd.net/i/ndrfs_mv@119226/master.m3u8", 0)
+ self.gui.buildVideoLink(DisplayObject("Mecklenburg-Vorpommern", "", "", "", links, True), self, nodeCount)
+
+ # Niedersachsen
+ nodeCount = nodeCount+1
+ links = {}
+ links[0] = SimpleLink("http://ndr_fs-lh.akamaihd.net/i/ndrfs_nds@119224/master.m3u8", 0)
+ self.gui.buildVideoLink(DisplayObject("Niedersachsen", "", "", "", links, True), self, nodeCount)
+
+ # Schleswig-Holstein
+ nodeCount = nodeCount+1
+ links = {}
+ links[0] = SimpleLink("http://ndr_fs-lh.akamaihd.net/i/ndrfs_sh@119225/master.m3u8", 0)
+ self.gui.buildVideoLink(DisplayObject("Schleswig-Holstein", "", "", "", links, True), self, nodeCount)
+
+ def buildPageMenuVideoListVerpasst(self, link, initCount):
+ self.gui.log("buildPageMenuVerpasst: " + link)
+
+ htmlPage = self.loadPage(link)
+
+ re_video_item = re.compile(
+ '<div class="videolinks"><a href="(.*?)" title=".*?" class=\'button epgbutton\' >'
+ )
+ # won't parse "Beiträge", they are only cutted parts of the main
+ # program
+ video_items = re.findall(re_video_item, htmlPage)
+ nodeCount = initCount + len(video_items)
+ for video_link in video_items:
+ if not re.compile("http://www.n-joy.de/.*").search(video_link):
+ print video_link
+ self.extractVideoInformation(video_link, None, nodeCount)
+
+
+ def buildPageMenuVideoList(self, link, initCount):
+ self.gui.log("buildPageMenu: " + link)
+
+ htmlPage = self.loadPage(link)
+
+ regex_extractVideoItems = re.compile(
+ "<div class=\"teaserpadding\">"
+ "(.*?)"
+ "(</p>\n</div>\n</div>|\n</div>\n</div>\n</li>)", re.DOTALL)
+ regex_extractVideoItemHref = re.compile("<a title=\".*?\" href=\"(.*?/[^/]*?\.html)\".*?>", re.DOTALL)
+ regex_extractVideoItemDate = re.compile("<div class=\"subline\" style=\"cursor: pointer;\">.*?(\\d{2}\.\\d{2}\.\\d{4} \\d{2}:\\d{2})</div>")
+
+ videoItems = regex_extractVideoItems.findall(htmlPage)
+ nodeCount = initCount + len(videoItems)
+
+ for videoItem in videoItems:
+ print "link: {0}".format(link)
+ print "videoItem: {0}".format(videoItem[0])
+ if "<div class=\"subline\">" not in videoItem[0]:
+ continue
+ videoLink = regex_extractVideoItemHref.search(videoItem[0]).group(1)
+ try:
+ dateString = regex_extractVideoItemDate.search(videoItem[0]).group(1)
+ dateTime = time.strptime(dateString, "%d.%m.%Y %H:%M")
+ except:
+ dateTime = None
+ # TODO: Some videos from Extra 3 are located on http://www.n-joy.de/
+ # which cannot be parsed by this script, yet.
+ if not re.compile("http://www.n-joy.de/.*").search(videoLink):
+ self.extractVideoInformation(videoLink, dateTime, nodeCount)
+
+ # Pagination (weiter)
+ regex_extractNextPage = re.compile(
+ "<a class=\"square button\" href=\"(.*?)\" title=\"(.*?)\".*?>"
+ )
+ for nextPageHref in regex_extractNextPage.finditer(htmlPage):
+ menuItemName = nextPageHref.group(2).decode("UTF-8")
+ link = self.rootLink+nextPageHref.group(1)
+ self.gui.buildVideoLink(DisplayObject(menuItemName, "", "", "description", link, False), self, nodeCount)
+
+ def buildPageMenu(self, link, initCount):
+
+ print link
+ if link[0:15] == "sendungverpasst":
+ self.buildPageMenuSendungVerpasst(link[15:])
+ elif link == "livestream":
+ self.buildPageMenuLivestream()
+ elif "/sendung_verpasst/" in link:
+ self.buildPageMenuVideoListVerpasst(link, initCount)
+ else:
+ self.buildPageMenuVideoList(link, initCount)
+
+ def searchVideo(self, searchText):
+ searchText = searchText.encode("UTF-8")
+ searchText = urllib.urlencode({"query": searchText})
+ self.buildPageMenu(self.searchLink+searchText, 0)
+
+ def extractVideoInformation(self, videoLink, pubDate, nodeCount):
+
+ regexFindVideoLink = re.compile("http://.*(hq.mp4|hi.mp4|lo.flv)")
+ regexFindImageLink = re.compile("/.*v-ardgalerie.jpg")
+ regexFindMediaData = re.compile(
+ "<div class=\"padding group\">\\s*?<div class=\"textinfo\">\\s*?<h\\d.*?>"
+ "(.*?)"
+ "</h\\d>\\s*?.*?<div class=\"subline\">.*?</div>\\s*?<p.*?>"
+ "(.*?)"
+ "</p>", re.DOTALL
+ )
+ if not videoLink.startswith(self.rootLink):
+ videoLink = self.rootLink+videoLink
+ videoPage = self.loadPage(videoLink)
+
+ self.gui.log("videolink: {0}".format(videoLink))
+ videoLink = {}
+ videoLink[0] = SimpleLink(regexFindVideoLink.search(videoPage).group(0), 0)
+
+ try:
+ pictureLink = self.rootLink+regexFindImageLink.search(videoPage).group(0)
+ except:
+ pictureLink = None
+ if '<article class="w66 ">' not in videoPage:
+ searchResult = regexFindMediaData.search(videoPage)
+ title = searchResult.group(1).decode('utf-8')
+ description = searchResult.group(2).decode('utf-8')
+ else:
+ title = re.search(
+ 'var trackTitle = "(.*?)"',
+ videoPage
+ ).group(1).decode('utf-8')
+ description = re.search(
+ '<meta name="description" content="(.*?)"',
+ videoPage
+ ).group(1).decode('utf-8')
+
+ self.gui.buildVideoLink(DisplayObject(title, "", pictureLink, description, videoLink, True, pubDate, 0), self, nodeCount)
diff --git a/plugin.video.mediathek/mediathek/orf.py b/plugin.video.mediathek/mediathek/orf.py
new file mode 100644
index 0000000..1146abf
--- /dev/null
+++ b/plugin.video.mediathek/mediathek/orf.py
@@ -0,0 +1,162 @@
+# -*- coding: utf-8 -*-
+# -------------LicenseHeader--------------
+# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters
+# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de]
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import re,time,urllib
+from xml.dom import Node;
+from xml.dom import minidom;
+from mediathek import *
+
+class ORFMediathek(Mediathek):
+ def __init__(self, simpleXbmcGui):
+
+ self.rootLink = "http://tvthek.orf.at"
+ self.gui = simpleXbmcGui;
+ self.menuTree = [];
+ self.menuTree.append(TreeNode("0","Startseite","http://tvthek.orf.at/",True));
+
+ menuPage = self.loadPage(self.rootLink+"/programs");
+ findMenuLink = re.compile("<li><a href=\"(/programs/.*?)\" title=\".*?\">(.*?)</a></li>");
+ findCategorie = re.compile("<h4>(.*?)</h4>\\s*?<ul>((\\s*?%s\\s*?)+)</ul>"%findMenuLink.pattern)
+ categories = [];
+
+ for categorieMatch in findCategorie.finditer(menuPage):
+ title = categorieMatch.group(1);
+ items = [];
+ for menuMatch in findMenuLink.finditer(categorieMatch.group(2)):
+ items.append(TreeNode("1.%d.%d"%(len(categories),len(items)), menuMatch.group(2),"%s%s"%(self.rootLink,menuMatch.group(1)),True));
+ categories.append(TreeNode("1.%d"%len(categories), title,"",False,items));
+
+ self.menuTree.append(TreeNode("1","Sendungen","",False,categories));
+
+ videoLinkPage = "/programs/.*"
+ imageLink = "http://tvthek.orf.at/assets/.*?.jpeg"
+
+
+ self.regex_extractVideoPageLink = re.compile(videoLinkPage+"?\"");
+ self.regex_extractImageLink = re.compile(imageLink);
+ self.regex_extractTitle = re.compile("<strong>.*<span");
+ self.regex_extractVideoLink = re.compile("/programs/.*.asx");
+ self.regex_extractVideoObject = re.compile("<a href=\""+videoLinkPage+"\" title=\".*\">\\s*<span class=\"spcr\">\\s*<img src=\""+imageLink+"\" title=\".*\" alt=\".*\" />\\s*<span class=\".*\"></span>\\s*<strong>.*<span class=\"nowrap duration\">.*</span></strong>\\s*<span class=\"desc\">.*</span>\\s*</span>\\s*</a>");
+
+ self.regex_extractSearchObject = re.compile("<li class=\"clearfix\">\\s*<a href=\".*\" title=\".*\" class=\".*\"><img src=\".*\" alt=\".*\" /><span class=\"btn_play\">.*</span></a>\\s*<p>.*</p>\\s*<h4><a href=\".*\" title=\".*\">.*</a></h4>\\s*<p><a href=\".*\" title=\".*\"></a></p>\\s*</li>");
+
+ self.regex_extractProgrammLink = re.compile("/programs/.*?\"");
+ self.regex_extractProgrammTitle = re.compile("title=\".*?\"");
+ self.regex_extractProgrammPicture = re.compile("/binaries/asset/segments/\\d*/image1");
+
+ self.regex_extractFlashVars = re.compile("ORF.flashXML = '.*?'");
+ self.regex_extractHiddenDate = re.compile("\d{4}-\d{2}-\d{2}");
+ self.regex_extractXML = re.compile("%3C.*%3E");
+ self.regex_extractReferingSites = re.compile("<li><a href=\"/programs/\d+.*?/episodes/\d+.*?\"");
+
+ self.replace_html = re.compile("<.*?>");
+ self.searchLink = "http://tvthek.orf.at/search?q="
+ @classmethod
+ def name(self):
+ return "ORF";
+
+ def isSearchable(self):
+ return True;
+
+ def createVideoLink(self,title,image,videoPageLink,elementCount):
+ videoPage = self.loadPage(self.rootLink+videoPageLink);
+
+ videoLink = self.regex_extractVideoLink.search(videoPage);
+ if(videoLink == None):
+ return;
+
+ simpleLink = SimpleLink(self.rootLink+videoLink.group(), 0);
+ videoLink = {0:simpleLink};
+ counter = 0
+ playlist = self.loadPage(simpleLink.basePath);
+ for line in playlist:
+ counter+=1;
+
+ if(counter == 1):
+ self.gui.buildVideoLink(DisplayObject(title,"",image,"",videoLink, True, time.gmtime()),self,elementCount);
+ else:
+ self.gui.buildVideoLink(DisplayObject(title,"",image,"",videoLink, "PlayList", time.gmtime()),self,elementCount);
+
+ def searchVideo(self, searchText):
+ link = self.searchLink = "http://tvthek.orf.at/search?q="+searchText;
+ mainPage = self.loadPage(link);
+ result = self.regex_extractSearchObject.findall(mainPage);
+ for searchObject in result:
+ videoLink = self.regex_extractProgrammLink.search(searchObject).group().replace("\"","");
+ title = self.regex_extractProgrammTitle.search(searchObject).group().replace("title=\"","").replace("\"","");
+ title = title.decode("UTF-8");
+ pictureLink = self.regex_extractProgrammPicture.search(searchObject).group();
+
+ print videoLink;
+
+ self.createVideoLink(title,pictureLink,videoLink, len(result));
+
+ def extractLinksFromFlashXml(self, flashXml, date, elementCount):
+ print flashXml.toprettyxml().encode('UTF-8');
+ playlistNode = flashXml.getElementsByTagName("Playlist")[0];
+ linkNode=flashXml.getElementsByTagName("AsxUrl")[0];
+ link=linkNode.firstChild.data;
+ asxLink = SimpleLink(self.rootLink+link,0);
+ videoLink = {0:asxLink};
+ for videoItem in playlistNode.getElementsByTagName("Items")[0].childNodes:
+ if(videoItem.nodeType == Node.ELEMENT_NODE):
+ titleNode=videoItem.getElementsByTagName("Title")[0];
+
+ descriptionNode=videoItem.getElementsByTagName("Description")[0];
+ title=titleNode.firstChild.data;
+
+ stringArray = link.split("mp4:");
+
+ try:
+ description=descriptionNode.firstChild.data;
+ except:
+ description="";
+ self.gui.buildVideoLink(DisplayObject(title,"","",description,videoLink, True, date),self,elementCount);
+ def extractFlashLinks(self, flashVars,videoPageLinks,elementCount):
+ for flashVar in flashVars:
+ encodedXML = self.regex_extractXML.search(flashVar).group();
+ dateString = self.regex_extractHiddenDate.search(flashVar).group();
+ date = time.strptime(dateString,"%Y-%m-%d");
+ parsedXML = minidom.parseString(urllib.unquote(encodedXML));
+ self.extractLinksFromFlashXml(parsedXML, date,elementCount);
+ for videoPageLink in videoPageLinks:
+ videoPageLink = self.rootLink+videoPageLink.replace("<li><a href=\"","").replace("\"","");
+ print videoPageLink;
+ videoPage = self.loadPage(videoPageLink);
+ flashVars = self.regex_extractFlashVars.findall(videoPage);
+ for flashVar in flashVars:
+ encodedXML = self.regex_extractXML.search(flashVar).group();
+ dateString = self.regex_extractHiddenDate.search(flashVar).group();
+ date = time.strptime(dateString,"%Y-%m-%d");
+ parsedXML = minidom.parseString(urllib.unquote(encodedXML));
+ self.extractLinksFromFlashXml(parsedXML,date,elementCount);
+ def buildPageMenu(self, link, initCount):
+ mainPage = self.loadPage(link);
+ videoPageLinks = self.regex_extractReferingSites.findall(mainPage);
+ flashVars = self.regex_extractFlashVars.findall(mainPage);
+ links = self.regex_extractVideoObject.findall(mainPage);
+ elementCount = initCount + len(links)+len(flashVars)+len(videoPageLinks);
+ self.extractFlashLinks(flashVars,videoPageLinks,elementCount);
+ for linkObject in links:
+
+ videoLink = self.regex_extractVideoPageLink.search(linkObject).group().replace("\"","");
+ image = self.regex_extractImageLink.search(linkObject).group();
+ title = self.regex_extractTitle.search(linkObject).group().decode('UTF8');
+ title = self.replace_html.sub("", title);
+ title = title.replace(" <span","");
+ self.createVideoLink(title,image,videoLink, elementCount);
+
diff --git a/plugin.video.mediathek/mediathek/wdr.py b/plugin.video.mediathek/mediathek/wdr.py
new file mode 100644
index 0000000..5c784db
--- /dev/null
+++ b/plugin.video.mediathek/mediathek/wdr.py
@@ -0,0 +1,191 @@
+# -*- coding: utf-8 -*-
+#-------------LicenseHeader--------------
+# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters
+# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de]
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import re, time;
+from mediathek import *
+from xml.dom import minidom;
+regex_dateString = re.compile("\\d{4}-\\d{2}-\\d{2}");
+class WDRMediathek(Mediathek):
+ def __init__(self, simpleXbmcGui):
+ self.gui = simpleXbmcGui;
+ self.pageSize = 20; #max 49;
+ self.rootLink = "http://www.wdr.de"
+ self.menuTree = (
+ TreeNode("0","Neuste Videos",self.rootLink+"/mediathek/rdf/regional/index.xml",True),
+ TreeNode("1","Sendungen von A-Z","",False,
+ (
+ TreeNode("1.0",u"A40","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=A40",True),
+ TreeNode("1.1",u"Aktuelle Stunde","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Aktuelle+Stunde",True),
+ TreeNode("1.2",u"Am Sonntag","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Der+Sonntag",True),
+ TreeNode("1.3",u"Cosmo","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Funkhaus+Europa+-+Cosmo",True),
+ TreeNode("1.4",u"daheim & unterwegs","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=daheim+%26+unterwegs",True),
+ TreeNode("1.5",u"die story","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=die+story",True),
+ TreeNode("1.6",u"Dittsche","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Dittsche",True),
+ TreeNode("1.7",u"eins zu eins","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=eins+zu+eins",True),
+ TreeNode("1.8",u"frauTV","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=frauTV",True),
+ TreeNode("1.9",u"hier und heute","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Hier+und+Heute",True),
+ TreeNode("1.10",u"Kabarett","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Kabarett",True),
+ TreeNode("1.11",u"Lokalzeit aus Aachen","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+aus+Aachen",True),
+ TreeNode("1.12",u"Lokalzeit aus Düsseldorf","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+aus+D%FCsseldorf",True),
+ TreeNode("1.13",u"Lokalzeit OWL","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+OWL+aktuell",True),
+ TreeNode("1.14",u"Lokalzeit aus Bonn","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+aus+Bonn",True),
+ TreeNode("1.15",u"Lokalzeit aus Köln","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+aus+K%F6ln",True),
+ TreeNode("1.16",u"Lokalzeit Ruhr","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+Ruhr",True),
+ TreeNode("1.17",u"Lokalzeit aus Dortmund","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+aus+Dortmund",True),
+ TreeNode("1.18",u"Lokalzeit Bergisches Land","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+Bergisches+Land",True),
+ TreeNode("1.19",u"Lokalzeit Südwestfalen","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+S%FCdwestfalen",True),
+ TreeNode("1.20",u"Lokalzeit aus Duisburg","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+aus+Duisburg",True),
+ TreeNode("1.21",u"Lokalzeit Münsterland","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Lokalzeit+M%FCnsterland",True),
+ TreeNode("1.22",u"markt","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=markt",True),
+ TreeNode("1.23",u"Menschen hautnah","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Menschen+hautnah",True),
+ TreeNode("1.24",u"Mittagsecho","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+5+-+Mittagsecho",True),
+ TreeNode("1.25",u"Mittagsmagazin","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Mittagsmagazin",True),
+ TreeNode("1.26",u"mittwochs live","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=mittwochs+live",True),
+ TreeNode("1.27",u"Morgenecho","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+5+-+Morgenecho",True),
+ TreeNode("1.28",u"Morgenmagazin","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Morgenmagazin",True),
+ TreeNode("1.29",u"Mosaik","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+3+-+Mosaik",True),
+ TreeNode("1.30",u"Piazza","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Funkhaus+Europa+-+Piazza",True),
+ TreeNode("1.31",u"Platz der Republik","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+5+-+Platz+der+Republik",True),
+ TreeNode("1.32",u"Quarks & Co","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Quarks+%26+Co",True),
+ TreeNode("1.33",u"Resonanzen","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+3+-+Resonanzen",True),
+ TreeNode("1.34",u"Scala","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+5+-+Scala",True),
+ TreeNode("1.35",u"schön hier","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=sch%F6n+hier",True),
+ TreeNode("1.36",u"Servicezeit","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Servicezeit",True),
+ TreeNode("1.37",u"sport inside","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=sport+inside",True),
+ TreeNode("1.38",u"Stichtag","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Stichtag",True),
+ TreeNode("1.39",u"Thema NRW","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+5+-+Thema+NRW",True),
+ TreeNode("1.40",u"WDR aktuell","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+aktuell",True),
+ TreeNode("1.41",u"WDR sport aktuell","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+sport+aktuell",True),
+ TreeNode("1.42",u"west.art","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=West.art",True),
+ TreeNode("1.43",u"Westblick","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+5+-+Westblick",True),
+ TreeNode("1.44",u"WESTPOL","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WESTPOL",True),
+ TreeNode("1.45",u"Westzeit","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Westzeit",True),
+ TreeNode("1.46",u"Zeiglers wunderbare Welt des Fußballs","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Zeiglers+wunderbare+Welt+des+Fu%DFballs",True),
+ TreeNode("1.47",u"ZeitZeichen","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+3+%2F+WDR+5+-+ZeitZeichen",True),
+ TreeNode("1.48",u"Zimmer Frei!","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=Zimmer+Frei%21",True),
+ TreeNode("1.49",u"Zwischen Rhein und Weser","http://www.wdr.de/mediathek/html/regional/ergebnisse/sendung.xml?rankingtype=sendung&rankingvalue=WDR+2+-+Zwischen+Rhein+und+Weser",True),
+ )
+ ),
+ TreeNode("2","Themen","",False,
+ (
+ TreeNode("2.0","Politik","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Politik",True),
+ TreeNode("2.1","Wirtschaft","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Wirtschaft",True),
+ TreeNode("2.2","Kultur","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Kultur",True),
+ TreeNode("2.3","Panorama","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Panorama",True),
+ TreeNode("2.4","Service","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Service",True),
+ TreeNode("2.5","Freizeit","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Freizeit",True),
+ TreeNode("2.6","Sport","http://www.wdr.de/mediathek/html/regional/ergebnisse/schlagwort.xml?rankingvalue=Sport",True),
+ )
+ ),
+ )
+ self._regex_extractTitle = re.compile("<h1>.*?<span class=\"inv\">");
+ self._regex_extractDescription = re.compile("<meta name=\"description\" content=\"(.|\\s)*?\" />");
+ self._regex_extractPicture = re.compile("<link rel=\"image_src\" href=\".*?\" />");
+ self._regex_extractDate = re.compile("<meta name=\"DC.Date\" content=\".*?\" />");
+ self._regex_extractDuration = re.compile("\\((.*)\\)<span class=\"inv\">");
+
+ self._regex_extractVideoPage = re.compile("<a href=\"/mediathek/html/.*?\\.xml\" title=\".*?\".*?>");
+ self._regex_extractLink = re.compile("/mediathek/html/.*?\\.xml");
+ self._regex_extractAudioLink = re.compile(self.rootLink+"/mediathek/.*?\\.mp3");
+ self._regex_extractVideoLink = re.compile("(dsl|isdn)Src=rtmp://.*?\\.(mp4|flv)");
+ self.replace_html = re.compile("<.*?>");
+ self.replace_tag = re.compile("(<meta name=\".*?\" content=\"|<link rel=\"image_src\" href=\"|\" />)");
+ self.searchLink = "http://www.wdr.de/mediathek/html/regional/suche/index.xml?wsSucheAusgabe=liste&wsSucheSuchart=volltext&wsSucheMedium=av&suche_submit=Suche+starten&wsSucheBegriff="
+
+ @classmethod
+ def name(self):
+ return "WDR";
+ def isSearchable(self):
+ return True;
+
+ def searchVideo(self, searchText):
+ link = self.searchLink+searchText;
+ self.buildPageMenu(link, 0, False)
+ def buildPageMenu(self, link, initCount, subLink = False):
+ link = link+"&rankingcount="+str(self.pageSize);
+ self.gui.log("MenuLink: %s"%link);
+ mainPage = self.loadPage(link);
+ if(mainPage.startswith("<?xml version=\"1.0\"")):
+ self.parseXml(mainPage);
+ else:
+ self.parseHtml(mainPage);
+ def parseHtml(self, htmlPage):
+ videoPageLinks = list(self._regex_extractVideoPage.finditer(htmlPage));
+
+ for videoPageLink in videoPageLinks:
+ link = self._regex_extractLink.search(videoPageLink.group()).group();
+ print link;
+ displayObject = self.generateDisplayObject(self.rootLink+link);
+ self.gui.buildVideoLink(displayObject,self,len(videoPageLinks));
+
+ def readText(self,node,textNode):
+ try:
+ node = node.getElementsByTagName(textNode)[0].firstChild;
+ return unicode(node.data);
+ except:
+ return "";
+
+ def parseDate(self,dateString):
+ dateString = regex_dateString.search(dateString).group();
+ return time.strptime(dateString,"%Y-%m-%d");
+
+ def parseXml(self, xmlPage):
+ xmlPage = minidom.parseString(xmlPage);
+ items = xmlPage.getElementsByTagName("item");
+ for itemNode in items:
+ link = self.readText(itemNode,"link");
+ displayObject = self.generateDisplayObject(link);
+ self.gui.buildVideoLink(displayObject,self,len(items));
+
+ def generateDisplayObject(self,videoPageLink):
+ mainPage = self.loadPage(videoPageLink);
+ title = unicode(self._regex_extractTitle.search(mainPage).group(),'ISO-8859-1');
+ description = unicode(self._regex_extractDescription.search(mainPage).group(),'ISO-8859-1');
+ picture = unicode(self._regex_extractPicture.search(mainPage).group(),'ISO-8859-1');
+ date = self._regex_extractDate.search(mainPage).group();
+ duration = self._regex_extractDuration.search(mainPage).group(1);
+
+ title = self.replace_html.sub("", title);
+ description = self.replace_tag.sub("",description);
+ picture = self.replace_tag.sub("",picture);
+ date = self.parseDate(self.replace_tag.sub("",date));
+
+ links = {};
+ for linkString in self._regex_extractVideoLink.finditer(mainPage):
+ linkString = linkString.group();
+ if linkString.startswith("dslSrc="):
+ linkString = linkString.replace("dslSrc=","");
+ links[1] = self.extractLink(linkString);
+ else:
+ linkString = linkString.replace("isdnSrc=","");
+ links[0] = self.extractLink(linkString);
+
+ if len(links) == 0:
+ linkString = self._regex_extractAudioLink.search(mainPage).group();
+ links[0] = self.extractLink(linkString);
+
+ return DisplayObject(title,"",picture,description,links,True, date, duration)
+
+ def extractLink(self, linkString):
+ if(linkString.find("mediartmp://")>-1):
+ linkString = linkString.split("mediartmp://")
+ return SimpleLink("rtmp://%s"%linkString[1], 0);
+ elif(linkString.find("mediahttp://")>-1):
+ linkString = linkString.split("mediahttp://")
+ return SimpleLink("http://%s"%linkString[1], 0);
+ else:
+ return SimpleLink(linkString, 0);
diff --git a/plugin.video.mediathek/mediathek/zdf.py b/plugin.video.mediathek/mediathek/zdf.py
new file mode 100644
index 0000000..3ece7a9
--- /dev/null
+++ b/plugin.video.mediathek/mediathek/zdf.py
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+#-------------LicenseHeader--------------
+# plugin.video.Mediathek - Gives access to most video-platforms from German public service broadcasters
+# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de]
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import re,math,traceback,time
+from mediathek import *
+from datetime import datetime,timedelta
+import json
+
+class ZDFMediathek(Mediathek):
+ def __init__(self, simpleXbmcGui):
+ self.gui = simpleXbmcGui;
+
+ today = datetime.today();
+
+ self.menuTree = (
+ TreeNode("0","Startseite","https://zdf-cdn.live.cellular.de/mediathekV2/start-page",True),
+ TreeNode("1","Kategorien","https://zdf-cdn.live.cellular.de/mediathekV2/categories",True),
+ TreeNode("2","Sendungen von A-Z","https://zdf-cdn.live.cellular.de/mediathekV2/brands-alphabetical",True),
+ TreeNode("3","Sendung verpasst?","",False,(
+ TreeNode("3.0","Heute","https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%(today.strftime("%Y-%m-%d")),True),
+ TreeNode("3.1","Gestern","https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=1)).strftime("%Y-%m-%d")),True),
+ TreeNode("3.2","Vorgestern","https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=2)).strftime("%Y-%m-%d")),True),
+ TreeNode("3.3",(today-timedelta(days=3)).strftime("%A"),"https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=3)).strftime("%Y-%m-%d")),True),
+ TreeNode("3.4",(today-timedelta(days=4)).strftime("%A"),"https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=4)).strftime("%Y-%m-%d")),True),
+ TreeNode("3.5",(today-timedelta(days=5)).strftime("%A"),"https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=5)).strftime("%Y-%m-%d")),True),
+ TreeNode("3.6",(today-timedelta(days=6)).strftime("%A"),"https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=6)).strftime("%Y-%m-%d")),True),
+ TreeNode("3.7",(today-timedelta(days=7)).strftime("%A"),"https://zdf-cdn.live.cellular.de/mediathekV2/broadcast-missed/%s"%((today-timedelta(days=7)).strftime("%Y-%m-%d")),True),
+ )),
+ TreeNode("4","Live TV","https://zdf-cdn.live.cellular.de/mediathekV2/live-tv/%s"%(today.strftime("%Y-%m-%d")),True)
+ );
+ @classmethod
+ def name(self):
+ return "ZDF";
+
+ def isSearchable(self):
+ return False;
+
+ def searchVideo(self, searchText):
+ return;
+
+ def buildPageMenu(self, link, initCount):
+ self.gui.log("buildPageMenu: "+link);
+ jsonObject = json.loads(self.loadPage(link));
+ callhash = self.gui.storeJsonFile(jsonObject);
+
+ if("stage" in jsonObject):
+ for stageObject in jsonObject["stage"]:
+ if(stageObject["type"]=="video"):
+ self.buildVideoLink(stageObject,initCount);
+
+ if("cluster" in jsonObject):
+ for counter, clusterObject in enumerate(jsonObject["cluster"]):
+ if "teaser" in clusterObject and "name" in clusterObject:
+ path = "cluster.%d.teaser"%(counter)
+ self.gui.buildJsonLink(self,clusterObject["name"],path,callhash,initCount)
+ if("broadcastCluster" in jsonObject):
+ for counter, clusterObject in enumerate(jsonObject["broadcastCluster"]):
+ if clusterObject["type"].startswith("teaser") and "name" in clusterObject:
+ path = "broadcastCluster.%d.teaser"%(counter)
+ self.gui.buildJsonLink(self,clusterObject["name"],path,callhash,initCount)
+ if("epgCluster" in jsonObject):
+ for epgObject in jsonObject["epgCluster"]:
+ if("liveStream" in epgObject and len(epgObject["liveStream"])>0):
+ self.buildVideoLink(epgObject["liveStream"], initCount);
+
+ def buildJsonMenu(self, path,callhash, initCount):
+ jsonObject=self.gui.loadJsonFile(callhash);
+ jsonObject=self.walkJson(path,jsonObject);
+ categoriePages=[];
+ videoObjects=[];
+
+ for entry in jsonObject:
+ if entry["type"] == "brand":
+ categoriePages.append(entry);
+ if entry["type"] == "video":
+ videoObjects.append(entry);
+ self.gui.log("CategoriePages: %d"%len(categoriePages));
+ self.gui.log("VideoPages: %d"%len(videoObjects));
+ for categoriePage in categoriePages:
+ title=categoriePage["titel"];
+ subTitle=categoriePage["beschreibung"];
+ imageLink="";
+ for width,imageObject in categoriePage["teaserBild"].iteritems():
+ if int(width)<=840:
+ imageLink=imageObject["url"];
+ url = categoriePage["url"];
+ self.gui.buildVideoLink(DisplayObject(title,subTitle,imageLink,"",url,False),self,initCount);
+
+ for videoObject in videoObjects:
+ self.buildVideoLink(videoObject,initCount);
+
+ def buildVideoLink(self,videoObject,counter):
+ title=videoObject["headline"];
+ subTitle=videoObject["titel"];
+
+ if(len(title)==0):
+ title = subTitle;
+ subTitle = "";
+ if("beschreibung" in videoObject):
+ description=videoObject["beschreibung"];
+ imageLink="";
+ if("teaserBild" in videoObject):
+ for width,imageObject in videoObject["teaserBild"].iteritems():
+ if int(width)<=840:
+ imageLink=imageObject["url"];
+ if("formitaeten" in videoObject):
+ links = self.extractLinks(videoObject);
+ self.gui.buildVideoLink(DisplayObject(title,subTitle,imageLink,description,links,True,None,videoObject.get('length')),self,counter);
+ else:
+ link = videoObject["url"];
+ self.gui.buildVideoLink(DisplayObject(title,subTitle,imageLink,description,link,"JsonLink",None,videoObject.get('length')),self,counter);
+
+ def playVideoFromJsonLink(self,link):
+ jsonObject = json.loads(self.loadPage(link));
+ links = self.extractLinks(jsonObject["document"]);
+ self.gui.play(links);
+ def extractLinks(self,jsonObject):
+ links={};
+ for formitaete in jsonObject["formitaeten"]:
+ url = formitaete["url"];
+ quality = formitaete["quality"];
+ hd = formitaete["hd"];
+ self.gui.log("quality:%s hd:%s url:%s"%(quality,hd,url));
+ if hd == True:
+ links[4] = SimpleLink(url, -1);
+ else:
+ if quality == "low":
+ links[0] = SimpleLink(url, -1);
+ if quality == "med":
+ links[1] = SimpleLink(url, -1);
+ if quality == "high":
+ links[2] = SimpleLink(url, -1);
+ if quality == "veryhigh":
+ links[3] = SimpleLink(url, -1);
+ if quality == "auto":
+ links[3] = SimpleLink(url, -1);
+ return links;
+
diff --git a/plugin.video.mediathek/resources/language/English/strings.po b/plugin.video.mediathek/resources/language/English/strings.po
new file mode 100644
index 0000000..6e7b1c2
--- /dev/null
+++ b/plugin.video.mediathek/resources/language/English/strings.po
@@ -0,0 +1,68 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: plugin.video.mediathek\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: en\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+
+msgctxt "#30001"
+msgid "Quality"
+msgstr ""
+
+msgctxt "#30002"
+msgid "Low"
+msgstr ""
+
+msgctxt "#30003"
+msgid "Medium"
+msgstr ""
+
+msgctxt "#30004"
+msgid "High"
+msgstr ""
+
+msgctxt "#30005"
+msgid "Very High"
+msgstr ""
+
+msgctxt "#30006"
+msgid "HD"
+msgstr ""
+
+msgctxt "#30010"
+msgid "Access Method"
+msgstr ""
+
+msgctxt "#30011"
+msgid "Online"
+msgstr ""
+
+msgctxt "#30012"
+msgid "Local cache"
+msgstr ""
+
+msgctxt "#30020"
+msgid "Prefered stream type"
+msgstr ""
+
+msgctxt "#30021"
+msgid "HTTP"
+msgstr ""
+
+msgctxt "#30022"
+msgid "RTMP"
+msgstr ""
+
+msgctxt "#30023"
+msgid "MMS"
+msgstr ""
+
+msgctxt "#30024"
+msgid "MOV"
+msgstr ""
+
+msgctxt "#30100"
+msgid "Search"
+msgstr ""
diff --git a/plugin.video.mediathek/resources/language/German/strings.po b/plugin.video.mediathek/resources/language/German/strings.po
new file mode 100644
index 0000000..a9c3995
--- /dev/null
+++ b/plugin.video.mediathek/resources/language/German/strings.po
@@ -0,0 +1,68 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: plugin.video.mediathek\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: de\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+
+msgctxt "#30001"
+msgid "Quality"
+msgstr "Qualität"
+
+msgctxt "#30002"
+msgid "Low"
+msgstr "Niedrig"
+
+msgctxt "#30003"
+msgid "Medium"
+msgstr "Normal"
+
+msgctxt "#30004"
+msgid "High"
+msgstr "Hoch"
+
+msgctxt "#30005"
+msgid "Very High"
+msgstr "Sehr Hoch"
+
+msgctxt "#30006"
+msgid "HD"
+msgstr "HD"
+
+msgctxt "#30010"
+msgid "Access Method"
+msgstr "Zugriffsmethode"
+
+msgctxt "#30011"
+msgid "Online"
+msgstr "Online"
+
+msgctxt "#30012"
+msgid "Local cache"
+msgstr "Lokaler Cache"
+
+msgctxt "#30020"
+msgid "Prefered stream type"
+msgstr "Bevorzugter Streamtyp"
+
+msgctxt "#30021"
+msgid "HTTP"
+msgstr "HTTP"
+
+msgctxt "#30022"
+msgid "RTMP"
+msgstr "RTMP"
+
+msgctxt "#30023"
+msgid "MMS"
+msgstr "MMS"
+
+msgctxt "#30024"
+msgid "MOV"
+msgstr "MOV"
+
+msgctxt "#30100"
+msgid "Search"
+msgstr "Suchen"
diff --git a/plugin.video.mediathek/resources/language/Italian/strings.po b/plugin.video.mediathek/resources/language/Italian/strings.po
new file mode 100644
index 0000000..d949faa
--- /dev/null
+++ b/plugin.video.mediathek/resources/language/Italian/strings.po
@@ -0,0 +1,68 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: plugin.video.mediathek\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: it\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+
+msgctxt "#30001"
+msgid "Quality"
+msgstr "Qualità"
+
+msgctxt "#30002"
+msgid "Low"
+msgstr "Bassa"
+
+msgctxt "#30003"
+msgid "Medium"
+msgstr "Media"
+
+msgctxt "#30004"
+msgid "High"
+msgstr "Alta"
+
+msgctxt "#30005"
+msgid "Very High"
+msgstr "altissima"
+
+msgctxt "#30006"
+msgid "HD"
+msgstr "HD"
+
+msgctxt "#30010"
+msgid "Access Method"
+msgstr "Metodo d'accesso"
+
+msgctxt "#30011"
+msgid "Online"
+msgstr "Online"
+
+msgctxt "#30012"
+msgid "Local cache"
+msgstr "Lokaler Cache"
+
+msgctxt "#30020"
+msgid "Prefered stream type"
+msgstr "Modalità streaming preferita"
+
+msgctxt "#30021"
+msgid "HTTP"
+msgstr "HTTP"
+
+msgctxt "#30022"
+msgid "RTMP"
+msgstr "RTMP"
+
+msgctxt "#30023"
+msgid "MMS"
+msgstr "MMS"
+
+msgctxt "#30024"
+msgid "MOV"
+msgstr "MOV"
+
+msgctxt "#30100"
+msgid "Search"
+msgstr "Cerca"
diff --git a/plugin.video.mediathek/resources/logos/3Sat.jpg b/plugin.video.mediathek/resources/logos/3Sat.jpg
new file mode 100644
index 0000000..fc41bdf
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/3Sat.jpg
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/ARD.jpg b/plugin.video.mediathek/resources/logos/ARD.jpg
new file mode 100644
index 0000000..447d58c
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/ARD.jpg
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/ARTE.jpg b/plugin.video.mediathek/resources/logos/ARTE.jpg
new file mode 100644
index 0000000..81ffabe
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/ARTE.jpg
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/BayernFS.jpg b/plugin.video.mediathek/resources/logos/BayernFS.jpg
new file mode 100644
index 0000000..0eb6656
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/BayernFS.jpg
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/ORF.jpg b/plugin.video.mediathek/resources/logos/ORF.jpg
new file mode 100644
index 0000000..177372e
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/ORF.jpg
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/ZDF.jpg b/plugin.video.mediathek/resources/logos/ZDF.jpg
new file mode 100644
index 0000000..841363c
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/ZDF.jpg
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/phoenix.jpg b/plugin.video.mediathek/resources/logos/phoenix.jpg
new file mode 100644
index 0000000..b56c6ee
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/phoenix.jpg
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/png/3Sat.png b/plugin.video.mediathek/resources/logos/png/3Sat.png
new file mode 100644
index 0000000..7213796
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/png/3Sat.png
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/png/ARD.png b/plugin.video.mediathek/resources/logos/png/ARD.png
new file mode 100644
index 0000000..274ef4b
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/png/ARD.png
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/png/ARTE.png b/plugin.video.mediathek/resources/logos/png/ARTE.png
new file mode 100644
index 0000000..dfe204f
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/png/ARTE.png
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/png/BayernFS.png b/plugin.video.mediathek/resources/logos/png/BayernFS.png
new file mode 100644
index 0000000..c604109
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/png/BayernFS.png
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/png/KI.KA-Plus.png b/plugin.video.mediathek/resources/logos/png/KI.KA-Plus.png
new file mode 100644
index 0000000..9bb468c
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/png/KI.KA-Plus.png
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/png/LICENSE b/plugin.video.mediathek/resources/logos/png/LICENSE
new file mode 100644
index 0000000..ffdbc3f
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/png/LICENSE
@@ -0,0 +1,118 @@
+APPLIES TO ALL FILES in logos/png/
+
+CC0 1.0 Universal
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator and
+subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for the
+purpose of contributing to a commons of creative, cultural and scientific
+works ("Commons") that the public can reliably and without fear of later
+claims of infringement build upon, modify, incorporate in other works, reuse
+and redistribute as freely as possible in any form whatsoever and for any
+purposes, including without limitation commercial purposes. These owners may
+contribute to the Commons to promote the ideal of a free culture and the
+further production of creative, cultural and scientific works, or to gain
+reputation or greater distribution for their Work in part through the use and
+efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation
+of additional consideration or compensation, the person associating CC0 with a
+Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
+and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
+and publicly distribute the Work under its terms, with knowledge of his or her
+Copyright and Related Rights in the Work and the meaning and intended legal
+effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not limited
+to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display, communicate,
+ and translate a Work;
+
+ ii. moral rights retained by the original author(s) and/or performer(s);
+
+ iii. publicity and privacy rights pertaining to a person's image or likeness
+ depicted in a Work;
+
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+
+ v. rights protecting the extraction, dissemination, use and reuse of data in
+ a Work;
+
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation thereof,
+ including any amended or successor version of such directive); and
+
+ vii. other similar, equivalent or corresponding rights throughout the world
+ based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of,
+applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
+unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
+and Related Rights and associated claims and causes of action, whether now
+known or unknown (including existing as well as future claims and causes of
+action), in the Work (i) in all territories worldwide, (ii) for the maximum
+duration provided by applicable law or treaty (including future time
+extensions), (iii) in any current or future medium and for any number of
+copies, and (iv) for any purpose whatsoever, including without limitation
+commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
+the Waiver for the benefit of each member of the public at large and to the
+detriment of Affirmer's heirs and successors, fully intending that such Waiver
+shall not be subject to revocation, rescission, cancellation, termination, or
+any other legal or equitable action to disrupt the quiet enjoyment of the Work
+by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be
+judged legally invalid or ineffective under applicable law, then the Waiver
+shall be preserved to the maximum extent permitted taking into account
+Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
+is so judged Affirmer hereby grants to each affected person a royalty-free,
+non transferable, non sublicensable, non exclusive, irrevocable and
+unconditional license to exercise Affirmer's Copyright and Related Rights in
+the Work (i) in all territories worldwide, (ii) for the maximum duration
+provided by applicable law or treaty (including future time extensions), (iii)
+in any current or future medium and for any number of copies, and (iv) for any
+purpose whatsoever, including without limitation commercial, advertising or
+promotional purposes (the "License"). The License shall be deemed effective as
+of the date CC0 was applied by Affirmer to the Work. Should any part of the
+License for any reason be judged legally invalid or ineffective under
+applicable law, such partial invalidity or ineffectiveness shall not
+invalidate the remainder of the License, and in such case Affirmer hereby
+affirms that he or she will not (i) exercise any of his or her remaining
+Copyright and Related Rights in the Work or (ii) assert any associated claims
+and causes of action with respect to the Work, in either case contrary to
+Affirmer's express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+
+ b. Affirmer offers the Work as-is and makes no representations or warranties
+ of any kind concerning the Work, express, implied, statutory or otherwise,
+ including without limitation warranties of title, merchantability, fitness
+ for a particular purpose, non infringement, or the absence of latent or
+ other defects, accuracy, or the present or absence of errors, whether or not
+ discoverable, all to the greatest extent permissible under applicable law.
+
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without limitation
+ any person's Copyright and Related Rights in the Work. Further, Affirmer
+ disclaims responsibility for obtaining any necessary consents, permissions
+ or other rights required for any use of the Work.
+
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to this
+ CC0 or use of the Work.
+
+For more information, please see
+<http://creativecommons.org/publicdomain/zero/1.0/>
diff --git a/plugin.video.mediathek/resources/logos/png/NDR.png b/plugin.video.mediathek/resources/logos/png/NDR.png
new file mode 100644
index 0000000..0cfb60f
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/png/NDR.png
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/png/ORF.png b/plugin.video.mediathek/resources/logos/png/ORF.png
new file mode 100644
index 0000000..350b868
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/png/ORF.png
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/png/ZDF.png b/plugin.video.mediathek/resources/logos/png/ZDF.png
new file mode 100644
index 0000000..ab47225
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/png/ZDF.png
Binary files differ
diff --git a/plugin.video.mediathek/resources/logos/png/phoenix.png b/plugin.video.mediathek/resources/logos/png/phoenix.png
new file mode 100644
index 0000000..1d708e6
--- /dev/null
+++ b/plugin.video.mediathek/resources/logos/png/phoenix.png
Binary files differ
diff --git a/plugin.video.mediathek/resources/settings.xml b/plugin.video.mediathek/resources/settings.xml
new file mode 100644
index 0000000..5a916e4
--- /dev/null
+++ b/plugin.video.mediathek/resources/settings.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<settings>
+
+ <!-- General -->
+ <category label="General">
+ <setting id="quality" type="enum" label="30001" lvalues="30002|30003|30004|30005|30006" default="2" />
+ <setting id="preferedStreamType" type="enum" label="30020" lvalues="30021|30022|30023|30024" default="1" />
+ </category>
+</settings>
+
diff --git a/plugin.video.mediathek/simplexbmc.py b/plugin.video.mediathek/simplexbmc.py
new file mode 100644
index 0000000..8924b34
--- /dev/null
+++ b/plugin.video.mediathek/simplexbmc.py
@@ -0,0 +1,216 @@
+# -*- coding: utf-8 -*-
+#-------------LicenseHeader--------------
+# plugin.video.mediathek - display german mediathekes
+# Copyright (C) 2010 Raptor 2101 [raptor2101@gmx.de]
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import xbmc, xbmcgui, xbmcplugin,xbmcaddon, sys, urllib, os, time, re
+from bs4 import BeautifulSoup;
+import json
+import hashlib
+from mediathek import ComplexLink;
+
+regex_findLink = re.compile("mms://[^\"]*wmv");
+
+__plugin__ = "Mediathek"
+
+settings = xbmcaddon.Addon(id='plugin.video.mediathek')
+translation = settings.getLocalizedString
+
+class SimpleXbmcGui(object):
+ def __init__(self,settings):
+ self.settings = xbmcaddon.Addon(id='plugin.video.mediathek');
+ self.quality = int(xbmcplugin.getSetting(int(sys.argv[1]), "quality" ));
+ self.preferedStreamTyp = int(xbmcplugin.getSetting(int(sys.argv[1]), "preferedStreamType"));
+ self.log("quality: %s"%(self.quality));
+ self.plugin_profile_dir = xbmc.translatePath(settings.getAddonInfo("profile"))
+ if not os.path.exists(self.plugin_profile_dir):
+ os.mkdir(self.plugin_profile_dir);
+
+ def log(self, msg):
+ if not isinstance(msg, (str, unicode)):
+ xbmc.log("[%s]: %s" % (__plugin__, type(msg)))
+ else:
+ xbmc.log("[%s]: %s" % (__plugin__, msg.encode('utf8')))
+
+ def buildVideoLink(self, displayObject, mediathek, objectCount):
+ if(displayObject.subTitle == "" or displayObject.subTitle == displayObject.title):
+ title = self.transformHtmlCodes(displayObject.title);
+ else:
+ title = self.transformHtmlCodes(displayObject.title +" - "+ displayObject.subTitle);
+ if displayObject.date is not None:
+ title = "(%s) %s"%(time.strftime("%d.%m",displayObject.date),title);
+ if displayObject.picture is not None:
+ listItem=xbmcgui.ListItem(title, iconImage="DefaultFolder.png", thumbnailImage=displayObject.picture)
+ else:
+ listItem=xbmcgui.ListItem(title, iconImage="DefaultFolder.png")
+ if(displayObject.isPlayable):
+ if(displayObject.isPlayable == "PlayList"):
+ link = displayObject.link[0]
+ url = "%s?type=%s&action=openPlayList&link=%s" % (sys.argv[0],mediathek.name(), urllib.quote_plus(link.basePath))
+ listItem.setProperty('IsPlayable', 'true');
+ xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=listItem,isFolder=False,totalItems = objectCount)
+ elif(displayObject.isPlayable == "JsonLink"):
+ link = displayObject.link
+ url = "%s?type=%s&action=openJsonLink&link=%s" % (sys.argv[0],mediathek.name(), urllib.quote_plus(link))
+ listItem.setProperty('IsPlayable', 'true');
+ listItem.setInfo("video", {
+ "mediatype":"video",
+ "title": title,
+ "plot": self.transformHtmlCodes(displayObject.description),
+ "duration": displayObject.duration
+ })
+ xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=listItem,isFolder=False,totalItems = objectCount)
+ else:
+ self.log(displayObject.title);
+ link = self.extractLink(displayObject.link);
+ if(isinstance(link,ComplexLink)):
+ self.log("PlayPath:"+ link.playPath);
+ listItem.setProperty("PlayPath", link.playPath);
+
+ self.log("URL:"+ link.basePath);
+
+ metaData = {
+ "mediatype":"video",
+ "size": link.size,
+ "title": title,
+ "plot": self.transformHtmlCodes(displayObject.description),
+ "duration": displayObject.duration
+ }
+
+ if(displayObject.date is not None):
+ metaData["date"] =time.strftime("%d.%m.%Y",displayObject.date);
+ metaData["year"] =int(time.strftime("%Y",displayObject.date));
+
+ listItem.setInfo("video",metaData);
+ listItem.setProperty('IsPlayable', 'true');
+ xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=link.basePath,listitem=listItem,isFolder=False,totalItems = objectCount)
+ else:
+ url = "%s?type=%s&action=openTopicPage&link=%s" % (sys.argv[0],mediathek.name(), urllib.quote_plus(displayObject.link))
+ xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=listItem,isFolder=True,totalItems = objectCount)
+
+ def transformHtmlCodes(self, content):
+ return BeautifulSoup(content).prettify(formatter=None);
+
+ def buildMenuLink(self,menuObject,mediathek,objectCount):
+ title = menuObject.name;
+ listItem=xbmcgui.ListItem(title, iconImage="DefaultFolder.png")
+ url = "%s?type=%s&action=openMenu&path=%s" % (sys.argv[0],mediathek.name(), menuObject.path)
+ xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=listItem,isFolder=True,totalItems = objectCount)
+
+ def storeJsonFile(self,jsonObject,additionalIdentifier = None):
+ hashGenerator = hashlib.md5();
+ hashGenerator.update(sys.argv[2]);
+ if(additionalIdentifier is not None):
+ hashGenerator.update(additionalIdentifier);
+ callhash = hashGenerator.hexdigest();
+ storedJsonFile = os.path.join(self.plugin_profile_dir,"%s.json"%callhash);
+ with open(storedJsonFile, 'wb') as output:
+ json.dump(jsonObject,output);
+ return callhash;
+
+ def loadJsonFile(self,callhash):
+ storedJsonFile = os.path.join(self.plugin_profile_dir,"%s.json"%callhash);
+ with open(storedJsonFile,"rb") as input:
+ return json.load(input);
+
+ def buildJsonLink(self,mediathek,title,jsonPath,callhash,objectCount):
+ listItem=xbmcgui.ListItem(title, iconImage="DefaultFolder.png")
+ url = "%s?type=%s&action=openJsonPath&path=%s&callhash=%s" % (sys.argv[0],mediathek.name(), jsonPath,callhash)
+ xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=listItem,isFolder=True,totalItems = objectCount)
+
+ def listAvailableMediathekes(self, mediathekNames):
+ rootPath = os.path.join(self.settings.getAddonInfo('path'),"resources/logos/png/");
+ for name in mediathekNames:
+ listItem=xbmcgui.ListItem(name, iconImage="DefaultFolder.png",thumbnailImage=os.path.join(rootPath,name+".png"))
+ url = "%s?type=%s" % (sys.argv[0], name)
+ xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=listItem,isFolder=True)
+ def openMenuContext(self):
+ self.dialogProgress = xbmcgui.DialogProgress();
+
+ def closeMenuContext(self):
+ xbmcplugin.endOfDirectory(int(sys.argv[1]))
+
+ def getHomeDir(self):
+ return self.settings.getAddonInfo("profile");
+
+ def back(self):
+ xbmc.executebuiltin("Action(PreviousMenu)");
+
+ def keyboardInput(self):
+ keyboard = xbmc.Keyboard("")
+ keyboard.doModal();
+ return keyboard;
+
+ def addSearchButton(self,mediathek):
+ title = translation(30100);
+ listItem=xbmcgui.ListItem(title, iconImage="DefaultFolder.png")
+ if(mediathek is not None):
+ url = "%s?type=%s&action=search" % (sys.argv[0],mediathek.name())
+ else:
+ url = "%s?action=search" % (sys.argv[0])
+ xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=url,listitem=listItem,isFolder=True)
+
+ def readText(self,node,textNode):
+ try:
+ node = node.getElementsByTagName(textNode)[0].firstChild;
+ return unicode(node.data);
+ except:
+ return "";
+
+ def playPlaylist(self, remotePlaylist):
+ player = xbmc.Player();
+
+ playerItem = xbmcgui.ListItem(remotePlaylist);
+ playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO);
+ playlist.clear();
+ for link in regex_findLink.findall(remotePlaylist):
+ listItem=xbmcgui.ListItem(link);
+ listItem.setProperty("PlayPath", link);
+ playlist.add(url=link, listitem=listItem);
+
+ player.play(playlist, playerItem, false);
+
+ def errorOK(self,title="", msg=""):
+ e = str( sys.exc_info()[ 1 ] )
+ self.log(e)
+ if not title:
+ title = __plugin__
+ if not msg:
+ msg = "ERROR!"
+ if(e == None):
+ xbmcgui.Dialog().ok( title, msg, e )
+ else:
+ xbmcgui.Dialog().ok( title, msg)
+ def play(self,links):
+ link = self.extractLink(links);
+ listItem = xbmcgui.ListItem(path=link.basePath)
+ xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem=listItem)
+
+ def extractLink(self, links):
+ if(self.quality in links):
+ return links[self.quality];
+ else:
+ selectedKey = -1;
+ for key in links.keys():
+ if(key < self.quality and key > selectedKey):
+ selectedKey = key;
+ if(selectedKey > -1):
+ return links[selectedKey];
+ else:
+ selectedKey = links.keys()[0];
+ for key in links.keys():
+ if(key < selectedKey):
+ selectedKey = key;
+ return links[selectedKey];