summaryrefslogtreecommitdiff
path: root/plugin.video.iplayerwww
diff options
context:
space:
mode:
authorCaptainT <CaptainTK@users.noreply.github.com>2017-03-01 19:27:48 +0100
committerCaptainT <CaptainTK@users.noreply.github.com>2017-03-01 19:27:48 +0100
commit8a75310fc6880fddd3e2a96cb6b0713151c53652 (patch)
tree79de90659f0f9a80c42c60ecc9fb7eefe7bf58f0 /plugin.video.iplayerwww
parent30e951462df33a56918cb744dd5041496b9743ab (diff)
[plugin.video.iplayerwww] 3.0.0
Diffstat (limited to 'plugin.video.iplayerwww')
-rw-r--r--plugin.video.iplayerwww/.gitignore68
-rw-r--r--plugin.video.iplayerwww/Credits.txt16
-rw-r--r--plugin.video.iplayerwww/LICENSE.txt87
-rw-r--r--plugin.video.iplayerwww/README.md4
-rw-r--r--plugin.video.iplayerwww/addon.xml466
-rw-r--r--plugin.video.iplayerwww/default.py226
-rw-r--r--plugin.video.iplayerwww/media/LICENSE.txt6
-rw-r--r--plugin.video.iplayerwww/media/bbc_1xtra.pngbin0 -> 4690 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_6music.pngbin0 -> 5254 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_alba.pngbin0 -> 1804 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_asian_network.pngbin0 -> 7127 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_four_hd.pngbin0 -> 3491 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_london.pngbin0 -> 4686 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_news24.pngbin0 -> 3872 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_cambridge.pngbin0 -> 6632 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_channel_islands.pngbin0 -> 7341 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_east.pngbin0 -> 4426 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_east_midlands.pngbin0 -> 7057 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_east_yorkshire.pngbin0 -> 7412 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_hd.pngbin0 -> 3068 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_london.pngbin0 -> 5012 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_north_east.pngbin0 -> 6258 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_north_west.pngbin0 -> 6660 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_northern_ireland_hd.pngbin0 -> 7265 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_oxford.pngbin0 -> 5808 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_scotland_hd.pngbin0 -> 6533 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_south.pngbin0 -> 4899 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_south_east.pngbin0 -> 6237 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_south_west.pngbin0 -> 6656 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_wales_hd.pngbin0 -> 5333 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_west.pngbin0 -> 4847 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_west_midlands.pngbin0 -> 7471 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_one_yorks.pngbin0 -> 5748 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_parliament.pngbin0 -> 4313 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_berkshire.pngbin0 -> 5041 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_bristol.pngbin0 -> 5156 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_cambridge.pngbin0 -> 5976 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_cornwall.pngbin0 -> 6161 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_coventry_warwickshire.pngbin0 -> 9421 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_cumbria.pngbin0 -> 5442 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_cymru.pngbin0 -> 4940 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_derby.pngbin0 -> 4653 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_devon.pngbin0 -> 5082 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_essex.pngbin0 -> 4503 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_five_live.pngbin0 -> 4137 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_five_live_sports_extra.pngbin0 -> 7138 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_four_extra.pngbin0 -> 4883 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_fourfm.pngbin0 -> 3851 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_fourlw.pngbin0 -> 4340 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_foyle.pngbin0 -> 4313 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_gloucestershire.pngbin0 -> 5945 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_guernsey.pngbin0 -> 5430 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_hereford_worcester.pngbin0 -> 8258 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_humberside.pngbin0 -> 5446 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_jersey.pngbin0 -> 4536 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_kent.pngbin0 -> 4075 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_lancashire.pngbin0 -> 5440 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_leeds.pngbin0 -> 4135 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_leicester.pngbin0 -> 4711 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_lincolnshire.pngbin0 -> 5630 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_manchester.pngbin0 -> 5711 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_merseyside.pngbin0 -> 5231 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_nan_gaidheal.pngbin0 -> 5808 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_newcastle.pngbin0 -> 5737 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_norfolk.pngbin0 -> 5155 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_northampton.pngbin0 -> 5881 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_nottingham.pngbin0 -> 5627 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_one.pngbin0 -> 3292 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_oxford.pngbin0 -> 5309 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_scotland_fm.pngbin0 -> 5873 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_sheffield.pngbin0 -> 4205 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_shropshire.pngbin0 -> 5297 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_solent.pngbin0 -> 4721 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_somerset_sound.pngbin0 -> 5167 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_stoke.pngbin0 -> 4830 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_suffolk.pngbin0 -> 5058 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_surrey.pngbin0 -> 4792 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_sussex.pngbin0 -> 4828 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_three.pngbin0 -> 3654 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_two.pngbin0 -> 3627 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_ulster.pngbin0 -> 4483 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_wales_fm.pngbin0 -> 4988 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_wiltshire.pngbin0 -> 5032 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_radio_york.pngbin0 -> 4898 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_tees.pngbin0 -> 3836 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_three_counties_radio.pngbin0 -> 5874 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_three_hd.pngbin0 -> 2349 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_two_england.pngbin0 -> 6077 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_two_hd.pngbin0 -> 3712 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_two_northern_ireland_digital.pngbin0 -> 7905 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_two_scotland.pngbin0 -> 7170 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_two_wales_digital.pngbin0 -> 5939 bytes
-rw-r--r--plugin.video.iplayerwww/media/bbc_wm.pngbin0 -> 5581 bytes
-rw-r--r--plugin.video.iplayerwww/media/cbbc_hd.pngbin0 -> 1941 bytes
-rw-r--r--plugin.video.iplayerwww/media/cbeebies_hd.pngbin0 -> 2694 bytes
-rw-r--r--plugin.video.iplayerwww/media/favourites.pngbin0 -> 12536 bytes
-rw-r--r--plugin.video.iplayerwww/media/lists.pngbin0 -> 6296 bytes
-rw-r--r--plugin.video.iplayerwww/media/live.pngbin0 -> 13099 bytes
-rw-r--r--plugin.video.iplayerwww/media/popular.pngbin0 -> 14773 bytes
-rw-r--r--plugin.video.iplayerwww/media/red_button.pngbin0 -> 31246 bytes
-rw-r--r--plugin.video.iplayerwww/media/s4cpbs.pngbin0 -> 2144 bytes
-rw-r--r--plugin.video.iplayerwww/media/search.pngbin0 -> 10489 bytes
-rw-r--r--plugin.video.iplayerwww/media/settings.pngbin0 -> 14231 bytes
-rw-r--r--plugin.video.iplayerwww/media/top_rated.pngbin0 -> 11128 bytes
-rw-r--r--plugin.video.iplayerwww/media/tv.pngbin0 -> 5214 bytes
-rw-r--r--plugin.video.iplayerwww/resources/fanart.jpgbin0 -> 68296 bytes
-rw-r--r--plugin.video.iplayerwww/resources/icon.pngbin0 -> 27305 bytes
-rw-r--r--plugin.video.iplayerwww/resources/language/English/strings.po412
-rw-r--r--plugin.video.iplayerwww/resources/lib/ipwww_common.py680
-rw-r--r--plugin.video.iplayerwww/resources/lib/ipwww_radio.py625
-rw-r--r--plugin.video.iplayerwww/resources/lib/ipwww_video.py1403
-rw-r--r--plugin.video.iplayerwww/resources/settings.xml56
112 files changed, 4049 insertions, 0 deletions
diff --git a/plugin.video.iplayerwww/.gitignore b/plugin.video.iplayerwww/.gitignore
new file mode 100644
index 0000000..c37a3eb
--- /dev/null
+++ b/plugin.video.iplayerwww/.gitignore
@@ -0,0 +1,68 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Eclipse PyDev
+.settings/
+.project
+.pydevproject
+
+# Backup Files
+*.bak
+*~
+*.orig
diff --git a/plugin.video.iplayerwww/Credits.txt b/plugin.video.iplayerwww/Credits.txt
new file mode 100644
index 0000000..9c81dbf
--- /dev/null
+++ b/plugin.video.iplayerwww/Credits.txt
@@ -0,0 +1,16 @@
+This plugin was inspired by the BBC iPlayer plugin of Mikey1234.
+http://www.xunitytalk.com/
+
+Thanks to the get_iplayer project. Their code helped to sort out live streams more than once.
+https://github.com/get-iplayer/get_iplayer
+
+Channel icons by primaeval
+
+The functions used for downloading subtitles are based on the latest version
+of the (currently defunct) iPlayer plugin.
+https://github.com/joolswills/plugin.video.iplayer
+
+IPlayer Icon by b0bd0gz
+http://b0bd0gz.deviantart.com/art/BBC-iPlayer-61572380
+CC License: http://creativecommons.org/licenses/by-nc-sa/3.0/
+
diff --git a/plugin.video.iplayerwww/LICENSE.txt b/plugin.video.iplayerwww/LICENSE.txt
new file mode 100644
index 0000000..2a05465
--- /dev/null
+++ b/plugin.video.iplayerwww/LICENSE.txt
@@ -0,0 +1,87 @@
+GNU GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) 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 this service 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 make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. 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.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification follow.
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
+ b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
+ c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+ b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+ c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the 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 a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE 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.
+
diff --git a/plugin.video.iplayerwww/README.md b/plugin.video.iplayerwww/README.md
new file mode 100644
index 0000000..a86726f
--- /dev/null
+++ b/plugin.video.iplayerwww/README.md
@@ -0,0 +1,4 @@
+# plugin.video.iplayerwww
+BBC iPlayer for Kodi
+Support: http://forum.kodi.tv/showthread.php?tid=239378
+Author: CaptainT \ No newline at end of file
diff --git a/plugin.video.iplayerwww/addon.xml b/plugin.video.iplayerwww/addon.xml
new file mode 100644
index 0000000..c5ac661
--- /dev/null
+++ b/plugin.video.iplayerwww/addon.xml
@@ -0,0 +1,466 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<addon id="plugin.video.iplayerwww" name="iPlayer WWW" version="3.0.0" provider-name="CaptainT, Cas, ihurst, primaeval">
+ <requires>
+ <import addon="xbmc.python" version="2.25.0"/>
+ <import addon="script.module.requests" version="2.7.0"/>
+ <import addon="inputstream.adaptive" version="1.0.6"/>
+ </requires>
+ <extension point="xbmc.python.pluginsource" library="default.py">
+ <provides>audio video</provides>
+ </extension>
+ <extension point="xbmc.addon.metadata">
+ <summary lang="en">TV and Radio from BBC iPlayer</summary>
+ <description lang="en">This add-on enables playing of Live and Catchup TV (UK only) and Radio content (UK or international) from the BBC iPlayer website. It is not created, maintained or in any way affiliated with the BBC. The add-on only provides an interface to access the BBC iPlayer website from Kodi.</description>
+ <platform>all</platform>
+ <license>GNU GENERAL PUBLIC LICENSE. Version 2, June 1991</license>
+ <forum>http://forum.kodi.tv/showthread.php?tid=239378</forum>
+ <website></website>
+ <email></email>
+ <source>https://github.com/vonH/plugin.video.iplayerwww</source>
+ <disclaimer>This add-on is a front end to the BBC iPlayer website. It is not an official BBC product. A UK TV Licence is required to watch the live, catch-up, or on-demand TV content.</disclaimer>
+ <assets>
+ <icon>resources/icon.png</icon>
+ <fanart>resources/fanart.jpg</fanart>
+ </assets>
+ <news>
+v3.0.0
+Initial release for Kodi Krypton.
+Support for DASH streams if inputstream.adaptive add-on is available.
+
+v2.5.6
+Added live TV channel BBC One South West.
+
+v2.5.5
+Fixed broken Highlights.
+Added option to automatically sign in with BBCiD.
+NOTE: If this option is enabled, users will always be signed in.
+
+v2.5.4
+Fixed missing icon image when using BBCiD.
+Properly added new CDN Bidi (worked before just by chance).
+Added option to select main menu entries.
+
+v2.5.3
+New image urls.
+
+v2.5.2
+Fixed typo.
+Adapted to latest changes on the website.
+
+v2.5.1
+Fix for broken BBC iD functionality.
+COMPATIBILITY NOTE:
+Users need to sign in on the website once and provide additional data, e.g. their
+postcode before using the updated BBC iD functionality in the add-on.
+
+v2.5.0
+New channel icons for TV and Radio by primaeval. Now all channels have dedicated icons.
+Added warning regarding new TV Licence legislation.
+
+v2.4.1
+Fixed problem which caused too many playlists to be loaded.
+Tidied up some code.
+
+v2.4.0
+Added more Red Button streams.
+Added more regional Live TV.
+Added option to disable Red Button warning pop-up.
+Added icons for top level directories.
+
+v2.3.4
+Moved to different, hopefully more stable, source for Red Button streams.
+Red Button now uses the same code as Watch Live.
+
+v2.3.3
+Fixed high quality Red Button streams.
+
+v2.3.2
+Fixed Radio Favourites and renamed to Added, as on the website.
+
+v2.3.1
+Fixed Favourites and renamed to Added, as on the website.
+
+v2.3.0
+Added mode which shows CBeebies/CBBC only.
+Rearranged settings.
+Moved translation to .po format. (already in 2.2.1)
+
+v2.2.1
+Fixed problem with new Highlights layout.
+Fixed problem with new XML format.
+
+v2.2.0
+Added support for Red Button.
+Fixed empty directory in Channel Highlights.
+
+v2.1.3
+Added more bitrate options for Radio.
+Switched to new stable source for live TV.
+COMPATIBILITY NOTE:
+If autoplay is used for live TV, users MUST set their preferences for live TV bitrate again.
+
+v2.1.2
+Fixed missing live non-HD TV.
+
+v2.1.1
+Fixed live HD TV.
+
+v2.1.0
+Added support for international radio.
+Set default Live TV to 1.7Mbps.
+Live Radio max bitrate setting.
+Removed BBC Three live.
+
+v2.0.1
+Reverted sorting of programmes back to previous default.
+Fixed various warnings in the logfile.
+Added support for Channel A-Z.
+
+v2.0.0
+Added support for radio.
+Broke up code into different files.
+Added option to sign out BBCiD.
+
+v1.3.5
+Fix for Unicode characters in subtitles.
+
+v1.3.4
+Fix for new URL of BBC Three.
+
+v1.3.3
+Fix for episode count in Favourites.
+
+v1.3.2
+Fixed missing multiple episodes in Favourites.
+Added missing unescaping of HTML.
+Fix for single page listings.
+Minor fixes of potential errors.
+Added optional settings for displaying programme lists.
+
+v1.3.1
+Fixed aired date in Favourites and Watching.
+Fixed incompatibility with Python 2.6.
+
+v1.3.0
+Redesign of scrapers for more robustness, speed and ease of maintenance.
+New progress bar during lengthy scrapes.
+Search groups are supported again.
+Fixed missing aired date.
+Corrected and added dependencies.
+Added support for keyword injection in Search.
+
+v1.2.10
+Fix for changed HTML layout.
+
+v1.2.9
+Fixed broken aired date.
+Added rudimentary support for aired date in Categories.
+
+v1.2.8
+Temporarily removed support for search groups as it may cause Search to fail completely.
+
+v1.2.7
+Fixed bug in Categories with subtcategories, which was introduced in 1.2.6.
+
+v1.2.6
+Cleaned up scraping of aired date.
+Fixed subtitles for newer programmes.
+Fixed some missing channel icons for Live TV.
+
+v1.2.5
+Adapted to new design Highlights website.
+
+v1.2.4
+Added Channel Highlights.
+Fixed missing plot bug in Watching.
+Fixed bugs related to UTF-8 encoded v2.5.4
+Fixed missing icon image when using BBCiD.
+Properly added new CDN Bidi (worked before just by chance).
+Added option to select main menu entries.
+
+v2.5.3
+New image urls.
+
+v2.5.2
+Fixed typo.
+Adapted to latest changes on the website.
+
+v2.5.1
+Fix for broken BBC iD functionality.
+COMPATIBILITY NOTE:
+Users need to sign in on the website once and provide additional data, e.g. their
+postcode before using the updated BBC iD functionality in the add-on.
+
+v2.5.0
+New channel icons for TV and Radio by primaeval. Now all channels have dedicated icons.
+Added warning regarding new TV Licence legislation.
+
+v2.4.1
+Fixed problem which caused too many playlists to be loaded.
+Tidied up some code.
+
+v2.4.0
+Added more Red Button streams.
+Added more regional Live TV.
+Added option to disable Red Button warning pop-up.
+Added icons for top level directories.
+
+v2.3.4
+Moved to different, hopefully more stable, source for Red Button streams.
+Red Button now uses the same code as Watch Live.
+
+v2.3.3
+Fixed high quality Red Button streams.
+
+v2.3.2
+Fixed Radio Favourites and renamed to Added, as on the website.
+
+v2.3.1
+Fixed Favourites and renamed to Added, as on the website.
+
+v2.3.0
+Added mode which shows CBeebies/CBBC only.
+Rearranged settings.
+Moved translation to .po format. (already in 2.2.1)
+
+v2.2.1
+Fixed problem with new Highlights layout.
+Fixed problem with new XML format.
+
+v2.2.0
+Added support for Red Button.
+Fixed empty directory in Channel Highlights.
+
+v2.1.3
+Added more bitrate options for Radio.
+Switched to new stable source for live TV.
+COMPATIBILITY NOTE:
+If autoplay is used for live TV, users MUST set their preferences for live TV bitrate again.
+
+v2.1.2
+Fixed missing live non-HD TV.
+
+v2.1.1
+Fixed live HD TV.
+
+v2.1.0
+Added support for international radio.
+Set default Live TV to 1.7Mbps.
+Live Radio max bitrate setting.
+Removed BBC Three live.
+
+v2.0.1
+Reverted sorting of programmes back to previous default.
+Fixed various warnings in the logfile.
+Added support for Channel A-Z.
+
+v2.0.0
+Added support for radio.
+Broke up code into different files.
+Added option to sign out BBCiD.
+
+v1.3.5
+Fix for Unicode characters in subtitles.
+
+v1.3.4
+Fix for new URL of BBC Three.
+
+v1.3.3
+Fix for episode count in Favourites.
+
+v1.3.2
+Fixed missing multiple episodes in Favourites.
+Added missing unescaping of HTML.
+Fix for single page listings.
+Minor fixes of potential errors.
+Added optional settings for displaying programme lists.
+
+v1.3.1
+Fixed aired date in Favourites and Watching.
+Fixed incompatibility with Python 2.6.
+
+v1.3.0
+Redesign of scrapers for more robustness, speed and ease of maintenance.
+New progress bar during lengthy scrapes.
+Search groups are supported again.
+Fixed missing aired date.
+Corrected and added dependencies.
+Added support for keyword injection in Search.
+
+v1.2.10
+Fix for changed HTML layout.
+
+v1.2.9
+Fixed broken aired date.
+Added rudimentary support for aired date in Categories.
+
+v1.2.8
+Temporarily removed support for search groups as it may cause Search to fail completely.
+
+v1.2.7
+Fixed bug in Categories with subtcategories, which was introduced in 1.2.6.
+
+v1.2.6
+Cleaned up scraping of aired date.
+Fixed subtitles for newer programmes.
+Fixed some missing channel icons for Live TV.
+
+v1.2.5
+Adapted to new design Highlights website.
+
+v1.2.4
+Added Channel Highlights.
+Fixed missing plot bug in Watching.
+Fixed bugs related to UTF-8 encoded programme names.
+Fixed HTML tags showing up in some Highlights programme names.
+
+v1.2.3
+Fixed various bugs in personalisation features.
+Improved cookie handling.
+Make sure the user data directory exists.
+
+v1.2.2
+Further localization of messages.
+Fixed bugs in translations.
+
+v1.2.1
+Localized main menu entries and pop-up messages.
+
+v1.2.0
+Added personalisation features.
+Fixed S4C live TV.
+Final tweaks and fixes for Highlights section.
+Added error pop-ups for requests.
+
+v1.1.2
+Adapted to new layout of Highlights page.
+Part of this is still experimental and may need further fixes.
+
+v1.1.1
+Fixed bug in settings dialog.
+
+v1.1.0
+Added first aired date for episodes.
+Redesigned user interface for manual stream selection and preferences dialog.
+Cleaned up unneccesary code.
+Improved error dialog.
+
+v1.0.1
+Fixup code to pep8 standard.
+Convert to docstrings.
+Fixed typo.
+Added error dialog.
+
+v1.0.0
+Corrected a few typos.
+
+v1.0.0rc4
+Added additional sources for catchup content.
+Adapted to new website layout in Highlights section.
+
+v1.0.0rc3
+Fixed bug in bitrate selection of live TV and catchup content.
+
+v1.0.0rc2
+Added regional channels for live TV.
+Fixed display of icons for live TV.
+Fixed bug in bitrate selection of live TV.
+Fixed bug in bitrate selection of some catchup content.
+
+v1.0.0rc1
+Fixed bug in category and search view: Programmes with multiple episodes did not show up correctly.
+
+v1.0.0beta5
+Moved from urllib2 to requests (adds dependency to requests add-on from official repo).
+Corrected ambiguous values in settings.
+
+v1.0.0beta4
+Added experimental support for sorting content by date, if the date is part of the title.
+
+v1.0.0beta3
+Fixed problem with some older catchup content, which was not found correctly.
+
+v1.0.0beta2
+Fixed typo in settings.
+Fixed live TV for News, Parliament and Alba.
+
+v1.0.0beta1
+Initial release.
+programme names.
+Fixed HTML tags showing up in some Highlights programme names.
+
+v1.2.3
+Fixed various bugs in personalisation features.
+Improved cookie handling.
+Make sure the user data directory exists.
+
+v1.2.2
+Further localization of messages.
+Fixed bugs in translations.
+
+v1.2.1
+Localized main menu entries and pop-up messages.
+
+v1.2.0
+Added personalisation features.
+Fixed S4C live TV.
+Final tweaks and fixes for Highlights section.
+Added error pop-ups for requests.
+
+v1.1.2
+Adapted to new layout of Highlights page.
+Part of this is still experimental and may need further fixes.
+
+v1.1.1
+Fixed bug in settings dialog.
+
+v1.1.0
+Added first aired date for episodes.
+Redesigned user interface for manual stream selection and preferences dialog.
+Cleaned up unneccesary code.
+Improved error dialog.
+
+v1.0.1
+Fixup code to pep8 standard.
+Convert to docstrings.
+Fixed typo.
+Added error dialog.
+
+v1.0.0
+Corrected a few typos.
+
+v1.0.0rc4
+Added additional sources for catchup content.
+Adapted to new website layout in Highlights section.
+
+v1.0.0rc3
+Fixed bug in bitrate selection of live TV and catchup content.
+
+v1.0.0rc2
+Added regional channels for live TV.
+Fixed display of icons for live TV.
+Fixed bug in bitrate selection of live TV.
+Fixed bug in bitrate selection of some catchup content.
+
+v1.0.0rc1
+Fixed bug in category and search view: Programmes with multiple episodes did not show up correctly.
+
+v1.0.0beta5
+Moved from urllib2 to requests (adds dependency to requests add-on from official repo).
+Corrected ambiguous values in settings.
+
+v1.0.0beta4
+Added experimental support for sorting content by date, if the date is part of the title.
+
+v1.0.0beta3
+Fixed problem with some older catchup content, which was not found correctly.
+
+v1.0.0beta2
+Fixed typo in settings.
+Fixed live TV for News, Parliament and Alba.
+
+v1.0.0beta1
+Initial release.
+ </news>
+ </extension>
+</addon>
diff --git a/plugin.video.iplayerwww/default.py b/plugin.video.iplayerwww/default.py
new file mode 100644
index 0000000..47d3eb9
--- /dev/null
+++ b/plugin.video.iplayerwww/default.py
@@ -0,0 +1,226 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import division
+
+import os
+import sys
+import urllib
+
+import xbmc
+import xbmcaddon
+import xbmcgui
+import xbmcplugin
+
+
+plugin_handle = int(sys.argv[1])
+ADDON = xbmcaddon.Addon(id='plugin.video.iplayerwww')
+sys.path.insert(0, os.path.join(ADDON.getAddonInfo("path"), 'resources', 'lib'))
+
+try:
+ import ipwww_common as Common
+ from ipwww_common import utf8_unquote_plus, CreateBaseDirectory, KidsMode
+ import ipwww_video as Video
+ import ipwww_radio as Radio
+except ImportError, error:
+ d = xbmcgui.Dialog()
+ d.ok(str(error), 'Please check you installed this plugin correctly.')
+ raise
+
+
+def get_params():
+ param = []
+ paramstring = sys.argv[2]
+ if len(paramstring) >= 2:
+ params = sys.argv[2]
+ cleanedparams = params.replace('?', '')
+ if (params[len(params) - 1] == '/'):
+ params = params[0:len(params) - 2]
+ pairsofparams = cleanedparams.split('&')
+ param = {}
+ for i in range(len(pairsofparams)):
+ splitparams = {}
+ splitparams = pairsofparams[i].split('=')
+ if (len(splitparams)) == 2:
+ param[splitparams[0]] = splitparams[1]
+ return param
+
+
+params = get_params()
+content_type = None
+url = None
+name = None
+mode = None
+iconimage = None
+description = None
+subtitles_url = None
+logged_in = False
+keyword = None
+
+
+try:
+ content_type = utf8_unquote_plus(params["content_type"])
+except:
+ pass
+try:
+ url = utf8_unquote_plus(params["url"])
+except:
+ pass
+try:
+ name = utf8_unquote_plus(params["name"])
+except:
+ pass
+try:
+ iconimage = utf8_unquote_plus(params["iconimage"])
+except:
+ pass
+try:
+ mode = int(params["mode"])
+except:
+ pass
+try:
+ description = utf8_unquote_plus(params["description"])
+except:
+ pass
+try:
+ subtitles_url = utf8_unquote_plus(params["subtitles_url"])
+except:
+ pass
+try:
+ logged_in = params['logged_in'] == 'True'
+except:
+ pass
+try:
+ keyword = utf8_unquote_plus(params["keyword"])
+except:
+ pass
+
+# These are the modes which tell the plugin where to go.
+if mode == 1:
+ KidsMode()
+
+elif mode is None or url is None or len(url) < 1:
+ CreateBaseDirectory(content_type)
+
+# Modes 101-119 will create a main directory menu entry
+elif mode == 101:
+ Video.ListLive()
+
+elif mode == 102:
+ Video.ListAtoZ()
+
+elif mode == 103:
+ Video.ListCategories()
+
+elif mode == 104:
+ Video.Search(keyword)
+
+elif mode == 105:
+ Video.ListMostPopular()
+
+elif mode == 106:
+ Video.ListHighlights(url)
+
+elif mode == 107:
+ Video.ListWatching(logged_in)
+
+elif mode == 108:
+ Video.ListFavourites(logged_in)
+
+elif mode == 109:
+ Video.ListChannelHighlights()
+
+elif mode == 112:
+ Radio.ListAtoZ()
+
+elif mode == 113:
+ Radio.ListLive()
+
+elif mode == 114:
+ Radio.ListGenres()
+
+elif mode == 115:
+ Radio.Search(keyword)
+
+elif mode == 116:
+ Radio.ListMostPopular()
+
+elif mode == 117:
+ Radio.ListListenList(logged_in)
+
+elif mode == 199:
+ Radio.ListFollowing(logged_in)
+
+elif mode == 118:
+ Video.RedButtonDialog()
+
+elif mode == 119:
+ Common.SignOutBBCiD()
+
+elif mode == 120:
+ Video.ListChannelAtoZ()
+
+ # Modes 121-199 will create a sub directory menu entry
+elif mode == 121:
+ Video.GetEpisodes(url)
+
+elif mode == 122:
+ Video.GetAvailableStreams(name, url, iconimage, description)
+
+elif mode == 123:
+ Video.AddAvailableLiveStreamsDirectory(name, url, iconimage)
+
+elif mode == 124:
+ Video.GetAtoZPage(url)
+
+elif mode == 125:
+ Video.ListCategoryFilters(url)
+
+elif mode == 126:
+ Video.GetFilteredCategory(url)
+
+elif mode == 127:
+ Video.GetGroup(url)
+
+elif mode == 128:
+ Video.ScrapeEpisodes(url)
+
+elif mode == 129:
+ Video.AddAvailableRedButtonDirectory(name, url)
+
+elif mode == 131:
+ Radio.GetEpisodes(url)
+
+elif mode == 132:
+ Radio.GetAvailableStreams(name, url, iconimage, description)
+
+elif mode == 133:
+ Radio.AddAvailableLiveStreamsDirectory(name, url, iconimage)
+
+elif mode == 136:
+ Radio.GetPage(url)
+
+# Modes 201-299 will create a playable menu entry, not a directory
+elif mode == 201:
+ Video.PlayStream(name, url, iconimage, description, subtitles_url)
+
+elif mode == 202:
+ Video.AddAvailableStreamItem(name, url, iconimage, description)
+
+elif mode == 203:
+ Video.AddAvailableLiveStreamItemSelector(name, url, iconimage)
+
+elif mode == 204:
+ Video.AddAvailableRedButtonItem(name, url)
+
+elif mode == 211:
+ Radio.PlayStream(name, url, iconimage, description, subtitles_url)
+
+elif mode == 212:
+ Radio.AddAvailableStreamItem(name, url, iconimage, description)
+
+elif mode == 213:
+ Radio.AddAvailableLiveStreamItem(name, url, iconimage)
+
+
+
+xbmcplugin.endOfDirectory(int(sys.argv[1]))
diff --git a/plugin.video.iplayerwww/media/LICENSE.txt b/plugin.video.iplayerwww/media/LICENSE.txt
new file mode 100644
index 0000000..b0fd9ab
--- /dev/null
+++ b/plugin.video.iplayerwww/media/LICENSE.txt
@@ -0,0 +1,6 @@
+Icons made by:
+- Freepik http://www.flaticon.com/authors/freepik
+- Icons8 http://www.flaticon.com/authors/icons8
+- Scott de Jonge http://www.flaticon.com/authors/scott-de-jonge
+- Icomoon http://www.flaticon.com/authors/icomoon
+- Anton Saputro http://www.flaticon.com/authors/anton-saputro from www.flaticon.com is licensed by CC BY 3.0 (http://creativecommons.org/licenses/by/3.0/)
diff --git a/plugin.video.iplayerwww/media/bbc_1xtra.png b/plugin.video.iplayerwww/media/bbc_1xtra.png
new file mode 100644
index 0000000..520eaf6
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_1xtra.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_6music.png b/plugin.video.iplayerwww/media/bbc_6music.png
new file mode 100644
index 0000000..10935a0
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_6music.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_alba.png b/plugin.video.iplayerwww/media/bbc_alba.png
new file mode 100644
index 0000000..4af56bd
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_alba.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_asian_network.png b/plugin.video.iplayerwww/media/bbc_asian_network.png
new file mode 100644
index 0000000..a6282d6
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_asian_network.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_four_hd.png b/plugin.video.iplayerwww/media/bbc_four_hd.png
new file mode 100644
index 0000000..b3e30c2
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_four_hd.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_london.png b/plugin.video.iplayerwww/media/bbc_london.png
new file mode 100644
index 0000000..a69eb81
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_london.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_news24.png b/plugin.video.iplayerwww/media/bbc_news24.png
new file mode 100644
index 0000000..cd43c66
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_news24.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_cambridge.png b/plugin.video.iplayerwww/media/bbc_one_cambridge.png
new file mode 100644
index 0000000..37845f9
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_cambridge.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_channel_islands.png b/plugin.video.iplayerwww/media/bbc_one_channel_islands.png
new file mode 100644
index 0000000..0d81d1d
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_channel_islands.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_east.png b/plugin.video.iplayerwww/media/bbc_one_east.png
new file mode 100644
index 0000000..7ab0755
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_east.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_east_midlands.png b/plugin.video.iplayerwww/media/bbc_one_east_midlands.png
new file mode 100644
index 0000000..5ebad44
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_east_midlands.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_east_yorkshire.png b/plugin.video.iplayerwww/media/bbc_one_east_yorkshire.png
new file mode 100644
index 0000000..f26c48a
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_east_yorkshire.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_hd.png b/plugin.video.iplayerwww/media/bbc_one_hd.png
new file mode 100644
index 0000000..7b885bd
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_hd.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_london.png b/plugin.video.iplayerwww/media/bbc_one_london.png
new file mode 100644
index 0000000..f0970e2
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_london.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_north_east.png b/plugin.video.iplayerwww/media/bbc_one_north_east.png
new file mode 100644
index 0000000..7d0b111
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_north_east.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_north_west.png b/plugin.video.iplayerwww/media/bbc_one_north_west.png
new file mode 100644
index 0000000..5098f10
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_north_west.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_northern_ireland_hd.png b/plugin.video.iplayerwww/media/bbc_one_northern_ireland_hd.png
new file mode 100644
index 0000000..a9bd0fa
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_northern_ireland_hd.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_oxford.png b/plugin.video.iplayerwww/media/bbc_one_oxford.png
new file mode 100644
index 0000000..9372e92
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_oxford.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_scotland_hd.png b/plugin.video.iplayerwww/media/bbc_one_scotland_hd.png
new file mode 100644
index 0000000..3fd8c0c
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_scotland_hd.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_south.png b/plugin.video.iplayerwww/media/bbc_one_south.png
new file mode 100644
index 0000000..2081b8d
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_south.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_south_east.png b/plugin.video.iplayerwww/media/bbc_one_south_east.png
new file mode 100644
index 0000000..9876903
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_south_east.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_south_west.png b/plugin.video.iplayerwww/media/bbc_one_south_west.png
new file mode 100644
index 0000000..7791902
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_south_west.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_wales_hd.png b/plugin.video.iplayerwww/media/bbc_one_wales_hd.png
new file mode 100644
index 0000000..813c1a9
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_wales_hd.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_west.png b/plugin.video.iplayerwww/media/bbc_one_west.png
new file mode 100644
index 0000000..2af47c1
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_west.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_west_midlands.png b/plugin.video.iplayerwww/media/bbc_one_west_midlands.png
new file mode 100644
index 0000000..7e812a6
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_west_midlands.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_one_yorks.png b/plugin.video.iplayerwww/media/bbc_one_yorks.png
new file mode 100644
index 0000000..beb5249
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_one_yorks.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_parliament.png b/plugin.video.iplayerwww/media/bbc_parliament.png
new file mode 100644
index 0000000..2d2b05b
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_parliament.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_berkshire.png b/plugin.video.iplayerwww/media/bbc_radio_berkshire.png
new file mode 100644
index 0000000..3abc70b
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_berkshire.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_bristol.png b/plugin.video.iplayerwww/media/bbc_radio_bristol.png
new file mode 100644
index 0000000..d39aa4c
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_bristol.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_cambridge.png b/plugin.video.iplayerwww/media/bbc_radio_cambridge.png
new file mode 100644
index 0000000..01b4cf3
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_cambridge.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_cornwall.png b/plugin.video.iplayerwww/media/bbc_radio_cornwall.png
new file mode 100644
index 0000000..b71e523
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_cornwall.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_coventry_warwickshire.png b/plugin.video.iplayerwww/media/bbc_radio_coventry_warwickshire.png
new file mode 100644
index 0000000..547c6f3
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_coventry_warwickshire.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_cumbria.png b/plugin.video.iplayerwww/media/bbc_radio_cumbria.png
new file mode 100644
index 0000000..8559c2d
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_cumbria.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_cymru.png b/plugin.video.iplayerwww/media/bbc_radio_cymru.png
new file mode 100644
index 0000000..122d623
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_cymru.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_derby.png b/plugin.video.iplayerwww/media/bbc_radio_derby.png
new file mode 100644
index 0000000..5530e06
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_derby.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_devon.png b/plugin.video.iplayerwww/media/bbc_radio_devon.png
new file mode 100644
index 0000000..a35ab0d
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_devon.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_essex.png b/plugin.video.iplayerwww/media/bbc_radio_essex.png
new file mode 100644
index 0000000..80260e5
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_essex.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_five_live.png b/plugin.video.iplayerwww/media/bbc_radio_five_live.png
new file mode 100644
index 0000000..7a3b27f
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_five_live.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_five_live_sports_extra.png b/plugin.video.iplayerwww/media/bbc_radio_five_live_sports_extra.png
new file mode 100644
index 0000000..6ec713d
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_five_live_sports_extra.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_four_extra.png b/plugin.video.iplayerwww/media/bbc_radio_four_extra.png
new file mode 100644
index 0000000..a4513be
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_four_extra.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_fourfm.png b/plugin.video.iplayerwww/media/bbc_radio_fourfm.png
new file mode 100644
index 0000000..780166f
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_fourfm.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_fourlw.png b/plugin.video.iplayerwww/media/bbc_radio_fourlw.png
new file mode 100644
index 0000000..69b48d1
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_fourlw.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_foyle.png b/plugin.video.iplayerwww/media/bbc_radio_foyle.png
new file mode 100644
index 0000000..53b5f65
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_foyle.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_gloucestershire.png b/plugin.video.iplayerwww/media/bbc_radio_gloucestershire.png
new file mode 100644
index 0000000..9e9b50f
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_gloucestershire.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_guernsey.png b/plugin.video.iplayerwww/media/bbc_radio_guernsey.png
new file mode 100644
index 0000000..c4b7f59
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_guernsey.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_hereford_worcester.png b/plugin.video.iplayerwww/media/bbc_radio_hereford_worcester.png
new file mode 100644
index 0000000..90b86b7
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_hereford_worcester.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_humberside.png b/plugin.video.iplayerwww/media/bbc_radio_humberside.png
new file mode 100644
index 0000000..0630f4a
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_humberside.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_jersey.png b/plugin.video.iplayerwww/media/bbc_radio_jersey.png
new file mode 100644
index 0000000..4ca4938
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_jersey.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_kent.png b/plugin.video.iplayerwww/media/bbc_radio_kent.png
new file mode 100644
index 0000000..d7e6aaf
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_kent.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_lancashire.png b/plugin.video.iplayerwww/media/bbc_radio_lancashire.png
new file mode 100644
index 0000000..c2a6a67
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_lancashire.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_leeds.png b/plugin.video.iplayerwww/media/bbc_radio_leeds.png
new file mode 100644
index 0000000..d860393
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_leeds.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_leicester.png b/plugin.video.iplayerwww/media/bbc_radio_leicester.png
new file mode 100644
index 0000000..a3e4224
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_leicester.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_lincolnshire.png b/plugin.video.iplayerwww/media/bbc_radio_lincolnshire.png
new file mode 100644
index 0000000..7e240a3
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_lincolnshire.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_manchester.png b/plugin.video.iplayerwww/media/bbc_radio_manchester.png
new file mode 100644
index 0000000..aa33b2c
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_manchester.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_merseyside.png b/plugin.video.iplayerwww/media/bbc_radio_merseyside.png
new file mode 100644
index 0000000..87caf09
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_merseyside.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_nan_gaidheal.png b/plugin.video.iplayerwww/media/bbc_radio_nan_gaidheal.png
new file mode 100644
index 0000000..56df81b
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_nan_gaidheal.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_newcastle.png b/plugin.video.iplayerwww/media/bbc_radio_newcastle.png
new file mode 100644
index 0000000..069572a
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_newcastle.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_norfolk.png b/plugin.video.iplayerwww/media/bbc_radio_norfolk.png
new file mode 100644
index 0000000..cd6dcc0
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_norfolk.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_northampton.png b/plugin.video.iplayerwww/media/bbc_radio_northampton.png
new file mode 100644
index 0000000..82019c8
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_northampton.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_nottingham.png b/plugin.video.iplayerwww/media/bbc_radio_nottingham.png
new file mode 100644
index 0000000..bbf686b
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_nottingham.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_one.png b/plugin.video.iplayerwww/media/bbc_radio_one.png
new file mode 100644
index 0000000..952e212
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_one.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_oxford.png b/plugin.video.iplayerwww/media/bbc_radio_oxford.png
new file mode 100644
index 0000000..222e029
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_oxford.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_scotland_fm.png b/plugin.video.iplayerwww/media/bbc_radio_scotland_fm.png
new file mode 100644
index 0000000..d0e1d4a
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_scotland_fm.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_sheffield.png b/plugin.video.iplayerwww/media/bbc_radio_sheffield.png
new file mode 100644
index 0000000..fec0e3d
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_sheffield.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_shropshire.png b/plugin.video.iplayerwww/media/bbc_radio_shropshire.png
new file mode 100644
index 0000000..9bf3a39
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_shropshire.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_solent.png b/plugin.video.iplayerwww/media/bbc_radio_solent.png
new file mode 100644
index 0000000..dffab14
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_solent.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_somerset_sound.png b/plugin.video.iplayerwww/media/bbc_radio_somerset_sound.png
new file mode 100644
index 0000000..b07bf2c
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_somerset_sound.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_stoke.png b/plugin.video.iplayerwww/media/bbc_radio_stoke.png
new file mode 100644
index 0000000..a88fd89
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_stoke.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_suffolk.png b/plugin.video.iplayerwww/media/bbc_radio_suffolk.png
new file mode 100644
index 0000000..4417854
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_suffolk.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_surrey.png b/plugin.video.iplayerwww/media/bbc_radio_surrey.png
new file mode 100644
index 0000000..b64c709
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_surrey.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_sussex.png b/plugin.video.iplayerwww/media/bbc_radio_sussex.png
new file mode 100644
index 0000000..a8a688b
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_sussex.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_three.png b/plugin.video.iplayerwww/media/bbc_radio_three.png
new file mode 100644
index 0000000..dfb0b49
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_three.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_two.png b/plugin.video.iplayerwww/media/bbc_radio_two.png
new file mode 100644
index 0000000..cffb9f4
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_two.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_ulster.png b/plugin.video.iplayerwww/media/bbc_radio_ulster.png
new file mode 100644
index 0000000..68ed867
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_ulster.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_wales_fm.png b/plugin.video.iplayerwww/media/bbc_radio_wales_fm.png
new file mode 100644
index 0000000..c61a5a3
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_wales_fm.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_wiltshire.png b/plugin.video.iplayerwww/media/bbc_radio_wiltshire.png
new file mode 100644
index 0000000..d92d7d9
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_wiltshire.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_radio_york.png b/plugin.video.iplayerwww/media/bbc_radio_york.png
new file mode 100644
index 0000000..131de89
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_radio_york.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_tees.png b/plugin.video.iplayerwww/media/bbc_tees.png
new file mode 100644
index 0000000..27d847e
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_tees.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_three_counties_radio.png b/plugin.video.iplayerwww/media/bbc_three_counties_radio.png
new file mode 100644
index 0000000..7fbbf9a
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_three_counties_radio.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_three_hd.png b/plugin.video.iplayerwww/media/bbc_three_hd.png
new file mode 100644
index 0000000..ee5d366
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_three_hd.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_two_england.png b/plugin.video.iplayerwww/media/bbc_two_england.png
new file mode 100644
index 0000000..cd40faa
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_two_england.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_two_hd.png b/plugin.video.iplayerwww/media/bbc_two_hd.png
new file mode 100644
index 0000000..19e4e1f
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_two_hd.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_two_northern_ireland_digital.png b/plugin.video.iplayerwww/media/bbc_two_northern_ireland_digital.png
new file mode 100644
index 0000000..fe18a8b
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_two_northern_ireland_digital.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_two_scotland.png b/plugin.video.iplayerwww/media/bbc_two_scotland.png
new file mode 100644
index 0000000..3e9744e
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_two_scotland.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_two_wales_digital.png b/plugin.video.iplayerwww/media/bbc_two_wales_digital.png
new file mode 100644
index 0000000..6471c46
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_two_wales_digital.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/bbc_wm.png b/plugin.video.iplayerwww/media/bbc_wm.png
new file mode 100644
index 0000000..0813524
--- /dev/null
+++ b/plugin.video.iplayerwww/media/bbc_wm.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/cbbc_hd.png b/plugin.video.iplayerwww/media/cbbc_hd.png
new file mode 100644
index 0000000..3737bd2
--- /dev/null
+++ b/plugin.video.iplayerwww/media/cbbc_hd.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/cbeebies_hd.png b/plugin.video.iplayerwww/media/cbeebies_hd.png
new file mode 100644
index 0000000..d80a657
--- /dev/null
+++ b/plugin.video.iplayerwww/media/cbeebies_hd.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/favourites.png b/plugin.video.iplayerwww/media/favourites.png
new file mode 100644
index 0000000..402081a
--- /dev/null
+++ b/plugin.video.iplayerwww/media/favourites.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/lists.png b/plugin.video.iplayerwww/media/lists.png
new file mode 100644
index 0000000..98aefd2
--- /dev/null
+++ b/plugin.video.iplayerwww/media/lists.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/live.png b/plugin.video.iplayerwww/media/live.png
new file mode 100644
index 0000000..ca760b0
--- /dev/null
+++ b/plugin.video.iplayerwww/media/live.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/popular.png b/plugin.video.iplayerwww/media/popular.png
new file mode 100644
index 0000000..a924919
--- /dev/null
+++ b/plugin.video.iplayerwww/media/popular.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/red_button.png b/plugin.video.iplayerwww/media/red_button.png
new file mode 100644
index 0000000..3b251cd
--- /dev/null
+++ b/plugin.video.iplayerwww/media/red_button.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/s4cpbs.png b/plugin.video.iplayerwww/media/s4cpbs.png
new file mode 100644
index 0000000..cc6487b
--- /dev/null
+++ b/plugin.video.iplayerwww/media/s4cpbs.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/search.png b/plugin.video.iplayerwww/media/search.png
new file mode 100644
index 0000000..ccf3d0b
--- /dev/null
+++ b/plugin.video.iplayerwww/media/search.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/settings.png b/plugin.video.iplayerwww/media/settings.png
new file mode 100644
index 0000000..aa1e7d5
--- /dev/null
+++ b/plugin.video.iplayerwww/media/settings.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/top_rated.png b/plugin.video.iplayerwww/media/top_rated.png
new file mode 100644
index 0000000..6131e76
--- /dev/null
+++ b/plugin.video.iplayerwww/media/top_rated.png
Binary files differ
diff --git a/plugin.video.iplayerwww/media/tv.png b/plugin.video.iplayerwww/media/tv.png
new file mode 100644
index 0000000..1b7e392
--- /dev/null
+++ b/plugin.video.iplayerwww/media/tv.png
Binary files differ
diff --git a/plugin.video.iplayerwww/resources/fanart.jpg b/plugin.video.iplayerwww/resources/fanart.jpg
new file mode 100644
index 0000000..4b88d15
--- /dev/null
+++ b/plugin.video.iplayerwww/resources/fanart.jpg
Binary files differ
diff --git a/plugin.video.iplayerwww/resources/icon.png b/plugin.video.iplayerwww/resources/icon.png
new file mode 100644
index 0000000..7b111e8
--- /dev/null
+++ b/plugin.video.iplayerwww/resources/icon.png
Binary files differ
diff --git a/plugin.video.iplayerwww/resources/language/English/strings.po b/plugin.video.iplayerwww/resources/language/English/strings.po
new file mode 100644
index 0000000..fbede79
--- /dev/null
+++ b/plugin.video.iplayerwww/resources/language/English/strings.po
@@ -0,0 +1,412 @@
+# Kodi Media Center language file
+# Addon Name: iPlayer WWW
+# Addon id: plugin.video.iplayerwww
+# Addon Provider: CaptainT|primaeval|ihurst|Cas
+msgid ""
+msgstr ""
+"Project-Id-Version: Kodi Addons\n"
+"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n"
+"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Kodi Translation Team\n"
+"Language-Team: English (http://www.transifex.com/projects/p/xbmc-addons/language/en/)\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=2; plural=(n != 1);\n"
+
+msgctxt "#30100"
+msgid "General"
+msgstr ""
+
+msgctxt "#30101"
+msgid "Video settings"
+msgstr ""
+
+msgctxt "#30102"
+msgid "Radio settings"
+msgstr ""
+
+msgctxt "#30103"
+msgid "Common settings"
+msgstr ""
+
+msgctxt "#30110"
+msgid "Play Signed programmes when available"
+msgstr ""
+
+msgctxt "#30120"
+msgid "Play Audio Described programmes when available"
+msgstr ""
+
+msgctxt "#30130"
+msgid "Use subtitles when available"
+msgstr ""
+
+msgctxt "#30140"
+msgid "Enable BBC iD"
+msgstr ""
+
+msgctxt "#30141"
+msgid "BBC Username"
+msgstr ""
+
+msgctxt "#30142"
+msgid "BBC Password"
+msgstr ""
+
+msgctxt "#30143"
+msgid "Sign in automatically"
+msgstr ""
+
+msgctxt "#30150"
+msgid "Suppress programmes with incomplete information in Highlights"
+msgstr ""
+
+msgctxt "#30160"
+msgid "Behaviour for getting A-Z"
+msgstr ""
+
+msgctxt "#30161"
+msgid "By character"
+msgstr ""
+
+msgctxt "#30162"
+msgid "All at once"
+msgstr ""
+
+msgctxt "#30170"
+msgid "Behaviour for getting programmes"
+msgstr ""
+
+msgctxt "#30171"
+msgid "By page"
+msgstr ""
+
+msgctxt "#30172"
+msgid "All at once"
+msgstr ""
+
+msgctxt "#30180"
+msgid "Switch CBeebies/CBBC only mode on/off"
+msgstr ""
+
+msgctxt "#30181"
+msgid "Enter old password:"
+msgstr ""
+
+msgctxt "#30182"
+msgid "Enter new password (empty to unlock):"
+msgstr ""
+
+msgctxt "#30190"
+msgid "Show Red Button warning"
+msgstr ""
+
+msgstr ""
+msgctxt "#30200"
+msgid "Streams"
+msgstr ""
+
+msgctxt "#30205"
+msgid "Play stream automatically using these settings"
+msgstr ""
+
+msgctxt "#30210"
+msgid "Catchup CDN source"
+msgstr ""
+
+msgctxt "#30211"
+msgid "Catchup bitrate"
+msgstr ""
+
+msgctxt "#30212"
+msgid "Stream protocol"
+msgstr ""
+
+msgctxt "#30220"
+msgid "Live CDN source"
+msgstr ""
+
+msgctxt "#30230"
+msgid "Live bitrate"
+msgstr ""
+
+msgctxt "#30231"
+msgid "Radio CDN source"
+msgstr ""
+
+msgctxt "#30232"
+msgid "Radio location"
+msgstr ""
+
+msgctxt "#30233"
+msgid "Max Live Radio bitrate"
+msgstr ""
+
+
+msgctxt "#30300"
+msgid "Highlights"
+msgstr ""
+
+msgctxt "#30301"
+msgid "Most Popular"
+msgstr ""
+
+msgctxt "#30302"
+msgid "Programme List A-Z"
+msgstr ""
+
+msgctxt "#30303"
+msgid "Categories"
+msgstr ""
+
+msgctxt "#30304"
+msgid "Search"
+msgstr ""
+
+msgctxt "#30305"
+msgid "Watch Live"
+msgstr ""
+
+msgctxt "#30306"
+msgid "Watching"
+msgstr ""
+
+msgctxt "#30307"
+msgid "Added"
+msgstr ""
+
+msgctxt "#30308"
+msgid "BBCiD Sign In"
+msgstr ""
+
+msgctxt "#30309"
+msgid "Successful"
+msgstr ""
+
+msgctxt "#30310"
+msgid "Failed - check settings"
+msgstr ""
+
+msgctxt "#30311"
+msgid "BBCiD set-up required, please check settings."
+msgstr ""
+
+msgctxt "#30312"
+msgid "Do you wish to sign in?"
+msgstr ""
+
+msgctxt "#30313"
+msgid "available episodes"
+msgstr ""
+
+msgctxt "#30314"
+msgid "Collection"
+msgstr ""
+
+msgctxt "#30315"
+msgid "available programmes"
+msgstr ""
+
+msgctxt "#30316"
+msgid "This programme is part of the collection"
+msgstr ""
+
+msgctxt "#30317"
+msgid "Channel Highlights"
+msgstr ""
+
+msgctxt "#30318"
+msgid "View all matching episodes"
+msgstr ""
+
+msgctxt "#30319"
+msgid "Finding episodes..."
+msgstr ""
+
+msgctxt "#30320"
+msgid "Next page"
+msgstr ""
+
+msgctxt "#30321"
+msgid "Listen Live"
+msgstr ""
+
+msgctxt "#30322"
+msgid "Listen Live High Quality"
+msgstr ""
+
+msgctxt "#30323"
+msgid "iPlayer: "
+msgstr ""
+
+msgctxt "#30324"
+msgid "Radio: "
+msgstr ""
+
+msgctxt "#30325"
+msgid "Sign out BBCiD"
+msgstr ""
+
+msgctxt "#30326"
+msgid "BBCiD Sign Out"
+msgstr ""
+
+msgctxt "#30327"
+msgid "Channel A-Z"
+msgstr ""
+
+msgctxt "#30328"
+msgid "Red Button"
+msgstr ""
+
+msgctxt "#30329"
+msgid "CBeebies Live"
+msgstr ""
+
+msgctxt "#30330"
+msgid "CBBC Live"
+msgstr ""
+
+msgctxt "#30331"
+msgid "CBeebies A-Z"
+msgstr ""
+
+msgctxt "#30332"
+msgid "CBBC A-Z"
+msgstr ""
+
+msgctxt "#30333"
+msgid "CBeebies Radio"
+msgstr ""
+
+msgctxt "#30334"
+msgid "Following"
+msgstr ""
+
+msgctxt "#30400"
+msgid "Error"
+msgstr ""
+
+msgctxt "#30401"
+msgid "BBC iPlayer TV programmes are available to play in the UK only."
+msgstr ""
+
+msgctxt "#30402"
+msgid "Cookie Load Failed"
+msgstr ""
+
+msgctxt "#30403"
+msgid "Playback failed"
+msgstr ""
+
+msgctxt "#30404"
+msgid "Can't find stream."
+msgstr ""
+
+msgctxt "#30405"
+msgid "Warning"
+msgstr ""
+
+msgctxt "#30406"
+msgid "Red Button channels may be unavailable depending on their schedule."
+msgstr ""
+
+msgctxt "#30407"
+msgid "Usage of Red Button is at your own risk."
+msgstr ""
+
+msgctxt "#30408"
+msgid "Agree"
+msgstr ""
+
+msgctxt "#30409"
+msgid "Disagree"
+msgstr ""
+
+msgctxt "#30410"
+msgid "To watch iPlayer TV content legally, you need a UK TV Licence."
+msgstr ""
+
+msgctxt "#30500"
+msgid "Display"
+msgstr ""
+
+msgctxt "#30510"
+msgid "Select main menu entries"
+msgstr ""
+
+msgctxt "#30511"
+msgid "iPlayer: Highlights"
+msgstr ""
+
+msgctxt "#30512"
+msgid "iPlayer: Channel Highlights"
+msgstr ""
+
+msgctxt "#30513"
+msgid "iPlayer: Most Popular"
+msgstr ""
+
+msgctxt "#30514"
+msgid "iPlayer: Programme List A-Z"
+msgstr ""
+
+msgctxt "#30515"
+msgid "iPlayer: Channel A-Z"
+msgstr ""
+
+msgctxt "#30516"
+msgid "iPlayer: Categories"
+msgstr ""
+
+msgctxt "#30517"
+msgid "iPlayer: Search"
+msgstr ""
+
+msgctxt "#30518"
+msgid "iPlayer: Watch Live"
+msgstr ""
+
+msgctxt "#30519"
+msgid "iPlayer: Red Button"
+msgstr ""
+
+msgctxt "#30520"
+msgid "iPlayer: Watching"
+msgstr ""
+
+msgctxt "#30521"
+msgid "iPlayer: Added"
+msgstr ""
+
+msgctxt "#30522"
+msgid "Radio: Listen Live"
+msgstr ""
+
+msgctxt "#30523"
+msgid "Radio: Programme List A-Z"
+msgstr ""
+
+msgctxt "#30524"
+msgid "Radio: Categories"
+msgstr ""
+
+msgctxt "#30525"
+msgid "Radio: Search"
+msgstr ""
+
+msgctxt "#30526"
+msgid "Radio: Most Popular"
+msgstr ""
+
+msgctxt "#30527"
+msgid "Radio: Added"
+msgstr ""
+
+msgctxt "#30528"
+msgid "Radio: Following"
+msgstr ""
+
diff --git a/plugin.video.iplayerwww/resources/lib/ipwww_common.py b/plugin.video.iplayerwww/resources/lib/ipwww_common.py
new file mode 100644
index 0000000..ed46858
--- /dev/null
+++ b/plugin.video.iplayerwww/resources/lib/ipwww_common.py
@@ -0,0 +1,680 @@
+# -*- coding: utf-8 -*-
+
+import sys
+import os
+import re
+import requests
+from requests.packages import urllib3
+#Below is required to get around an ssl issue
+urllib3.disable_warnings()
+import cookielib
+import urllib
+import HTMLParser
+import codecs
+
+import xbmc
+import xbmcaddon
+import xbmcgui
+import xbmcplugin
+
+ADDON = xbmcaddon.Addon(id='plugin.video.iplayerwww')
+
+
+def GetAddonInfo():
+ addon_info = {}
+ addon_info["id"] = addonid
+ addon_info["addon"] = xbmcaddon.Addon(addonid)
+ addon_info["language"] = addon_info["addon"].getLocalizedString
+ addon_info["version"] = addon_info["addon"].getAddonInfo("version")
+ addon_info["path"] = addon_info["addon"].getAddonInfo("path")
+ addon_info["profile"] = xbmc.translatePath(addon_info["addon"].getAddonInfo('profile'))
+ return addon_info
+
+
+addonid = "plugin.video.iplayerwww"
+addoninfo = GetAddonInfo()
+DIR_USERDATA = xbmc.translatePath(addoninfo["profile"])
+cookie_jar = None
+user_agent = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:51.0) Gecko/20100101 Firefox/51.0'
+headers = {'User-Agent': user_agent}
+
+
+if(not os.path.exists(DIR_USERDATA)):
+ os.makedirs(DIR_USERDATA)
+
+
+def translation(id):
+ return xbmcaddon.Addon(addonid).getLocalizedString(id)
+
+
+re_subtitles = re.compile('^\s*<p.*?begin=\"(.*?)(\.([0-9]+))?\"\s+.*?end=\"(.*?)(\.([0-9]+))?\"\s*>(.*?)</p>')
+
+
+def ParseImageUrl(url):
+ return url.replace("{recipe}", "832x468")
+
+
+def download_subtitles(url):
+ # Download and Convert the TTAF format to srt
+ # SRT:
+ # 1
+ # 00:01:22,490 --> 00:01:26,494
+ # Next round!
+ #
+ # 2
+ # 00:01:33,710 --> 00:01:37,714
+ # Now that we've moved to paradise, there's nothing to eat.
+ #
+
+ # TT:
+ # <p begin="0:01:12.400" end="0:01:13.880">Thinking.</p>
+ outfile = os.path.join(DIR_USERDATA, 'iplayer.srt')
+ # print "Downloading subtitles from %s to %s"%(url, outfile)
+ fw = codecs.open(outfile, 'w', encoding='utf-8')
+
+ if not url:
+ fw.write("1\n0:00:00,001 --> 0:01:00,001\nNo subtitles available\n\n")
+ fw.close()
+ return
+
+ txt = OpenURL(url)
+
+ # print txt
+
+ i = 0
+ prev = None
+
+ # some of the subtitles are a bit rubbish in particular for live tv
+ # with lots of needless repeats. The follow code will collapse sequences
+ # of repeated subtitles into a single subtitles that covers the total time
+ # period. The downside of this is that it would mess up in the rare case
+ # where a subtitle actually needs to be repeated
+ for line in txt.split('\n'):
+ entry = None
+ m = re_subtitles.match(line)
+ # print line
+ # print m
+ if m:
+ if(m.group(3)):
+ start_mil = "%s000" % m.group(3) # pad out to ensure 3 digits
+ else:
+ start_mil = "000"
+ if(m.group(6)):
+ end_mil = "%s000" % m.group(6)
+ else:
+ end_mil = "000"
+
+ ma = {'start': m.group(1),
+ 'start_mil': start_mil[:3],
+ 'end': m.group(4),
+ 'end_mil': end_mil[:3],
+ 'text': m.group(7)}
+
+ # ma['text'] = ma['text'].replace('&amp;', '&')
+ # ma['text'] = ma['text'].replace('&gt;', '>')
+ # ma['text'] = ma['text'].replace('&lt;', '<')
+ ma['text'] = ma['text'].replace('<br />', '\n')
+ ma['text'] = ma['text'].replace('<br/>', '\n')
+ ma['text'] = re.sub('<.*?>', '', ma['text'])
+ ma['text'] = re.sub('&#[0-9]+;', '', ma['text'])
+ # ma['text'] = ma['text'].replace('<.*?>', '')
+ # print ma
+ if not prev:
+ # first match - do nothing wait till next line
+ prev = ma
+ continue
+
+ if prev['text'] == ma['text']:
+ # current line = previous line then start a sequence to be collapsed
+ prev['end'] = ma['end']
+ prev['end_mil'] = ma['end_mil']
+ else:
+ i += 1
+ entry = "%d\n%s,%s --> %s,%s\n%s\n\n" % (
+ i, prev['start'], prev['start_mil'], prev['end'], prev['end_mil'], prev['text'])
+ prev = ma
+ elif prev:
+ i += 1
+ entry = "%d\n%s,%s --> %s,%s\n%s\n\n" % (
+ i, prev['start'], prev['start_mil'], prev['end'], prev['end_mil'], prev['text'])
+
+ if entry:
+ fw.write(entry)
+
+ fw.close()
+ return outfile
+
+
+def InitialiseCookieJar():
+ cookie_file = os.path.join(DIR_USERDATA,'iplayer.cookies')
+ cj = cookielib.LWPCookieJar(cookie_file)
+ if(os.path.exists(cookie_file)):
+ try:
+ cj.load(ignore_discard=True)
+ except:
+ xbmcgui.Dialog().notification(translation(30400), translation(30402), xbmcgui.NOTIFICATION_ERROR)
+ return cj
+
+cookie_jar = InitialiseCookieJar()
+
+
+def SignInBBCiD():
+ sign_in_url="https://ssl.bbc.co.uk/id/signin"
+
+ username=ADDON.getSetting('bbc_id_username')
+ password=ADDON.getSetting('bbc_id_password')
+
+ post_data={
+ 'username': username,
+ 'password': password,
+ 'attempts':'0'}
+
+ #Regular expression to get 'nonce' from login page
+ p = re.compile('form method="post" action="([^""]*)"')
+
+ with requests.Session() as s:
+ resp = s.get('https://www.bbc.com/', headers=headers)
+
+ # Call the login page to get a 'nonce' for actual login
+ signInUrl = 'https://www.bbc.com/session'
+ resp = s.get(signInUrl, headers=headers)
+ m = p.search(resp.text)
+ url = m.group(1)
+
+ url = "https://www.bbc.com%s" % url
+ resp = s.post(url, data=post_data, headers=headers)
+
+ for cookie in s.cookies:
+ cookie_jar.set_cookie(cookie)
+ cookie_jar.save(ignore_discard=True)
+
+ with requests.Session() as s:
+ resp = s.get('https://www.bbc.co.uk/iplayer', headers=headers)
+
+ # Call the login page to get a 'nonce' for actual login
+ signInUrl = 'https://www.bbc.co.uk/session'
+ resp = s.get(signInUrl, headers=headers)
+ m = p.search(resp.text)
+ url = m.group(1)
+
+ url = "https://www.bbc.com%s" % url
+ resp = s.post(url, data=post_data, headers=headers)
+
+ for cookie in s.cookies:
+ cookie_jar.set_cookie(cookie)
+ cookie_jar.save(ignore_discard=True)
+
+ #if (r.status_code == 302):
+ # xbmcgui.Dialog().notification(translation(30308), translation(30309))
+ #else:
+ # xbmcgui.Dialog().notification(translation(30308), translation(30310))
+
+
+def SignOutBBCiD():
+ sign_out_url="https://ssl.bbc.co.uk/id/signout"
+ OpenURL(sign_out_url)
+ cookie_jar.clear()
+ cookie_jar.save()
+ if (StatusBBCiD()):
+ xbmcgui.Dialog().notification(translation(30326), translation(30310))
+ else:
+ xbmcgui.Dialog().notification(translation(30326), translation(30309))
+
+
+def StatusBBCiD():
+ r = requests.head("https://www.bbc.com/account", cookies=cookie_jar, allow_redirects=False)
+ if r.status_code == 200:
+ return True
+ else:
+ return False
+
+
+def CheckLogin(logged_in):
+ if(logged_in == True or StatusBBCiD() == True):
+ logged_in = True
+ return True
+ elif ADDON.getSetting('bbc_id_enabled') != 'true':
+ xbmcgui.Dialog().ok(translation(30308), translation(30311))
+ else:
+ if ADDON.getSetting('bbc_id_autologin') == 'true':
+ attemptLogin = True
+ else:
+ attemptLogin = xbmcgui.Dialog().yesno(translation(30308), translation(30312))
+ if attemptLogin:
+ SignInBBCiD()
+ if(StatusBBCiD()):
+ if ADDON.getSetting('bbc_id_autologin') == 'false':
+ xbmcgui.Dialog().notification(translation(30308), translation(30309))
+ logged_in = True;
+ return True;
+ else:
+ xbmcgui.Dialog().notification(translation(30308), translation(30310))
+
+ return False
+
+
+def OpenURL(url):
+ try:
+ r = requests.get(url, headers=headers, cookies=cookie_jar)
+ except requests.exceptions.RequestException as e:
+ dialog = xbmcgui.Dialog()
+ dialog.ok(translation(30400), "%s" % e)
+ sys.exit(1)
+ try:
+ for cookie in r.cookies:
+ cookie_jar.set_cookie(cookie)
+ #Set ignore_discard to overcome issue of not having session
+ #as cookie_jar is reinitialised for each action.
+ cookie_jar.save(ignore_discard=True)
+ except:
+ pass
+ return HTMLParser.HTMLParser().unescape(r.content.decode('utf-8'))
+
+
+def OpenURLPost(url, post_data):
+
+ headers_ssl = {
+ 'User-Agent': user_agent,
+ 'Host':'ssl.bbc.co.uk',
+ 'Accept':'*/*',
+ 'Referer':'https://ssl.bbc.co.uk/id/signin',
+ 'Content-Type':'application/x-www-form-urlencoded'}
+ try:
+ r = requests.post(url, headers=headers_ssl, data=post_data, allow_redirects=False,
+ cookies=cookie_jar)
+ except requests.exceptions.RequestException as e:
+ dialog = xbmcgui.Dialog()
+ dialog.ok(translation(30400), "%s" % e)
+ sys.exit(1)
+ try:
+ for cookie in r.cookies:
+ cookie_jar.set_cookie(cookie)
+ #Set ignore_discard to overcome issue of not having session
+ #as cookie_jar is reinitialised for each action.
+ cookie_jar.save(ignore_discard=True)
+ except:
+ pass
+ return r
+
+
+def GetCookieJar():
+ return cookie_jar
+
+
+# Creates a 'urlencoded' string from a unicode input
+def utf8_quote_plus(unicode):
+ return urllib.quote_plus(unicode.encode('utf-8'))
+
+
+# Gets a unicode string from a 'urlencoded' string
+def utf8_unquote_plus(str):
+ return urllib.unquote_plus(str).decode('utf-8')
+
+
+def AddMenuEntry(name, url, mode, iconimage, description, subtitles_url, aired=None, resolution=None, logged_in=False):
+ """Adds a new line to the Kodi list of playables.
+ It is used in multiple ways in the plugin, which are distinguished by modes.
+ """
+ if not iconimage:
+ iconimage="DefaultFolder.png"
+ listitem_url = (sys.argv[0] + "?url=" + utf8_quote_plus(url) + "&mode=" + str(mode) +
+ "&name=" + utf8_quote_plus(name) +
+ "&iconimage=" + utf8_quote_plus(iconimage) +
+ "&description=" + utf8_quote_plus(description) +
+ "&subtitles_url=" + utf8_quote_plus(subtitles_url) +
+ "&logged_in=" + str(logged_in))
+
+ if aired:
+ ymd = aired.split('-')
+ date_string = ymd[2] + '/' + ymd[1] + '/' + ymd[0]
+ else:
+ date_string = ""
+
+ # Modes 201-299 will create a new playable line, otherwise create a new directory line.
+ if mode in (201, 202, 203, 204, 211, 212, 213, 214):
+ isFolder = False
+ # Mode 119 is not a folder, but it is also not a playable.
+ elif mode == 119:
+ isFolder = False
+ else:
+ isFolder = True
+
+ listitem = xbmcgui.ListItem(label=name, label2=description,
+ iconImage="DefaultFolder.png", thumbnailImage=iconimage)
+ if aired:
+ listitem.setInfo("video", {
+ "title": name,
+ "plot": description,
+ "plotoutline": description,
+ "date": date_string,
+ "aired": aired})
+ else:
+ listitem.setInfo("video", {
+ "title": name,
+ "plot": description,
+ "plotoutline": description})
+
+ video_streaminfo = {'codec': 'h264'}
+ if not isFolder:
+ if int(ADDON.getSetting('stream_protocol')) == 0:
+ listitem.setPath(url)
+ listitem.setProperty('inputstreamaddon', 'inputstream.adaptive')
+ listitem.setProperty('inputstream.adaptive.manifest_type', 'mpd')
+ else:
+ if resolution:
+ resolution = resolution.split('x')
+ video_streaminfo['aspect'] = round(int(resolution[0]) / int(resolution[1]), 2)
+ video_streaminfo['width'] = resolution[0]
+ video_streaminfo['height'] = resolution[1]
+ listitem.addStreamInfo('video', video_streaminfo)
+ listitem.addStreamInfo('audio', {'codec': 'aac', 'language': 'en', 'channels': 2})
+ if subtitles_url:
+ listitem.addStreamInfo('subtitle', {'language': 'en'})
+
+ # Mode 119 is not a folder, but it is also not a playable.
+ if mode == 119:
+ listitem.setProperty("IsPlayable", 'false')
+ else:
+ listitem.setProperty("IsPlayable", str(not isFolder).lower())
+ listitem.setProperty("IsFolder", str(isFolder).lower())
+ listitem.setProperty("Property(Addon.Name)", "iPlayer WWW")
+ xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),
+ url=listitem_url, listitem=listitem, isFolder=isFolder)
+ xbmcplugin.setContent(int(sys.argv[1]), 'episodes')
+ return True
+
+def KidsMode():
+ dialog = xbmcgui.Dialog()
+ old_password = ''
+ try:
+ old_password = ADDON.getSetting('kids_password')
+ old_password = old_password.decode('base64', 'strict')
+ except:
+ pass
+ password = ''
+ if old_password:
+ password = dialog.input(translation(30181), type=xbmcgui.INPUT_ALPHANUM)
+ if old_password == password:
+ new_password = dialog.input(translation(30182), type=xbmcgui.INPUT_ALPHANUM)
+ ADDON.setSetting('kids_password',new_password.encode('base64','strict'))
+ quit()
+
+def ShowLicenceWarning():
+ if not (ADDON.getSetting("licence_warning_shown") == 'true'):
+ dialog = xbmcgui.Dialog()
+ ok = dialog.ok(translation(30405), translation(30410))
+ if ok:
+ ADDON.setSetting("licence_warning_shown", 'true')
+
+
+def CreateBaseDirectory(content_type):
+ if ADDON.getSetting('kids_password'):
+ if ADDON.getSetting('streams_autoplay') == 'true':
+ live_mode = 203
+ else:
+ live_mode = 123
+ AddMenuEntry(translation(30329), 'cbeebies_hd', live_mode,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/cbeebies.png'
+ ),
+ '', '')
+ AddMenuEntry(translation(30330), 'cbbc_hd', live_mode,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/cbbc.png'
+ ),
+ '', '')
+ AddMenuEntry(translation(30331), 'cbeebies', 125,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/cbeebies.png'
+ ),
+ '', '')
+ AddMenuEntry(translation(30332), 'cbbc', 125,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/cbbc.png'
+ ),
+ '', '')
+ AddMenuEntry(translation(30333), 'p02pnn9d', 131,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/cbeebies.png'
+ ),
+ '', '')
+ return
+
+ if content_type == "video":
+ ShowLicenceWarning()
+ if ADDON.getSetting("menu_video_highlights") == 'true':
+ AddMenuEntry(translation(30300), 'iplayer', 106,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/top_rated.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_channel_highlights") == 'true':
+ AddMenuEntry(translation(30317), 'url', 109,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/top_rated.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_most_popular") == 'true':
+ AddMenuEntry(translation(30301), 'url', 105,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/popular.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_az") == 'true':
+ AddMenuEntry(translation(30302), 'url', 102,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/lists.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_channel_az") == 'true':
+ AddMenuEntry(translation(30327), 'url', 120,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/lists.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_categories") == 'true':
+ AddMenuEntry(translation(30303), 'url', 103,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/lists.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_search") == 'true':
+ AddMenuEntry(translation(30304), 'url', 104,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/search.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_live") == 'true':
+ AddMenuEntry(translation(30305), 'url', 101,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/tv.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_red_button") == 'true':
+ AddMenuEntry(translation(30328), 'url', 118,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/tv.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_watching") == 'true':
+ AddMenuEntry(translation(30306), 'url', 107,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/favourites.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_added") == 'true':
+ AddMenuEntry(translation(30307), 'url', 108,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/favourites.png'
+ ),
+ '', '')
+ AddMenuEntry(translation(30325), 'url', 119,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/settings.png'
+ ),
+ '', '')
+ elif content_type == "audio":
+ if ADDON.getSetting("menu_radio_live") == 'true':
+ AddMenuEntry(translation(30321), 'url', 113,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/live.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_radio_az") == 'true':
+ AddMenuEntry(translation(30302), 'url', 112,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/lists.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_radio_categories") == 'true':
+ AddMenuEntry(translation(30303), 'url', 114,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/lists.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_radio_search") == 'true':
+ AddMenuEntry(translation(30304), 'url', 115,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/search.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_radio_most_popular") == 'true':
+ AddMenuEntry(translation(30301), 'url', 116,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/popular.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_radio_added") == 'true':
+ AddMenuEntry(translation(30307), 'url', 117,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/favourites.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_radio_following") == 'true':
+ AddMenuEntry(translation(30334), 'url', 199,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/favourites.png'
+ ),
+ '', '')
+ AddMenuEntry(translation(30325), 'url', 119,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/settings.png'
+ ),
+ '', '')
+ else:
+ ShowLicenceWarning()
+ if ADDON.getSetting("menu_video_highlights") == 'true':
+ AddMenuEntry((translation(30323)+translation(30300)), 'iplayer', 106,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/top_rated.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_channel_highlights") == 'true':
+ AddMenuEntry((translation(30323)+translation(30317)), 'url', 109,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/top_rated.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_most_popular") == 'true':
+ AddMenuEntry((translation(30323)+translation(30301)), 'url', 105,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/popular.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_az") == 'true':
+ AddMenuEntry((translation(30323)+translation(30302)), 'url', 102,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/lists.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_channel_az") == 'true':
+ AddMenuEntry((translation(30323)+translation(30327)), 'url', 120,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/lists.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_categories") == 'true':
+ AddMenuEntry((translation(30323)+translation(30303)), 'url', 103,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/lists.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_search") == 'true':
+ AddMenuEntry((translation(30323)+translation(30304)), 'url', 104,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/search.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_live") == 'true':
+ AddMenuEntry((translation(30323)+translation(30305)), 'url', 101,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/tv.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_red_button") == 'true':
+ AddMenuEntry((translation(30323)+translation(30328)), 'url', 118,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/tv.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_watching") == 'true':
+ AddMenuEntry((translation(30323)+translation(30306)), 'url', 107,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/favourites.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_video_added") == 'true':
+ AddMenuEntry((translation(30323)+translation(30307)), 'url', 108,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/favourites.png'
+ ),
+ '', '')
+
+ if ADDON.getSetting("menu_radio_live") == 'true':
+ AddMenuEntry((translation(30324)+translation(30321)), 'url', 113,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/live.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_radio_az") == 'true':
+ AddMenuEntry((translation(30324)+translation(30302)), 'url', 112,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/lists.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_radio_categories") == 'true':
+ AddMenuEntry((translation(30324)+translation(30303)), 'url', 114,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/lists.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_radio_search") == 'true':
+ AddMenuEntry((translation(30324)+translation(30304)), 'url', 115,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/search.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_radio_most_popular") == 'true':
+ AddMenuEntry((translation(30324)+translation(30301)), 'url', 116,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/popular.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_radio_added") == 'true':
+ AddMenuEntry((translation(30324)+translation(30307)), 'url', 117,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/favourites.png'
+ ),
+ '', '')
+ if ADDON.getSetting("menu_radio_following") == 'true':
+ AddMenuEntry((translation(30324)+translation(30334)), 'url', 199,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/favourites.png'
+ ),
+ '', '')
+ AddMenuEntry(translation(30325), 'url', 119,
+ xbmc.translatePath(
+ 'special://home/addons/plugin.video.iplayerwww/media/settings.png'
+ ),
+ '', '')
+
diff --git a/plugin.video.iplayerwww/resources/lib/ipwww_radio.py b/plugin.video.iplayerwww/resources/lib/ipwww_radio.py
new file mode 100644
index 0000000..e640f57
--- /dev/null
+++ b/plugin.video.iplayerwww/resources/lib/ipwww_radio.py
@@ -0,0 +1,625 @@
+# -*- coding: utf-8 -*-
+
+import sys
+import os
+import re
+from operator import itemgetter
+from ipwww_common import translation, AddMenuEntry, OpenURL, \
+ CheckLogin, CreateBaseDirectory
+
+import xbmc
+import xbmcgui
+import xbmcplugin
+import xbmcaddon
+import random
+
+ADDON = xbmcaddon.Addon(id='plugin.video.iplayerwww')
+
+
+def GetPage(page_url, just_episodes=False):
+ """ Generic Radio page scraper. """
+
+ pDialog = xbmcgui.DialogProgressBG()
+ pDialog.create(translation(30319))
+
+ html = OpenURL(page_url)
+
+ total_pages = 1
+ current_page = 1
+ page_range = range(1)
+ paginate = re.search(r'<ol.+?class="pagination.*?</ol>',html)
+ next_page = 1
+ if paginate:
+ if int(ADDON.getSetting('radio_paginate_episodes')) == 0:
+ current_page_match = re.search(r'page=(\d*)', page_url)
+ if current_page_match:
+ current_page = int(current_page_match.group(1))
+ page_range = range(current_page, current_page+1)
+ next_page_match = re.search(r'<li class="pagination__next"><a href="(.*?page=)(.*?)">', paginate.group(0))
+ if next_page_match:
+ page_base_url = next_page_match.group(1)
+ next_page = int(next_page_match.group(2))
+ else:
+ next_page = current_page
+ page_range = range(current_page, current_page+1)
+ else:
+ pages = re.findall(r'<li.+?class="pagination__page.*?</li>',paginate.group(0))
+ if pages:
+ last = pages[-1]
+ last_page = re.search(r'<a.+?href="(.*?=)(.*?)"',last)
+ page_base_url = last_page.group(1)
+ total_pages = int(last_page.group(2))
+ page_range = range(1, total_pages+1)
+
+ for page in page_range:
+
+ if page > current_page:
+ page_url = 'http://www.bbc.co.uk' + page_base_url + str(page)
+ html = OpenURL(page_url)
+
+ masthead_title = ''
+ masthead_title_match = re.search(r'<div.+?id="programmes-main-content".*?<span property="name">(.+?)</span>', html)
+ if masthead_title_match:
+ masthead_title = masthead_title_match.group(1)
+
+ list_item_num = 1
+
+ programmes = html.split('<div class="programme ')
+ for programme in programmes:
+
+ if not programme.startswith("programme--radio"):
+ continue
+
+ if "available" not in programme: #TODO find a more robust test
+ continue
+
+ series_id = ''
+ series_id_match = re.search(r'<a class="iplayer-text js-lazylink__link" href="/programmes/(.+?)/episodes/player"', programme)
+ if series_id_match:
+ series_id = series_id_match.group(1)
+
+ programme_id = ''
+ programme_id_match = re.search(r'data-pid="(.+?)"', programme)
+ if programme_id_match:
+ programme_id = programme_id_match.group(1)
+
+ name = ''
+ name_match = re.search(r'<span property="name">(.+?)</span>', programme)
+ if name_match:
+ name = name_match.group(1)
+
+ subtitle = ''
+ subtitle_match = re.search(r'<span class="programme__subtitle.+?property="name">(.*?)</span>(.*?property="name">(.*?)</span>)?', programme)
+ if subtitle_match:
+ series = subtitle_match.group(1)
+ episode = subtitle_match.group(3)
+ if episode:
+ subtitle = "(%s, %s)" % (series, episode)
+ else:
+ if series.strip():
+ subtitle = "(%s)" % series
+
+ image = ''
+ image_match = re.search(r'<meta property="image" content="(.+?)" />', programme)
+ if image_match:
+ image = image_match.group(1)
+
+ synopsis = ''
+ synopsis_match = re.search(r'<span property="description">(.+?)</span>', programme)
+ if synopsis_match:
+ synopsis = synopsis_match.group(1)
+
+ station = ''
+ station_match = re.search(r'<p class="programme__service.+?<strong>(.+?)</strong>.*?</p>', programme)
+ if station_match:
+ station = station_match.group(1).strip()
+
+ series_title = "[B]%s - %s[/B]" % (station, name)
+ if just_episodes:
+ title = "[B]%s[/B] - %s" % (masthead_title, name)
+ else:
+ title = "[B]%s[/B] - %s %s" % (station, name, subtitle)
+
+ if series_id:
+ AddMenuEntry(series_title, series_id, 131, image, synopsis, '')
+ elif programme_id: #TODO maybe they are not always mutually exclusive
+
+ url = "http://www.bbc.co.uk/programmes/%s" % programme_id
+ CheckAutoplay(title, url, image, ' ', '')
+
+ percent = int(100*(page+list_item_num/len(programmes))/total_pages)
+ pDialog.update(percent,translation(30319),name)
+
+ list_item_num += 1
+
+ percent = int(100*page/total_pages)
+ pDialog.update(percent,translation(30319))
+
+
+ if int(ADDON.getSetting('radio_paginate_episodes')) == 0:
+ if current_page < next_page:
+ page_url = 'http://www.bbc.co.uk' + page_base_url + str(next_page)
+ AddMenuEntry(" [COLOR ffffa500]%s >>[/COLOR]" % translation(30320), page_url, 136, '', '', '')
+
+ #BUG: this should sort by original order but it doesn't (see http://trac.kodi.tv/ticket/10252)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_UNSORTED)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE)
+
+ pDialog.close()
+
+
+
+def GetEpisodes(url):
+ new_url = 'http://www.bbc.co.uk/programmes/%s/episodes/player' % url
+ GetPage(new_url,True)
+
+
+
+def AddAvailableLiveStreamItem(name, channelname, iconimage):
+ """Play a live stream based on settings for preferred live source and bitrate."""
+ providers = [('ak', 'Akamai'), ('llnw', 'Limelight')]
+ location_qualities = {'uk' : ['sbr_vlow', 'sbr_low', 'sbr_med', 'sbr_high'], 'nonuk': ['sbr_vlow', 'sbr_low'] }
+ location_settings = ['uk', 'nonuk']
+
+ location = location_settings[int(ADDON.getSetting('radio_location'))]
+
+ for provider_url, provider_name in providers:
+ qualities = location_qualities[location]
+ max_quality = int(ADDON.getSetting('radio_live_bitrate')) + 1
+ max_quality = min(len(qualities),max_quality)
+ qualities = qualities[0:max_quality]
+ qualities.reverse()
+ for quality in qualities:
+ url = 'http://a.files.bbci.co.uk/media/live/manifesto/audio/simulcast/hls/%s/%s/%s/%s.m3u8' % (location, quality, provider_url, channelname)
+
+ PlayStream(name, url, iconimage, '', '')
+
+
+def AddAvailableLiveStreamsDirectory(name, channelname, iconimage):
+ """Retrieves the available live streams for a channel
+
+ Args:
+ name: only used for displaying the channel.
+ iconimage: only used for displaying the channel.
+ channelname: determines which channel is queried.
+ """
+ providers = [('ak', 'Akamai'), ('llnw', 'Limelight')]
+ location_qualities = {
+ 'uk' : ['sbr_vlow', 'sbr_low', 'sbr_med', 'sbr_high'],
+ 'nonuk': ['sbr_vlow', 'sbr_low']
+ }
+ location_names = {'uk': 'UK', 'nonuk': 'International'}
+ quality_colours = {
+ 'sbr_vlow': 'ffff0000',
+ 'sbr_low': 'ffffa500',
+ 'sbr_med': 'ffffff00',
+ 'sbr_high': 'ff008000'
+ }
+ quality_bitrates = {
+ 'sbr_vlow': '48',
+ 'sbr_low': '96',
+ 'sbr_med': '128',
+ 'sbr_high': '320'
+ }
+
+ for location in location_qualities.keys():
+ qualities = location_qualities[location]
+ qualities.reverse()
+ for quality in qualities:
+ for provider_url, provider_name in providers:
+ url = 'http://a.files.bbci.co.uk/media/live/manifesto/audio/simulcast/hls/%s/%s/%s/%s.m3u8' % (location, quality, provider_url, channelname)
+
+ title = name + ' - [I][COLOR %s]%s Kbps[/COLOR] [COLOR fff1f1f1]%s[/COLOR] [COLOR ffb4b4b4]%s[/COLOR][/I]' % (
+ quality_colours[quality], quality_bitrates[quality] , location_names[location], provider_name)
+
+ AddMenuEntry(title, url, 201, '', '', '')
+
+
+def PlayStream(name, url, iconimage, description, subtitles_url):
+ html = OpenURL(url)
+
+ check_geo = re.search(
+ '<H1>Access Denied</H1>', html)
+ if check_geo or not html:
+ # print "Geoblock detected, raising error message"
+ dialog = xbmcgui.Dialog()
+ dialog.ok(translation(30400), translation(30401))
+ raise
+ liz = xbmcgui.ListItem(name, iconImage='DefaultVideo.png', thumbnailImage=iconimage)
+ liz.setInfo(type='Audio', infoLabels={'Title': name})
+ liz.setProperty("IsPlayable", "true")
+ liz.setPath(url)
+ xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz)
+
+
+def AddAvailableStreamsDirectory(name, stream_id, iconimage, description):
+ """Will create one menu entry for each available stream of a particular stream_id"""
+
+ streams = ParseStreams(stream_id)
+
+ for supplier, bitrate, url, encoding in sorted(streams[0], key=itemgetter(1), reverse=True):
+ bitrate = int(bitrate)
+ if bitrate >= 320:
+ color = 'ff008000'
+ elif bitrate >= 128:
+ color = 'ffffff00'
+ elif bitrate >= 96:
+ color = 'ffffa500'
+ else:
+ color = 'ffff0000'
+ title = name + ' - [I][COLOR %s]%d Kbps %s[/COLOR] [COLOR ffd3d3d3]%s[/COLOR][/I]' % (
+ color, bitrate, encoding, supplier)
+ AddMenuEntry(title, url, 201, iconimage, description, '', '')
+
+
+def AddAvailableStreamItem(name, url, iconimage, description):
+ """Play a streamm based on settings for preferred catchup source and bitrate."""
+ stream_ids = ScrapeAvailableStreams(url)
+ if len(stream_ids) < 1:
+ #TODO check CBeeBies for special cases
+ xbmcgui.Dialog().ok(translation(30403), translation(30404))
+ return
+ streams_all = ParseStreams(stream_ids)
+ streams = streams_all[0]
+ source = int(ADDON.getSetting('radio_source'))
+ if source > 0:
+ # Case 1: Selected source
+ match = [x for x in streams if (x[0] == source)]
+ if len(match) == 0:
+ # Fallback: Use any source and any bitrate
+ match = streams
+ match.sort(key=lambda x: x[1], reverse=True)
+ else:
+ # Case 3: Any source
+ # Play highest available bitrate
+ match = streams
+ match.sort(key=lambda x: x[1], reverse=True)
+ PlayStream(name, match[0][2], iconimage, description, '')
+
+
+
+def ListAtoZ():
+ """List programmes based on alphabetical order.
+
+ Only creates the corresponding directories for each character.
+ """
+ characters = [
+ ('A', 'a'), ('B', 'b'), ('C', 'c'), ('D', 'd'), ('E', 'e'), ('F', 'f'),
+ ('G', 'g'), ('H', 'h'), ('I', 'i'), ('J', 'j'), ('K', 'k'), ('L', 'l'),
+ ('M', 'm'), ('N', 'n'), ('O', 'o'), ('P', 'p'), ('Q', 'q'), ('R', 'r'),
+ ('S', 's'), ('T', 't'), ('U', 'u'), ('V', 'v'), ('W', 'w'), ('X', 'x'),
+ ('Y', 'y'), ('Z', 'z'), ('0-9', '@')]
+
+ for name, url in characters:
+ url = 'http://www.bbc.co.uk/radio/programmes/a-z/by/%s/current' % url
+ AddMenuEntry(name, url, 136, '', '', '')
+
+
+def ListGenres():
+ """List programmes based on alphabetical order.
+
+ Only creates the corresponding directories for each character.
+ """
+ genres = []
+ html = OpenURL('http://www.bbc.co.uk/radio/programmes/genres')
+ mains = html.split('<li class="category br-keyline highlight-box--list">')
+
+ for main in mains:
+ current_main_match = re.search(r'<a.+?class="beta box-link".+?href="(.+?)">(.+?)</a>',main)
+ if current_main_match:
+ genres.append((current_main_match.group(1), current_main_match.group(2), True))
+ current_sub_match = re.findall(r'<a.+?class="box-link".+?href="(.+?)">(.+?)</a>',main)
+ for sub_match_url, sub_match_name in current_sub_match:
+ genres.append((sub_match_url, current_main_match.group(2)+' - '+sub_match_name, False))
+
+ for url, name, group in genres:
+ new_url = 'http://www.bbc.co.uk%s/player/episodes' % url
+ if group:
+ AddMenuEntry("[B]%s[/B]" % name, new_url, 136, '', '', '')
+ else:
+ AddMenuEntry("%s" % name, new_url, 136, '', '', '')
+
+ #BUG: this should sort by original order but it doesn't (see http://trac.kodi.tv/ticket/10252)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_UNSORTED)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE)
+
+
+def ListLive():
+ channel_list = [
+ ('bbc_radio_one', 'BBC Radio 1'),
+ ('bbc_1xtra', 'BBC Radio 1Xtra'),
+ ('bbc_radio_two', 'BBC Radio 2'),
+ ('bbc_radio_three', 'BBC Radio 3'),
+ ('bbc_radio_fourfm', 'BBC Radio 4 FM'),
+ ('bbc_radio_fourlw', 'BBC Radio 4 LW'),
+ ('bbc_radio_four_extra', 'BBC Radio 4 Extra'),
+ ('bbc_radio_five_live', 'BBC Radio 5 live'),
+ ('bbc_radio_five_live_sports_extra', 'BBC Radio 5 live sports extra'),
+ ('bbc_6music', 'BBC Radio 6 Music'),
+ ('bbc_asian_network', 'BBC Asian Network'),
+ ('bbc_radio_scotland_fm', 'BBC Radio Scotland'),
+ ('bbc_radio_nan_gaidheal', u'BBC Radio nan Gàidheal'),
+ ('bbc_radio_ulster', 'BBC Radio Ulster'),
+ ('bbc_radio_foyle', 'BBC Radio Foyle'),
+ ('bbc_radio_wales_fm', 'BBC Radio Wales'),
+ ('bbc_radio_cymru', 'BBC Radio Cymru'),
+ ('bbc_radio_berkshire', 'BBC Radio Berkshire'),
+ ('bbc_radio_bristol', 'BBC Radio Bristol'),
+ ('bbc_radio_cambridge', 'BBC Radio Cambridgeshire'),
+ ('bbc_radio_cornwall', 'BBC Radio Cornwall'),
+ ('bbc_radio_coventry_warwickshire', 'BBC Coventry & Warwickshire'),
+ ('bbc_radio_cumbria', 'BBC Radio Cumbria'),
+ ('bbc_radio_derby', 'BBC Radio Derby'),
+ ('bbc_radio_devon', 'BBC Radio Devon'),
+ ('bbc_radio_essex', 'BBC Essex'),
+ ('bbc_radio_gloucestershire', 'BBC Radio Gloucestershire'),
+ ('bbc_radio_guernsey', 'BBC Radio Guernsey'),
+ ('bbc_radio_hereford_worcester', 'BBC Hereford & Worcester'),
+ ('bbc_radio_humberside', 'BBC Radio Humberside'),
+ ('bbc_radio_jersey', 'BBC Radio Jersey'),
+ ('bbc_radio_kent', 'BBC Radio Kent'),
+ ('bbc_radio_lancashire', 'BBC Radio Lancashire'),
+ ('bbc_radio_leeds', 'BBC Radio Leeds'),
+ ('bbc_radio_leicester', 'BBC Radio Leicester'),
+ ('bbc_radio_lincolnshire', 'BBC Radio Lincolnshire'),
+ ('bbc_london', 'BBC Radio London'),
+ ('bbc_radio_manchester', 'BBC Radio Manchester'),
+ ('bbc_radio_merseyside', 'BBC Radio Merseyside'),
+ ('bbc_radio_newcastle', 'BBC Newcastle'),
+ ('bbc_radio_norfolk', 'BBC Radio Norfolk'),
+ ('bbc_radio_northampton', 'BBC Radio Northampton'),
+ ('bbc_radio_nottingham', 'BBC Radio Nottingham'),
+ ('bbc_radio_oxford', 'BBC Radio Oxford'),
+ ('bbc_radio_sheffield', 'BBC Radio Sheffield'),
+ ('bbc_radio_shropshire', 'BBC Radio Shropshire'),
+ ('bbc_radio_solent', 'BBC Radio Solent'),
+ ('bbc_radio_somerset_sound', 'BBC Somerset'),
+ ('bbc_radio_stoke', 'BBC Radio Stoke'),
+ ('bbc_radio_suffolk', 'BBC Radio Suffolk'),
+ ('bbc_radio_surrey', 'BBC Surrey'),
+ ('bbc_radio_sussex', 'BBC Sussex'),
+ ('bbc_tees', 'BBC Tees'),
+ ('bbc_three_counties_radio', 'BBC Three Counties Radio'),
+ ('bbc_radio_wiltshire', 'BBC Wiltshire'),
+ ('bbc_wm', 'BBC WM 95.6'),
+ ('bbc_radio_york', 'BBC Radio York'),
+ ]
+ for id, name in channel_list:
+ iconimage = xbmc.translatePath(
+ os.path.join('special://home/addons/plugin.video.iplayerwww/media', id + '.png'))
+ if ADDON.getSetting('streams_autoplay') == 'true':
+ AddMenuEntry(name, id, 213, iconimage, '', '')
+ else:
+ AddMenuEntry(name, id, 133, iconimage, '', '')
+
+
+def ListListenList(logged_in):
+ if(CheckLogin(logged_in) == False):
+ CreateBaseDirectory('audio')
+ return
+
+ """Scrapes all episodes of the favourites page."""
+ html = OpenURL('http://www.bbc.co.uk/radio/favourites/episodesandclips')
+
+ programmes = html.split('<div class="favourites box-link favourite ')
+ for programme in programmes:
+
+ if not programme.startswith('media'):
+ continue
+
+ data_available_match = re.search(r'data-is-available="(.*?)"', programme)
+ if ((not data_available_match) or (data_available_match.group(1) == '')):
+ continue
+
+ series_id = ''
+ series_name = ''
+ series_id_match = re.search(r'<a href="http://www.bbc.co.uk/programmes/(.*?)" class="media__meta-row size-f clr-light-grey text--single-line">\s*(.*?)\s*</a>',programme)
+ if series_id_match:
+ series_name = series_id_match.group(2)
+ series_id = series_id_match.group(1)
+
+ episode_name = ''
+ episode_id = ''
+ episode_id_match = re.search(r'<a aria-label="(.*?) Duration: (.*?)" class="favourites__brand-link(.*?)" href="http://www.bbc.co.uk/programmes/(.*?)#play">',programme)
+ if episode_id_match:
+ episode_name = episode_id_match.group(1)
+ episode_id = episode_id_match.group(4)
+
+ episode_image = ''
+ episode_image_match = re.search(r'<img alt="" class="favourites__brand-image media__image " src="(.*?)"',programme)
+ if episode_image_match:
+ episode_image = "http:%s" % episode_image_match.group(1)
+
+ series_image = ''
+ series_image_match = re.search(r'<img class="media__image avatar-image--small" src="(.*?)">',programme)
+ if series_image_match:
+ series_image = "http:%s" % series_image_match.group(1)
+ series_image = re.sub(r'96x96','640x360',series_image)
+
+ station = ''
+ station_match = re.search(r'<span class="favourites__network-name.*?<a href="(.*?)" class="clr-light-grey">\s+?(.*?)\s+?<',programme, flags=(re.DOTALL | re.MULTILINE))
+ if station_match:
+ station = station_match.group(2).strip()
+
+ description = ''
+ description_match = re.search(r'<p class="favourites__description media__meta-row size-f clr-white.*?">\s+?(.*?)\s+?</p>',programme, flags=(re.DOTALL | re.MULTILINE))
+ if description_match:
+ description = description_match.group(1).strip()
+
+ if series_id:
+ series_title = "[B]%s - %s[/B]" % (station, series_name)
+ AddMenuEntry(series_title, series_id, 131, series_image, description, '')
+
+ if episode_id:
+ if series_name:
+ episode_title = "[B]%s[/B] - %s - %s" % (station, series_name, episode_name)
+ else:
+ episode_title = "[B]%s[/B] - %s" % (station, episode_name)
+ episode_url = "http://www.bbc.co.uk/programmes/%s" % episode_id
+ # xbmc.log(episode_url)
+ CheckAutoplay(episode_title, episode_url, episode_image, ' ', '')
+
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_UNSORTED)
+
+
+def ListFollowing(logged_in):
+ if(CheckLogin(logged_in) == False):
+ CreateBaseDirectory('audio')
+ return
+
+ """Scrapes all episodes of the favourites page."""
+ html = OpenURL('https://www.bbc.co.uk/radio/favourites/programmes')
+
+ programmes = html.split('<div class="favourites follow ')
+ for programme in programmes:
+
+ if not programme.startswith('media'):
+ continue
+
+ series_id = ''
+ series_name = ''
+ series_id_match = re.search(r'<a aria-label="(.*?)" class="follows__image-link" href="http://www.bbc.co.uk/programmes/(.*?)">',programme)
+ if series_id_match:
+ series_name = series_id_match.group(1)
+ series_id = series_id_match.group(2)
+
+ episode_name = ''
+ episode_id = ''
+ episode_id_match = re.search(r'<a aria-label="(.*?)" class="size-e clr-white" href="http://www.bbc.co.uk/programmes/(.*?)#play"',programme)
+ if episode_id_match:
+ episode_name = episode_id_match.group(1)
+ episode_id = episode_id_match.group(2)
+
+ episode_image = ''
+ series_image = ''
+ series_image_match = re.search(r'<img class="media__image" src="(.*?)"',programme)
+ if series_image_match:
+ series_image = "https:%s" % series_image_match.group(1)
+ episode_image = series_image
+
+ station = ''
+ station_match = re.search(r'<a href="(.*?)" class="clr-light-grey">\s*(.*?)\s*</a>',programme, flags=(re.DOTALL | re.MULTILINE))
+ if station_match:
+ station = station_match.group(2).strip()
+
+ description = ''
+
+ if series_id:
+ series_title = "[B]%s - %s[/B]" % (station, series_name)
+ AddMenuEntry(series_title, series_id, 131, series_image, description, '')
+
+ if episode_id:
+ if series_name:
+ episode_title = "[B]%s[/B] - %s - %s" % (station, series_name, episode_name)
+ else:
+ episode_title = "[B]%s[/B] - %s" % (station, episode_name)
+ episode_url = "http://www.bbc.co.uk/programmes/%s" % episode_id
+ # xbmc.log(episode_url)
+ CheckAutoplay(episode_title, episode_url, episode_image, ' ', '')
+
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_UNSORTED)
+
+
+def ListMostPopular():
+ html = OpenURL('http://www.bbc.co.uk/radio/popular')
+
+ programmes = re.split(r'<li class="(episode|clip) typical-list-item', html)
+ for programme in programmes:
+
+ if not programme.startswith(" item-idx-"):
+ continue
+
+ programme_id = ''
+ programme_id_match = re.search(r'<a href="/programmes/(.*?)"', programme)
+ if programme_id_match:
+ programme_id = programme_id_match.group(1)
+
+ name = ''
+ name_match = re.search(r'<img src=".*?" alt="(.*?)"', programme)
+ if name_match:
+ name = name_match.group(1)
+
+ subtitle = ''
+ subtitle_match = re.search(r'<span class="subtitle">\s*(.+?)\s*</span>', programme)
+ if subtitle_match:
+ if subtitle_match.group(1).strip():
+ subtitle = "(%s)" % subtitle_match.group(1)
+
+ image = ''
+ image_match = re.search(r'<img src="(.*?)"', programme)
+ if image_match:
+ image = image_match.group(1)
+
+ station = ''
+ station_match = re.search(r'<span class="service_title">\s*(.+?)\s*</span>', programme)
+ if station_match:
+ station = station_match.group(1)
+
+ title = "[B]%s[/B] - %s %s" % (station, name, subtitle)
+
+ if programme_id and title and image:
+ url = "http://www.bbc.co.uk/programmes/%s" % programme_id
+ CheckAutoplay(title, url, image, ' ', '')
+
+ #BUG: this should sort by original order but it doesn't (see http://trac.kodi.tv/ticket/10252)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_UNSORTED)
+
+
+
+def Search(search_entered):
+ """Simply calls the online search function. The search is then evaluated in EvaluateSearch."""
+ if search_entered is None:
+ keyboard = xbmc.Keyboard('', 'Search iPlayer')
+ keyboard.doModal()
+ if keyboard.isConfirmed():
+ search_entered = keyboard.getText()
+
+ if search_entered is None:
+ return False
+
+ url = 'http://www.bbc.co.uk/radio/programmes/a-z/by/%s/current' % search_entered
+ GetPage(url)
+
+
+def GetAvailableStreams(name, url, iconimage, description):
+ """Calls AddAvailableStreamsDirectory based on user settings"""
+ stream_ids = ScrapeAvailableStreams(url)
+ if stream_ids:
+ AddAvailableStreamsDirectory(name, stream_ids, iconimage, description)
+
+
+def ParseStreams(stream_id):
+ retlist = []
+ # print "Parsing streams for PID: %s"%stream_id[0]
+ # Open the page with the actual strem information and display the various available streams.
+ NEW_URL = "http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/apple-ipad-hls/vpid/%s/proto/http?cb=%d" % (stream_id[0], random.randrange(10000,99999)) #NOTE magic from get_iplayer
+
+ html = OpenURL(NEW_URL)
+
+ # Parse the different streams and add them as new directory entries.
+ match = re.compile(
+ 'media.+?bitrate="(.+?)".+?encoding="(.+?)".+?connection.+?href="(.+?)".+?supplier="(.+?)".+?transferFormat="(.+?)"'
+ ).findall(html)
+ for bitrate, encoding, url, supplier, transfer_format in match:
+ retlist.append((supplier, bitrate, url, encoding))
+
+ return retlist, match
+
+
+def ScrapeAvailableStreams(url):
+ # Open page and retrieve the stream ID
+ html = OpenURL(url)
+ # Search for standard programmes.
+ stream_id_st = re.compile('"vpid":"(.+?)"').findall(html)
+ return stream_id_st
+
+
+def CheckAutoplay(name, url, iconimage, plot, aired=None):
+ if ADDON.getSetting('streams_autoplay') == 'true':
+ AddMenuEntry(name, url, 212, iconimage, plot, '', aired=aired)
+ else:
+ AddMenuEntry(name, url, 132, iconimage, plot, '', aired=aired)
+
diff --git a/plugin.video.iplayerwww/resources/lib/ipwww_video.py b/plugin.video.iplayerwww/resources/lib/ipwww_video.py
new file mode 100644
index 0000000..b6dc3ac
--- /dev/null
+++ b/plugin.video.iplayerwww/resources/lib/ipwww_video.py
@@ -0,0 +1,1403 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import division
+
+import os
+import sys
+import re
+import datetime
+import time
+import json
+from operator import itemgetter
+from ipwww_common import translation, AddMenuEntry, OpenURL, \
+ CheckLogin, CreateBaseDirectory, GetCookieJar, \
+ ParseImageUrl, download_subtitles
+
+import xbmc
+import xbmcgui
+import xbmcplugin
+import xbmcaddon
+from random import randint
+
+ADDON = xbmcaddon.Addon(id='plugin.video.iplayerwww')
+
+
+def RedButtonDialog():
+ if ADDON.getSetting('redbutton_warning') == 'true':
+ dialog = xbmcgui.Dialog()
+ ret = dialog.yesno(translation(30405), translation(30406), '',
+ translation(30407), translation(30409), translation(30408))
+ if ret:
+ ListRedButton()
+ else:
+ ListRedButton()
+
+
+def ListRedButton():
+ channel_list = [
+ ('sport_stream_01', 'BBC Red Button 1'),
+ ('sport_stream_02', 'BBC Red Button 2'),
+ ('sport_stream_03', 'BBC Red Button 3'),
+ ('sport_stream_04', 'BBC Red Button 4'),
+ ('sport_stream_05', 'BBC Red Button 5'),
+ ('sport_stream_06', 'BBC Red Button 6'),
+ ('sport_stream_07', 'BBC Red Button 7'),
+ ('sport_stream_08', 'BBC Red Button 8'),
+ ('sport_stream_09', 'BBC Red Button 9'),
+ ('sport_stream_10', 'BBC Red Button 10'),
+ ('sport_stream_11', 'BBC Red Button 11'),
+ ('sport_stream_12', 'BBC Red Button 12'),
+ ('sport_stream_13', 'BBC Red Button 13'),
+ ('sport_stream_14', 'BBC Red Button 14'),
+ ('sport_stream_15', 'BBC Red Button 15'),
+ ('sport_stream_16', 'BBC Red Button 16'),
+ ('sport_stream_17', 'BBC Red Button 17'),
+ ('sport_stream_18', 'BBC Red Button 18'),
+ ('sport_stream_19', 'BBC Red Button 19'),
+ ('sport_stream_20', 'BBC Red Button 20'),
+ ('sport_stream_21', 'BBC Red Button 21'),
+ ('sport_stream_22', 'BBC Red Button 22'),
+ ('sport_stream_23', 'BBC Red Button 23'),
+ ('sport_stream_24', 'BBC Red Button 24'),
+ ('sport_stream_01b', 'BBC Red Button 1b'),
+ ('sport_stream_02b', 'BBC Red Button 2b'),
+ ('sport_stream_03b', 'BBC Red Button 3b'),
+ ('sport_stream_04b', 'BBC Red Button 4b'),
+ ('sport_stream_05b', 'BBC Red Button 5b'),
+ ('sport_stream_06b', 'BBC Red Button 6b'),
+ ('sport_stream_07b', 'BBC Red Button 7b'),
+ ('sport_stream_08b', 'BBC Red Button 8b'),
+ ('sport_stream_09b', 'BBC Red Button 9b'),
+ ('sport_stream_10b', 'BBC Red Button 10b'),
+ ('sport_stream_11b', 'BBC Red Button 11b'),
+ ('sport_stream_12b', 'BBC Red Button 12b'),
+ ('sport_stream_13b', 'BBC Red Button 13b'),
+ ('sport_stream_14b', 'BBC Red Button 14b'),
+ ('sport_stream_15b', 'BBC Red Button 15b'),
+ ('sport_stream_16b', 'BBC Red Button 16b'),
+ ('sport_stream_17b', 'BBC Red Button 17b'),
+ ('sport_stream_18b', 'BBC Red Button 18b'),
+ ('sport_stream_19b', 'BBC Red Button 19b'),
+ ('sport_stream_20b', 'BBC Red Button 20b'),
+ ('sport_stream_21b', 'BBC Red Button 21b'),
+ ('sport_stream_22b', 'BBC Red Button 22b'),
+ ('sport_stream_23b', 'BBC Red Button 23b'),
+ ('sport_stream_24b', 'BBC Red Button 24b'),
+ ]
+ iconimage = xbmc.translatePath('special://home/addons/plugin.video.iplayerwww/media/red_button.png')
+ for id, name in channel_list:
+ if ADDON.getSetting('streams_autoplay') == 'true':
+ AddMenuEntry(name, id, 203, iconimage, '', '')
+ else:
+ AddMenuEntry(name, id, 123, iconimage, '', '')
+
+
+# ListLive creates menu entries for all live channels.
+def ListLive():
+ channel_list = [
+ ('bbc_one_hd', 'BBC One'),
+ ('bbc_two_hd', 'BBC Two'),
+ ('bbc_four_hd', 'BBC Four'),
+ ('cbbc_hd', 'CBBC'),
+ ('cbeebies_hd', 'CBeebies'),
+ ('bbc_news24', 'BBC News Channel'),
+ ('bbc_parliament', 'BBC Parliament'),
+ ('bbc_alba', 'Alba'),
+ ('s4cpbs', 'S4C'),
+ ('bbc_one_london', 'BBC One London'),
+ ('bbc_one_scotland_hd', 'BBC One Scotland'),
+ ('bbc_one_northern_ireland_hd', 'BBC One Northern Ireland'),
+ ('bbc_one_wales_hd', 'BBC One Wales'),
+ ('bbc_two_scotland', 'BBC Two Scotland'),
+ ('bbc_two_northern_ireland_digital', 'BBC Two Northern Ireland'),
+ ('bbc_two_wales_digital', 'BBC Two Wales'),
+ ('bbc_two_england', 'BBC Two England',),
+ ('bbc_one_cambridge', 'BBC One Cambridge',),
+ ('bbc_one_channel_islands', 'BBC One Channel Islands',),
+ ('bbc_one_east', 'BBC One East',),
+ ('bbc_one_east_midlands', 'BBC One East Midlands',),
+ ('bbc_one_east_yorkshire', 'BBC One East Yorkshire',),
+ ('bbc_one_north_east', 'BBC One North East',),
+ ('bbc_one_north_west', 'BBC One North West',),
+ ('bbc_one_oxford', 'BBC One Oxford',),
+ ('bbc_one_south', 'BBC One South',),
+ ('bbc_one_south_east', 'BBC One South East',),
+ ('bbc_one_south_west', 'BBC One South West',),
+ ('bbc_one_west', 'BBC One West',),
+ ('bbc_one_west_midlands', 'BBC One West Midlands',),
+ ('bbc_one_yorks', 'BBC One Yorks',),
+ ]
+ for id, name in channel_list:
+ iconimage = xbmc.translatePath(
+ os.path.join('special://home/addons/plugin.video.iplayerwww/media', id + '.png'))
+ if ADDON.getSetting('streams_autoplay') == 'true':
+ AddMenuEntry(name, id, 203, iconimage, '', '')
+ else:
+ AddMenuEntry(name, id, 123, iconimage, '', '')
+
+
+def ListAtoZ():
+ """List programmes based on alphabetical order.
+
+ Only creates the corresponding directories for each character.
+ """
+ characters = [
+ ('A', 'a'), ('B', 'b'), ('C', 'c'), ('D', 'd'), ('E', 'e'), ('F', 'f'),
+ ('G', 'g'), ('H', 'h'), ('I', 'i'), ('J', 'j'), ('K', 'k'), ('L', 'l'),
+ ('M', 'm'), ('N', 'n'), ('O', 'o'), ('P', 'p'), ('Q', 'q'), ('R', 'r'),
+ ('S', 's'), ('T', 't'), ('U', 'u'), ('V', 'v'), ('W', 'w'), ('X', 'x'),
+ ('Y', 'y'), ('Z', 'z'), ('0-9', '0-9')]
+
+ if int(ADDON.getSetting('scrape_atoz')) == 1:
+ pDialog = xbmcgui.DialogProgressBG()
+ pDialog.create(translation(30319))
+ page = 1
+ total_pages = len(characters)
+ for name, url in characters:
+ GetAtoZPage(url)
+ percent = int(100*page/total_pages)
+ pDialog.update(percent,translation(30319),name)
+ page += 1
+ pDialog.close()
+ else:
+ for name, url in characters:
+ AddMenuEntry(name, url, 124, '', '', '')
+
+def ListChannelAtoZ():
+ """List programmes for each channel based on alphabetical order.
+
+ Only creates the corresponding directories for each channel.
+ """
+ channel_list = [
+ ('bbcone', 'bbc_one_hd', 'BBC One'),
+ ('bbctwo', 'bbc_two_hd', 'BBC Two'),
+ ('tv/bbcthree', 'bbc_three_hd', 'BBC Three'),
+ ('bbcfour', 'bbc_four_hd', 'BBC Four'),
+ ('tv/cbbc', 'cbbc_hd', 'CBBC'),
+ ('tv/cbeebies', 'cbeebies_hd', 'CBeebies'),
+ ('tv/bbcnews', 'bbc_news24', 'BBC News Channel'),
+ ('tv/bbcparliament', 'bbc_parliament', 'BBC Parliament'),
+ ('tv/bbcalba', 'bbc_alba', 'Alba'),
+ ('tv/s4c', 's4cpbs', 'S4C'),
+ ]
+ for id, img, name in channel_list:
+ iconimage = xbmc.translatePath(
+ os.path.join('special://home/addons/plugin.video.iplayerwww/media', img + '.png'))
+ url = "http://www.bbc.co.uk/%s/a-z" % id
+ AddMenuEntry(name, url, 128, iconimage, '', '')
+
+
+def GetAtoZPage(url):
+ """Allows to list programmes based on alphabetical order.
+
+ Creates the list of programmes for one character.
+ """
+ link = OpenURL('http://www.bbc.co.uk/iplayer/a-z/%s' % url)
+ match = re.compile(
+ '<a href="/iplayer/brand/(.+?)".+?<span class="title">(.+?)</span>',
+ re.DOTALL).findall(link)
+ for programme_id, name in match:
+ AddMenuEntry(name, programme_id, 121, '', '', '')
+
+
+def ParseAired(aired):
+ """Parses a string format %d %b %Y to %d/%n/%Y otherwise empty string."""
+ if aired:
+ try:
+ # Need to use equivelent for datetime.strptime() due to weird TypeError.
+ return datetime.datetime(*(time.strptime(aired[0], '%d %b %Y')[0:6])).strftime('%d/%m/%Y')
+ except ValueError:
+ pass
+ return ''
+
+
+def FirstShownToAired(first_shown):
+ """Converts the 'First shown' tag to %Y %m %d format."""
+ release_parts = first_shown.split(' ')
+
+ if len(release_parts) == 1:
+ month = '01'
+ day = '01'
+ year = first_shown
+ else:
+ year = release_parts[-1]
+ month = release_parts[-2]
+ monthDict={
+ 'Jan':'01', 'Feb':'02', 'Mar':'03', 'Apr':'04', 'May':'05', 'Jun':'06',
+ 'Jul':'07', 'Aug':'08', 'Sep':'09', 'Oct':'10', 'Nov':'11', 'Dec':'12'}
+ if month in monthDict:
+ month = monthDict[month]
+ day = release_parts[-3].rjust(2,'0')
+ else:
+ month = '01'
+ day = '01'
+ aired = year + '-' + month + '-' + day
+ return aired
+
+
+def GetEpisodes(url):
+ new_url = 'http://www.bbc.co.uk/iplayer/episodes/%s' % url
+ ScrapeEpisodes(new_url)
+
+
+def GetGroup(url):
+ new_url = "http://www.bbc.co.uk/iplayer/group/%s" % url
+ ScrapeEpisodes(new_url)
+
+
+def ScrapeEpisodes(page_url):
+ """Creates a list of programmes on one standard HTML page.
+
+ ScrapeEpisodes contains a number of special treatments, which are only needed for
+ specific pages, e.g. Search, but allows to use a single function for all kinds
+ of pages.
+ """
+
+ pDialog = xbmcgui.DialogProgressBG()
+ pDialog.create(translation(30319))
+
+ html = OpenURL(page_url)
+
+ total_pages = 1
+ current_page = 1
+ page_range = range(1)
+ paginate = re.search(r'<div class="paginate.*?</div>', html, re.DOTALL)
+ next_page = 1
+ if paginate:
+ if int(ADDON.getSetting('paginate_episodes')) == 0:
+ current_page_match = re.search(r'page=(\d*)', page_url)
+ if current_page_match:
+ current_page = int(current_page_match.group(1))
+ page_range = range(current_page, current_page+1)
+ next_page_match = re.search(r'<span class="next txt">.+?href="(.*?page=)(.*?)"',
+ paginate.group(0),
+ re.DOTALL)
+ if next_page_match:
+ page_base_url = next_page_match.group(1)
+ next_page = int(next_page_match.group(2))
+ else:
+ next_page = current_page
+ page_range = range(current_page, current_page+1)
+ else:
+ pages = re.findall(r'<li class="page.*?</li>',paginate.group(0),re.DOTALL)
+ if pages:
+ last = pages[-1]
+ last_page = re.search(r'<a href="(.*?page=)(.*?)"',last)
+ page_base_url = last_page.group(1)
+ total_pages = int(last_page.group(2))
+ page_range = range(1, total_pages+1)
+
+ for page in page_range:
+
+ if page > current_page:
+ page_url = 'http://www.bbc.co.uk' + page_base_url + str(page)
+ html = OpenURL(page_url)
+
+ # NOTE remove inner li to match outer li
+
+ # <li data-version-type="hd">
+ html = re.compile(r'<li data-version-type.*?</li>',
+ flags=(re.DOTALL | re.MULTILINE)).sub('', html)
+
+ # <li class="list-item programme" data-ip-id="p026f2t4">
+ list_items = re.findall(r'<li class="list-item.*?</li>', html, flags=(re.DOTALL | re.MULTILINE))
+
+ list_item_num = 1
+
+ for li in list_items:
+ # <li class="list-item unavailable" data-ip-id="b06sq9xj">
+ unavailable_match = re.search(
+ 'data-timeliness-type="unavailable"',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if unavailable_match:
+ continue
+
+ # <li class="list-item search-group" data-ip-id="b06rdtx0">
+ search_group = False
+ search_group_match = re.search(
+ '<li class="list-item.*?search-group.*?"',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if search_group_match:
+ search_group = True
+
+ main_url = None
+ # <a href="/iplayer/episode/p026gmw9/world-of-difference-the-models"
+ # title="World of Difference, The Models" class="list-item-link stat"
+ url_match = re.search(
+ r'<a.*?href="(.*?)".*?list-item-link.*?>',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if url_match:
+ url = url_match.group(1)
+ if url:
+ main_url = 'http://www.bbc.co.uk' + url
+
+ name = ''
+ title = ''
+ #<div class="title top-title">World of Difference</div>
+ title_match = re.search(
+ r'<div class="title top-title">\s*(.*?)\s*</div>',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if title_match:
+ title = title_match.group(1)
+ name = title
+
+ subtitle = None
+ #<div class="subtitle">The Models</div>
+ subtitle_match = re.search(
+ r'<div class="subtitle">\s*(.*?)\s*</div>',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if subtitle_match:
+ subtitle = subtitle_match.group(1)
+ if subtitle:
+ name = name + " - " + subtitle
+
+ icon = ''
+ # <source srcset="http://ichef.bbci.co.uk/images/ic/336x189/p04cd999.jpg"
+ icon_match = re.search(
+ r'<source.*?srcset="https://ichef.bbci.co.uk/images/ic/.*?/(.*?)\.jpg"',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if icon_match:
+ image = icon_match.group(1)
+ if image:
+ icon = "https://ichef.bbci.co.uk/images/ic/832x468/" + image + ".jpg"
+
+
+ type = None
+ # <div class="r-image" data-ip-type="episode"
+ # data-ip-src="http://ichef.bbci.co.uk/images/ic/336x189/p026vl1q.jpg">
+ # <div class="r-image" data-ip-type="group"
+ # data-ip-src="http://ichef.bbci.co.uk/images/ic/336x189/p037ty9z.jpg">
+ image_match = re.search(
+ r'<div class="r-image".+?data-ip-type="(.*?)"',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if image_match:
+ type = image_match.group(1)
+
+ synopsis = ''
+ # <p class="synopsis">What was it like to be a top fashion model 30 years ago? (1978)</p>
+ synopsis_match = re.search(
+ r'<p class="synopsis">\s*(.*?)\s*</p>',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if synopsis_match:
+ synopsis = synopsis_match.group(1)
+
+ aired = ''
+ # <span class="release">\nFirst shown: 8 Jun 1967\n</span>
+ release_match = re.search(
+ r'<span class="release">.*?First shown:\s*(.*?)\n.*?</span>',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if release_match:
+ release = release_match.group(1)
+ if release:
+ aired = FirstShownToAired(release)
+
+ episodes = None
+ # <a class="view-more-container avail stat" href="/iplayer/episodes/p00db1jf" data-progress-state="">
+ # <a class="view-more-container sibling stat"
+ # href="/iplayer/search?q=doctor&amp;search_group_id=urn:bbc:programmes:b06qbs4n">
+ episodes_match = re.search(
+ r'<a class="view-more-container.+?stat".+?href="(.*?)"',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if episodes_match:
+ episodes = episodes_match.group(1)
+
+ more = None
+ # <em class="view-more-heading">27</em>
+ more_match = re.search(
+ r'<em class="view-more-heading">(.*?)</em>',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if more_match:
+ more = more_match.group(1)
+
+ if episodes:
+ episodes_url = 'http://www.bbc.co.uk' + episodes
+ if search_group:
+ AddMenuEntry('[B]%s[/B] - %s' % (title, translation(30318)),
+ episodes_url, 128, icon, '', '')
+ else:
+ AddMenuEntry('[B]%s[/B] - %s %s' % (title, more, translation(30313)),
+ episodes_url, 128, icon, '', '')
+ elif more:
+ AddMenuEntry('[B]%s[/B] - %s %s' % (title, more, translation(30313)),
+ main_url, 128, icon, '', '')
+
+ if type != "group":
+ CheckAutoplay(name , main_url, icon, synopsis, aired)
+
+ percent = int(100*(page+list_item_num/len(list_items))/total_pages)
+ pDialog.update(percent,translation(30319),name)
+
+ list_item_num += 1
+
+ percent = int(100*page/total_pages)
+ pDialog.update(percent,translation(30319))
+
+ if int(ADDON.getSetting('paginate_episodes')) == 0:
+ if current_page < next_page:
+ page_url = 'http://www.bbc.co.uk' + page_base_url + str(next_page)
+ AddMenuEntry(" [COLOR ffffa500]%s >>[/COLOR]" % translation(30320), page_url, 128, '', '', '')
+
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_UNSORTED)
+
+ pDialog.close()
+
+
+def ScrapeMarkup(markup):
+ """Creates a list of programmes of a markup response.
+
+ """
+ pDialog = xbmcgui.DialogProgressBG()
+ pDialog.create(translation(30319))
+
+ # <li class="list-item programme" data-ip-id="p026f2t4">
+ list_items = re.findall(r'<li class="list-item.*?</li>', markup, flags=(re.DOTALL | re.MULTILINE))
+
+ list_item_num = 1
+
+ for li in list_items:
+ main_url = None
+ # <a href="/iplayer/episode/p026gmw9/world-of-difference-the-models"
+ # title="World of Difference, The Models" class="list-item-link stat"
+ url_match = re.search(
+ r'<a.*?href="(.*?)".*?list-item-link.*?>',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if url_match:
+ main_url = url_match.group(1)
+
+ name = ''
+ title = ''
+ #<div class="title top-title">World of Difference</div>
+ title_match = re.search(
+ r'<div class="title top-title">\s*(.*?)\s*</div>',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if title_match:
+ title = title_match.group(1)
+ name = title
+
+ subtitle = None
+ #<div class="subtitle">The Models</div>
+ subtitle_match = re.search(
+ r'<div class="subtitle">\s*(.*?)\s*</div>',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if subtitle_match:
+ subtitle = subtitle_match.group(1)
+ if subtitle:
+ name = name + " - " + subtitle
+
+ icon = ''
+ type = None
+ # <div class="r-image" data-ip-type="episode"
+ # data-ip-src="https://ichef.bbci.co.uk/images/ic/336x189/p033s1dh.jpg">
+
+ image_match = re.search(
+ r'srcset="https://ichef.bbci.co.uk/images/ic/.*?/(.*?).jpg"',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if image_match:
+ image = image_match.group(1)
+ if image:
+ icon = "https://ichef.bbci.co.uk/images/ic/832x468/" + image + ".jpg"
+
+ synopsis = ''
+ # <p class="synopsis">What was it like to be a top fashion model 30 years ago? (1978)</p>
+ synopsis_match = re.search(
+ r'<p class="synopsis">\s*(.*?)\s*</p>',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if synopsis_match:
+ synopsis = synopsis_match.group(1)
+
+ # There is no aired date available
+ aired = ''
+
+ episodes_url = None
+ # <a class="view-more-container avail stat" href="/iplayer/episodes/p00db1jf" data-progress-state="">
+ # <a class="view-more-container sibling stat"
+ # href="/iplayer/search?q=doctor&amp;search_group_id=urn:bbc:programmes:b06qbs4n">
+ episodes_match = re.search(
+ r'<a class="view-more-container.+?stat".+?href="(.*?)"',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if episodes_match:
+ episodes_url = episodes_match.group(1)
+
+ more = None
+ # <em class="view-more-heading">27</em>
+ more_match = re.search(
+ r'<em class="view-more-heading">(.*?)</em>',
+ li, flags=(re.DOTALL | re.MULTILINE))
+ if more_match:
+ more = more_match.group(1)
+
+ if episodes_url:
+ AddMenuEntry('[B]%s[/B] - %s %s' % (title, more, translation(30313)),
+ episodes_url, 128, icon, '', '')
+ elif more:
+ AddMenuEntry('[B]%s[/B] - %s %s' % (title, more, translation(30313)),
+ main_url, 128, icon, '', '')
+
+ if type != "group":
+ CheckAutoplay(name , main_url, icon, synopsis, aired)
+
+ percent = int(100*(list_item_num/len(list_items)))
+ pDialog.update(percent,translation(30319),name)
+
+ list_item_num += 1
+
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_UNSORTED)
+
+ pDialog.close()
+
+
+def ListCategories():
+ """Parses the available categories and creates directories for selecting one of them.
+ The category names are scraped from the website.
+ """
+ html = OpenURL('http://www.bbc.co.uk/iplayer')
+ match = re.compile(
+ '<a href="/iplayer/categories/(.+?)" class="stat">(.+?)</a>'
+ ).findall(html)
+ for url, name in match:
+ AddMenuEntry(name, url, 125, '', '', '')
+
+
+def ListCategoryFilters(url):
+ """Parses the available category filters (if available) and creates directories for selcting them.
+ If there are no filters available, all programmes will be listed using GetFilteredCategory.
+ """
+ NEW_URL = 'http://www.bbc.co.uk/iplayer/categories/%s/all?sort=atoz' % url
+ # Read selected category's page.
+ html = OpenURL(NEW_URL)
+ # Some categories offer filters, we want to provide these filters as options.
+ match1 = re.findall(
+ '<li class="filter"> <a class="name" href="/iplayer/categories/(.+?)"> (.+?)</a>',
+ html,
+ re.DOTALL)
+ if match1:
+ AddMenuEntry('All', url, 126, '', '', '')
+ for url, name in match1:
+ AddMenuEntry(name, url, 126, '', '', '')
+ else:
+ GetFilteredCategory(url)
+
+
+def GetFilteredCategory(url):
+ """Parses the programmes available in the category view."""
+ NEW_URL = 'http://www.bbc.co.uk/iplayer/categories/%s/all?sort=atoz' % url
+
+ ScrapeEpisodes(NEW_URL)
+
+
+def ListChannelHighlights():
+ """Creates a list directories linked to the highlights section of each channel."""
+ channel_list = [
+ ('bbcone', 'bbc_one_hd', 'BBC One'),
+ ('bbctwo', 'bbc_two_hd', 'BBC Two'),
+ ('tv/bbcthree', 'bbc_three_hd', 'BBC Three'),
+ ('bbcfour', 'bbc_four_hd', 'BBC Four'),
+ ('tv/cbbc', 'cbbc_hd', 'CBBC'),
+ ('tv/cbeebies', 'cbeebies_hd', 'CBeebies'),
+ ('tv/bbcnews', 'bbc_news24', 'BBC News Channel'),
+ ('tv/bbcparliament', 'bbc_parliament', 'BBC Parliament'),
+ ('tv/bbcalba', 'bbc_alba', 'Alba'),
+ ('tv/s4c', 's4cpbs', 'S4C'),
+ ]
+ for id, img, name in channel_list:
+ iconimage = xbmc.translatePath(
+ os.path.join('special://home/addons/plugin.video.iplayerwww/media', img + '.png'))
+ AddMenuEntry(name, id, 106, iconimage, '', '')
+
+
+def ListHighlights(highlights_url):
+ """Creates a list of the programmes in the highlights section.
+ """
+
+ html = OpenURL('http://www.bbc.co.uk/%s' % highlights_url)
+
+ inner_anchors = re.findall(r'<a.*?(?!<a).*?</a>',html,flags=(re.DOTALL | re.MULTILINE))
+
+ # First find all groups as we need to store some properties of groups for later reuse.
+ group_properties = []
+
+ # NOTE find episode count first
+ episode_count = dict()
+ groups = [a for a in inner_anchors if re.match(
+ r'<a[^<]*?class="grouped-items__cta.*?data-object-type="group-list-link".*?',
+ a, flags=(re.DOTALL | re.MULTILINE))]
+ for group in groups:
+
+ href = ''
+ href_match = re.match(
+ r'<a[^<]*?href="(.*?)"',
+ group, flags=(re.DOTALL | re.MULTILINE))
+ if href_match:
+ href = href_match.group(1)
+
+ count_match = re.search(
+ r'>View all ([0-9]*).*?</a>',
+ group, flags=(re.DOTALL | re.MULTILINE))
+ if count_match:
+ count = count_match.group(1)
+ episode_count[href] = count
+
+ groups = [a for a in inner_anchors if re.match(
+ r'<a[^<]*?class="grouped-items__title.*?data-object-type="group-list-link".*?',
+ a, flags=(re.DOTALL | re.MULTILINE))]
+ for group in groups:
+
+ href = ''
+ href_match = re.match(
+ r'<a[^<]*?href="(.*?)"',
+ group, flags=(re.DOTALL | re.MULTILINE))
+ if href_match:
+ href = href_match.group(1)
+
+ name = ''
+ name_match = re.search(
+ r'<strong>(.*?)</strong>',
+ group, flags=(re.DOTALL | re.MULTILINE))
+ if name_match:
+ name = name_match.group(1)
+
+ count = ''
+ if href in episode_count:
+ count = episode_count[href]
+
+ url = 'http://www.bbc.co.uk' + href
+
+ # Unfortunately, the group type is not inside the links, so we need to search the whole HTML.
+ group_type = ''
+ group_type_match = re.search(
+ r'data-group-name="'+name+'".+?data-group-type="(.+?)"',
+ html, flags=(re.DOTALL | re.MULTILINE))
+ if group_type_match:
+ group_type = group_type_match.group(1)
+
+ position = ''
+ position_match = re.search(
+ r'data-object-position="(.+?)-ALL"',
+ group, flags=(re.DOTALL | re.MULTILINE))
+ if position_match:
+ group_properties.append(
+ [position_match.group(1),
+ name, group_type])
+
+ AddMenuEntry('[B]%s: %s[/B] - %s %s' % (translation(30314), name, count, translation(30315)),
+ url, 128, '', '', '')
+
+ # Some programmes show up twice in HTML, once inside the groups, once outside.
+ # We need to parse both to avoid duplicates and to make sure we get all of them.
+ episodelist = []
+
+ # <a\n href="/iplayer/episode/b06tr74y/eastenders-24122015"\n class="grouped-items__list-link
+ listeds = [a for a in inner_anchors if re.search(
+ r'class="grouped-items__list-link',
+ a, flags=(re.DOTALL | re.MULTILINE))]
+
+ for listed in listeds:
+
+ episode_id = ''
+ # <a\n href="/iplayer/episode/b06tr74y/eastenders-24122015"
+ id_match = re.match(
+ r'<a.*?href="/iplayer/episode/(.*?)/',
+ listed, flags=(re.DOTALL | re.MULTILINE))
+ if id_match:
+ episode_id = id_match.group(1)
+
+ name = ''
+ # <p class="grouped-items__title grouped-items__title--item typo typo--skylark">
+ # <strong>EastEnders</strong></p>
+ title_match = re.search(
+ r'<.*?class="grouped-items__title.*?<strong>(.*?)</strong>',
+ listed, flags=(re.DOTALL | re.MULTILINE))
+ if title_match:
+ name = title_match.group(1)
+ name = re.compile(r'<.*?>', flags=(re.DOTALL | re.MULTILINE)).sub('', name)
+
+ # <p class="grouped-items__subtitle typo typo--canary">24/12/2015</p>
+ subtitle_match = re.search(
+ r'<.*?class="grouped-items__subtitle.*?>(.*?)<',
+ listed, flags=(re.DOTALL | re.MULTILINE))
+ if subtitle_match:
+ name = name + ' - ' + subtitle_match.group(1)
+
+ # Assign correct group based on the position of the episode
+ position = ''
+ position_match = re.search(
+ r'data-object-position="(.+?)"',
+ listed, flags=(re.DOTALL | re.MULTILINE))
+ if position_match:
+ for n,i in enumerate(group_properties):
+ if re.match(i[0], position_match.group(1), flags=(re.DOTALL | re.MULTILINE)):
+ position = i[1]
+ # For series-catchup groups, we need to modify the title.
+ if i[2] == 'series-catchup':
+ name = i[1]+': '+name
+
+ episodelist.append(
+ [episode_id,
+ name,
+ "%s %s" % (translation(30316), position),
+ 'DefaultVideo.png',
+ '']
+ )
+
+ # < a\nhref="/iplayer/episode/p036gq3z/bbc-music-introducing-from-buddhist-monk-to-rock-star"\n
+ # class="single-item stat"
+ singles = [a for a in inner_anchors if re.search(
+ r'class="thumbnail-item',
+ a, flags=(re.DOTALL | re.MULTILINE))]
+
+ for single in singles:
+
+ object_type = ''
+ # data-object-type="episode-backfill"
+ data_object_type = re.search(
+ r'data-object-type="(.*?)"',
+ single, flags=(re.DOTALL | re.MULTILINE))
+ if data_object_type:
+ object_type = data_object_type.group(1)
+ if object_type == "episode-backfill":
+ if (highlights_url not in ['tv/bbcnews', 'tv/bbcparliament', 'tv/s4c']):
+ continue
+
+ episode_id = ''
+ url = ''
+ # <a\nhref="/iplayer/episode/p036gq3z/bbc-music-introducing-from-buddhist-monk-to-rock-star"
+ if object_type == "editorial-promo":
+ id_match = re.match(
+ r'<a.*?href="(.*?)"',
+ single, flags=(re.DOTALL | re.MULTILINE))
+ else:
+ id_match = re.match(
+ r'<a.*?href="/iplayer/episode/(.*?)/',
+ single, flags=(re.DOTALL | re.MULTILINE))
+ if id_match:
+ episode_id = id_match.group(1)
+ url = 'http://www.bbc.co.uk/iplayer/episode/' + episode_id
+
+ name = ''
+ # <h3 class="single-item__title typo typo--skylark"><strong>BBC Music Introducing</strong></h3>
+ title_match = re.search(
+ r'<.*?class="thumbnail-item__title.*?<strong>(.*?)</strong>',
+ single, flags=(re.DOTALL | re.MULTILINE))
+ if title_match:
+ name = title_match.group(1)
+ name = re.compile(r'<.*?>', flags=(re.DOTALL | re.MULTILINE)).sub('', name)
+
+ # <p class="single-item__subtitle typo typo--canary">From Buddhist Monk to Rock Star</p>
+ subtitle_match = re.search(
+ r'<.*?class="thumbnail-item__subtitle.*?>(.*?)<',
+ single, flags=(re.DOTALL | re.MULTILINE))
+ if subtitle_match:
+ name = name + ' - ' + subtitle_match.group(1)
+
+ icon = ''
+ # <div class="rs-image">
+ # <picture>
+ # <!--[if IE 9]><video style="display:none;"><![endif]-->
+ # <source media="(min-width: 1008px)" srcset="http://ichef.bbci.co.uk/images/ic/234x131/p04g9wkg.jpg 234w,http://ichef.bbci.co.uk/images/ic/352x198/p04g9wkg.jpg 352w" sizes="232px">
+ # <source media="(min-width: 400px) and (max-width: 1007px)" srcset="http://ichef.bbci.co.uk/images/ic/234x131/p04g9wkg.jpg 234w,http://ichef.bbci.co.uk/images/ic/352x198/p04g9wkg.jpg 352w,http://ichef.bbci.co.uk/images/ic/640x360/p04g9wkg.jpg 640w" sizes="calc(50vw - 16px)">
+ # <source media="(max-width: 399px)" srcset="http://ichef.bbci.co.uk/images/ic/176x99/p04g9wkg.jpg 176w,http://ichef.bbci.co.uk/images/ic/272x153/p04g9wkg.jpg 272w" sizes="calc(50vw - 12px)">
+ # <!--[if IE 9]></video><![endif]-->
+ # <img srcset="http://static.bbci.co.uk/tviplayer/img/episode_placeholder.jpg" alt="">
+ # </picture>
+ # </div>
+ image_match = re.search(
+ r'<picture>.*?srcset="https://ichef.bbci.co.uk/images/ic/.*?/(.*?)\.jpg',
+ single, flags=(re.DOTALL | re.MULTILINE))
+ if image_match:
+ image = image_match.group(1)
+ if image:
+ icon = "http://ichef.bbci.co.uk/images/ic/832x468/" + image + ".jpg"
+
+ desc = ''
+ # <p class="item-overlay__text__inner typo typo--canary">
+ # A hospital visit reveals devastating news for Jasmin and Dev.
+ # </p>
+ desc_match = re.search(
+ r'<.*?class="overlay__text__inner.*?>(.*?)<',
+ single, flags=(re.DOTALL | re.MULTILINE))
+ if desc_match:
+ desc = desc_match.group(1)
+
+ aired = ''
+ # <p class="single-item__overlay__subtitle">First shown: 4 Nov 2015</p>
+ release_match = re.search(
+ r'<.*?class="single-item__overlay__subtitle">First shown: (.*?)<',
+ single, flags=(re.DOTALL | re.MULTILINE))
+ if release_match:
+ release = release_match.group(1)
+ if release:
+ aired = FirstShownToAired(release)
+
+ add_entry = True
+ for n,i in enumerate(episodelist):
+ if i[0]==episode_id:
+ episodelist[n][2]=desc
+ episodelist[n][3]=icon
+ episodelist[n][4]=aired
+ add_entry = False
+ if add_entry:
+ if object_type == "editorial-promo":
+ if episode_id:
+ AddMenuEntry('[B]%s[/B]' % (name), episode_id, 128, icon, '', '')
+ else:
+ if url:
+ CheckAutoplay(name, url, icon, desc, aired)
+
+ # Finally add all programmes which have been identified as part of a group before.
+ for episode in episodelist:
+ episode_url = "http://www.bbc.co.uk/iplayer/episode/%s" % episode[0]
+ if ((ADDON.getSetting('suppress_incomplete') == 'false') or (not episode[4] == '')):
+ if episode[0]:
+ CheckAutoplay(episode[1], episode_url, episode[3], episode[2], episode[4])
+
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE)
+ xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_UNSORTED)
+
+def ListMostPopular():
+ """Scrapes all episodes of the most popular page."""
+ ScrapeEpisodes("http://www.bbc.co.uk/iplayer/group/most-popular")
+
+
+def AddAvailableStreamItem(name, url, iconimage, description):
+ """Play a streamm based on settings for preferred catchup source and bitrate."""
+ stream_ids = ScrapeAvailableStreams(url)
+ if stream_ids['stream_id_ad']:
+ streams_all = ParseStreamsHLSDASH(stream_ids['stream_id_ad'])
+ elif stream_ids['stream_id_sl']:
+ streams_all = ParseStreamsHLSDASH(stream_ids['stream_id_sl'])
+ else:
+ streams_all = ParseStreamsHLSDASH(stream_ids['stream_id_st'])
+ if streams_all[1]:
+ # print "Setting subtitles URL"
+ subtitles_url = streams_all[1][0]
+ # print subtitles_url
+ else:
+ subtitles_url = ''
+ streams = streams_all[0]
+ source = int(ADDON.getSetting('catchup_source'))
+ bitrate = int(ADDON.getSetting('catchup_bitrate'))
+ # print "Selected source is %s"%source
+ # print "Selected bitrate is %s"%bitrate
+ # print streams
+ if source > 0:
+ if bitrate > 0:
+ # Case 1: Selected source and selected bitrate
+ match = [x for x in streams if ((x[0] == source) and (x[1] == bitrate))]
+ if len(match) == 0:
+ # Fallback: Use same bitrate but different supplier.
+ match = [x for x in streams if (x[1] == bitrate)]
+ if len(match) == 0:
+ # Second Fallback: Use any lower bitrate from selected source.
+ match = [x for x in streams if (x[0] == source) and (x[1] in range(1, bitrate))]
+ match.sort(key=lambda x: x[1], reverse=True)
+ if len(match) == 0:
+ # Third Fallback: Use any lower bitrate from any source.
+ match = [x for x in streams if (x[1] in range(1, bitrate))]
+ match.sort(key=lambda x: x[1], reverse=True)
+ else:
+ # Case 2: Selected source and any bitrate
+ match = [x for x in streams if (x[0] == source)]
+ if len(match) == 0:
+ # Fallback: Use any source and any bitrate
+ match = streams
+ match.sort(key=lambda x: x[1], reverse=True)
+ else:
+ if bitrate > 0:
+ # Case 3: Any source and selected bitrate
+ match = [x for x in streams if (x[1] == bitrate)]
+ if len(match) == 0:
+ # Fallback: Use any source and any lower bitrate
+ match = streams
+ match = [x for x in streams if (x[1] in range(1, bitrate))]
+ match.sort(key=lambda x: x[1], reverse=True)
+ else:
+ # Case 4: Any source and any bitrate
+ # Play highest available bitrate
+ match = streams
+ match.sort(key=lambda x: x[1], reverse=True)
+ PlayStream(name, match[0][2], iconimage, description, subtitles_url)
+
+
+def GetAvailableStreams(name, url, iconimage, description):
+ """Calls AddAvailableStreamsDirectory based on user settings"""
+ #print url
+ stream_ids = ScrapeAvailableStreams(url)
+ AddAvailableStreamsDirectory(name, stream_ids['stream_id_st'], iconimage, description)
+ # If we searched for Audio Described programmes and they have been found, append them to the list.
+ if stream_ids['stream_id_ad']:
+ AddAvailableStreamsDirectory(name + ' - (Audio Described)', stream_ids['stream_id_ad'], iconimage, description)
+ # If we search for Signed programmes and they have been found, append them to the list.
+ if stream_ids['stream_id_sl']:
+ AddAvailableStreamsDirectory(name + ' - (Signed)', stream_ids['stream_id_sl'], iconimage, description)
+
+
+def Search(search_entered):
+ """Simply calls the online search function. The search is then evaluated in EvaluateSearch."""
+ if search_entered is None:
+ keyboard = xbmc.Keyboard('', 'Search iPlayer')
+ keyboard.doModal()
+ if keyboard.isConfirmed():
+ search_entered = keyboard.getText() .replace(' ', '%20') # sometimes you need to replace spaces with + or %20
+
+ if search_entered is None:
+ return False
+
+ NEW_URL = 'http://www.bbc.co.uk/iplayer/search?q=%s' % search_entered
+ ScrapeEpisodes(NEW_URL)
+
+
+def AddAvailableLiveStreamItemSelector(name, channelname, iconimage):
+ if ((int(ADDON.getSetting('stream_protocol')) == 1) or
+ (channelname.startswith('sport_stream_'))):
+ return AddAvailableLiveStreamItem(name, channelname, iconimage)
+ elif int(ADDON.getSetting('stream_protocol')) == 0:
+ return AddAvailableLiveDASHStreamItem(name, channelname, iconimage)
+
+
+def AddAvailableLiveDASHStreamItem(name, channelname, iconimage):
+
+ streams = ParseLiveDASHStreams(channelname)
+
+ source = int(ADDON.getSetting('live_source'))
+ if source > 0:
+ match = [x for x in streams if (x[0] == source)]
+ if len(match) == 0:
+ match = [x for x in streams if (x[1] in range(1, bitrate))]
+ match.sort(key=lambda x: x[1], reverse=True)
+ else:
+ match = streams
+ match.sort(key=lambda x: x[1], reverse=True)
+ PlayStream(name, match[0][2], iconimage, '', '')
+
+
+def AddAvailableLiveStreamItem(name, channelname, iconimage):
+ """Play a live stream based on settings for preferred live source and bitrate."""
+ stream_bitrates = [9999, 0.1, 0.2, 0.3, 0.6, 1.0, 1.8, 3.1, 5.5]
+
+ if int(ADDON.getSetting('live_source')) == 1:
+ providers = [('ak', 'Akamai')]
+ elif int(ADDON.getSetting('live_source')) == 2:
+ providers = [('llnw', 'Limelight')]
+ else:
+ providers = [('ak', 'Akamai'), ('llnw', 'Limelight')]
+ bitrate_selected = int(ADDON.getSetting('live_bitrate'))
+ if bitrate_selected > len(stream_bitrates) - 1:
+ bitrate_selected = 0
+ ADDON.setSetting('live_bitrate', str(bitrate_selected))
+
+ streams_available = ParseLiveStreams(channelname, providers)
+
+ # print streams_available
+ # Play the prefered option
+ if bitrate_selected > 0:
+ match = [x for x in streams_available if (x[1] == stream_bitrates[bitrate_selected])]
+ if len(match) == 0:
+ # Fallback: Use any bitrate lower than the selected from any source.
+ match = [x for x in streams_available if (x[1] <= stream_bitrates[bitrate_selected] )]
+ match.sort(key=lambda x: x[1], reverse=True)
+ if len(match) == 0:
+ # Fallback: Selected bitrate is too low. Use lowest available bitrate.
+ match = sorted(streams_available, key=lambda x: x[1], reverse=False)
+ # print "Selected bitrate is %s"%stream_bitrates[bitrate_selected]
+ # print match
+ # print "Playing %s from %s with bitrate %s"%(name, match[0][4], match [0][1])
+ if len(match) > 0: #TODO error message
+ PlayStream(name, match[0][4], iconimage, '', '')
+ # Play the fastest available stream of the preferred provider
+ else:
+ PlayStream(name, streams_available[0][4], iconimage, '', '')
+
+
+def AddAvailableLiveStreamsDirectory(name, channelname, iconimage):
+ """Retrieves the available live streams for a channel
+
+ Args:
+ name: only used for displaying the channel.
+ iconimage: only used for displaying the channel.
+ channelname: determines which channel is queried.
+ """
+ if ((int(ADDON.getSetting('stream_protocol')) == 1) or
+ (channelname.startswith('sport_stream_'))):
+ streams = ParseLiveStreams(channelname, '')
+
+ # Add each stream to the Kodi selection menu.
+ for id, bitrate, codecs, resolution, url, provider_name in streams:
+ # For easier selection use colors to indicate high and low bitrate streams
+ if bitrate > 2.1:
+ color = 'ff008000'
+ elif bitrate > 1.0:
+ color = 'ffffff00'
+ elif bitrate > 0.6:
+ color = 'ffffa500'
+ else:
+ color = 'ffff0000'
+
+ title = name + ' - [I][COLOR %s]%0.1f Mbps[/COLOR] [COLOR fff1f1f1]%s[/COLOR][/I]' % (
+ color, bitrate, provider_name)
+ # Finally add them to the selection menu.
+ AddMenuEntry(title, url, 201, iconimage, '', '')
+
+ elif int(ADDON.getSetting('stream_protocol')) == 0:
+ streams = ParseLiveDASHStreams(channelname)
+ suppliers = ['', 'Akamai', 'Limelight', 'Bidi']
+ for supplier, bitrate, url, resolution in streams:
+ title = name + ' - [I][COLOR fff1f1f1]%s[/COLOR][/I]' % (suppliers[supplier])
+ AddMenuEntry(title, url, 201, iconimage, '', '')
+
+
+def ListWatching(logged_in):
+
+ if(CheckLogin(logged_in) == False):
+ CreateBaseDirectory('video')
+ return
+
+ cookie_jar = None
+ cookie_jar = GetCookieJar()
+ url = "https://component.iplayer.api.bbc.co.uk/v1/user/lists/watching"
+ html = OpenURL(url)
+ json_data = json.loads(html)
+ markup = json_data.get('markup')
+ ScrapeMarkup(markup)
+
+
+def ListFavourites(logged_in):
+
+ if(CheckLogin(logged_in) == False):
+ CreateBaseDirectory('video')
+ return
+
+ cookie_jar = None
+ cookie_jar = GetCookieJar()
+ url = "https://component.iplayer.api.bbc.co.uk/v1/user/lists/added"
+ html = OpenURL(url)
+ json_data = json.loads(html)
+ markup = json_data.get('markup')
+ ScrapeMarkup(markup)
+
+
+def PlayStream(name, url, iconimage, description, subtitles_url):
+ if iconimage == '':
+ iconimage = 'DefaultVideo.png'
+ html = OpenURL(url)
+ check_geo = re.search(
+ '<H1>Access Denied</H1>', html)
+ if check_geo or not html:
+ # print "Geoblock detected, raising error message"
+ dialog = xbmcgui.Dialog()
+ dialog.ok(translation(30400), translation(30401))
+ raise
+ liz = xbmcgui.ListItem(name, iconImage='DefaultVideo.png', thumbnailImage=iconimage)
+ liz.setInfo(type='Video', infoLabels={'Title': name})
+ liz.setProperty("IsPlayable", "true")
+ liz.setPath(url)
+ if subtitles_url and ADDON.getSetting('subtitles') == 'true':
+ subtitles_file = download_subtitles(subtitles_url)
+ xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, liz)
+ if subtitles_url and ADDON.getSetting('subtitles') == 'true':
+ # Successfully started playing something?
+ while True:
+ if xbmc.Player().isPlaying():
+ break
+ else:
+ xbmc.sleep(500)
+ xbmc.Player().setSubtitles(subtitles_file)
+
+
+def AddAvailableStreamsDirectory(name, stream_id, iconimage, description):
+ """Will create one menu entry for each available stream of a particular stream_id"""
+ # print "Stream ID: %s"%stream_id
+ streams = ParseStreamsHLSDASH(stream_id)
+ # print streams
+ if streams[1]:
+ # print "Setting subtitles URL"
+ subtitles_url = streams[1][0]
+ # print subtitles_url
+ else:
+ subtitles_url = ''
+ suppliers = ['', 'Akamai', 'Limelight', 'Bidi']
+ bitrates = [0, 800, 1012, 1500, 1800, 2400, 3116, 5510]
+ for supplier, bitrate, url, resolution in sorted(streams[0], key=itemgetter(1), reverse=True):
+ if bitrate in (5, 7):
+ color = 'ff008000'
+ elif bitrate == 6:
+ color = 'ff0084ff'
+ elif bitrate in (3, 4):
+ color = 'ffffff00'
+ else:
+ color = 'ffffa500'
+ if int(ADDON.getSetting('stream_protocol')) == 1:
+ title = name + ' - [I][COLOR %s]%0.1f Mbps[/COLOR] [COLOR ffd3d3d3]%s[/COLOR][/I]' % (
+ color, bitrates[bitrate] / 1000, suppliers[supplier])
+ else:
+ title = name + ' - [I][COLOR ffd3d3d3]%s[/COLOR][/I]' % (suppliers[supplier])
+ AddMenuEntry(title, url, 201, iconimage, description, subtitles_url, resolution=resolution)
+
+
+def ParseStreamsHLSDASH(stream_id):
+ if int(ADDON.getSetting('stream_protocol')) == 1:
+ return ParseStreams(stream_id)
+ elif int(ADDON.getSetting('stream_protocol')) == 0:
+ return ParseDASHStreams(stream_id)
+
+
+def ParseStreams(stream_id):
+ retlist = []
+ # print "Parsing streams for PID: %s"%stream_id[0]
+ # Open the page with the actual strem information and display the various available streams.
+ NEW_URL = "http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/iptv-all/vpid/%s" % stream_id[0]
+ html = OpenURL(NEW_URL)
+ # Parse the different streams and add them as new directory entries.
+ match = re.compile(
+ 'connection authExpires=".+?href="(.+?)".+?supplier="mf_(.+?)".+?transferFormat="(.+?)"'
+ ).findall(html)
+ for m3u8_url, supplier, transfer_format in match:
+ tmp_sup = 0
+ tmp_br = 0
+ if transfer_format == 'hls':
+ if supplier == 'akamai_uk_hls':
+ tmp_sup = 1
+ elif supplier == 'limelight_uk_hls':
+ tmp_sup = 2
+ elif supplier == 'bidi_uk_hls':
+ tmp_sup = 3
+ m3u8_breakdown = re.compile('(.+?)iptv.+?m3u8(.+?)$').findall(m3u8_url)
+ #print m3u8_breakdown
+ # print m3u8_url
+ m3u8_html = OpenURL(m3u8_url)
+ m3u8_match = re.compile('BANDWIDTH=(.+?),.+?RESOLUTION=(.+?)(?:,.+?\n|\n)(.+?)\n').findall(m3u8_html)
+ for bandwidth, resolution, stream in m3u8_match:
+ # print bandwidth
+ # print resolution
+ #print stream
+ url = "%s%s%s" % (m3u8_breakdown[0][0], stream, m3u8_breakdown[0][1])
+ #print url
+ if 1000000 <= int(bandwidth) <= 1100000:
+ tmp_br = 2
+ elif 1790000 <= int(bandwidth) <= 1800000:
+ tmp_br = 4
+ elif 3100000 <= int(bandwidth) <= 3120000:
+ tmp_br = 6
+ elif int(bandwidth) >= 5500000:
+ tmp_br = 7
+ retlist.append((tmp_sup, tmp_br, url, resolution))
+ # It may be useful to parse these additional streams as a default as they offer additional bandwidths.
+ match = re.compile(
+ 'kind="video".+?connection href="(.+?)".+?supplier="(.+?)".+?transferFormat="(.+?)"'
+ ).findall(html)
+ # print match
+ unique = []
+ [unique.append(item) for item in match if item not in unique]
+ # print unique
+ for m3u8_url, supplier, transfer_format in unique:
+ tmp_sup = 0
+ tmp_br = 0
+ if transfer_format == 'hls':
+ if supplier == 'akamai_hls_open':
+ tmp_sup = 1
+ elif supplier == 'limelight_hls_open':
+ tmp_sup = 2
+ m3u8_breakdown = re.compile('.+?master.m3u8(.+?)$').findall(m3u8_url)
+ # print m3u8_url
+ # print m3u8_breakdown
+ m3u8_html = OpenURL(m3u8_url)
+ # print m3u8_html
+ m3u8_match = re.compile('BANDWIDTH=(.+?),RESOLUTION=(.+?),.+?\n(.+?)\n').findall(m3u8_html)
+ # print m3u8_match
+ for bandwidth, resolution, stream in m3u8_match:
+ # print bandwidth
+ # print resolution
+ # print stream
+ url = "%s%s" % (stream, m3u8_breakdown[0][0])
+ # This is not entirely correct, displayed bandwidth may be higher or lower than actual bandwidth.
+ if int(bandwidth) <= 801000:
+ tmp_br = 1
+ elif int(bandwidth) <= 1510000:
+ tmp_br = 3
+ elif int(bandwidth) <= 2410000:
+ tmp_br = 5
+ retlist.append((tmp_sup, tmp_br, url, resolution))
+ # Some events have special live streams which show up as normal programmes.
+ # They need to be parsed separately.
+ match = re.compile(
+ 'connection.+?href="(.+?)".+?supplier="(.+?)".+?transferFormat="(.+?)"'
+ ).findall(html)
+ # print match
+ unique = []
+ [unique.append(item) for item in match if item not in unique]
+ # print unique
+ for m3u8_url, supplier, transfer_format in unique:
+ tmp_sup = 0
+ tmp_br = 0
+ if transfer_format == 'hls':
+ if supplier == 'akamai_hls_live':
+ tmp_sup = 1
+ elif supplier == 'll_hls_live':
+ tmp_sup = 2
+ else:
+ # This is not a live stream, skip code to avoid unnecessary loading of playlists.
+ continue
+ html = OpenURL(m3u8_url)
+ match = re.compile('#EXT-X-STREAM-INF:PROGRAM-ID=(.+?),BANDWIDTH=(.+?),CODECS="(.*?)",RESOLUTION=(.+?)\s*(.+?.m3u8)').findall(html)
+ for stream_id, bandwidth, codecs, resolution, url in match:
+ # Note: This is not entirely correct as these bandwidths relate to live programmes,
+ # not catchup.
+ if int(bandwidth) <= 1000000:
+ tmp_br = 1
+ elif int(bandwidth) <= 1100000:
+ tmp_br = 2
+ elif 1700000 <= int(bandwidth) <= 1900000:
+ tmp_br = 4
+ elif 3100000 <= int(bandwidth) <= 3120000:
+ tmp_br = 6
+ elif int(bandwidth) >= 5500000:
+ tmp_br = 7
+ retlist.append((tmp_sup, tmp_br, url, resolution))
+ match = re.compile('service="captions".+?connection href="(.+?)"').findall(html)
+ # print "Subtitle URL: %s"%match
+ # print retlist
+ if not match:
+ # print "No streams found"
+ check_geo = re.search(
+ '<error id="geolocation"/>', html)
+ if check_geo:
+ # print "Geoblock detected, raising error message"
+ dialog = xbmcgui.Dialog()
+ dialog.ok(translation(30400), translation(30401))
+ raise
+ return retlist, match
+
+
+def ParseDASHStreams(stream_id):
+ retlist = []
+ # print "Parsing streams for PID: %s"%stream_id[0]
+ # Open the page with the actual strem information and display the various available streams.
+ NEW_URL = "http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/iptv-all/vpid/%s" % stream_id[0]
+ html = OpenURL(NEW_URL)
+ # Parse the different streams and add them as new directory entries.
+ match = re.compile(
+ 'connection authExpires=".+?href="(.+?)".+?supplier="mf_(.+?)".+?transferFormat="(.+?)"'
+ ).findall(html)
+ for mpd_url, supplier, transfer_format in match:
+ tmp_sup = 0
+ tmp_br = 0
+ if transfer_format == 'dash':
+ if supplier in ['akamai_uk_dash', 'akamai_uk_dash_https']:
+ tmp_sup = 1
+ elif supplier in ['limelight_uk_dash', 'limelight_uk_dash_https']:
+ tmp_sup = 2
+ elif supplier in ['bidi_uk_dash', 'bidi_uk_dash_https']:
+ tmp_sup = 3
+ retlist.append((tmp_sup, 1, mpd_url, '1280x720'))
+
+ match = re.compile('service="captions".+?connection href="(.+?)"').findall(html)
+ # print "Subtitle URL: %s"%match
+ # print retlist
+ if not match:
+ # print "No streams found"
+ check_geo = re.search(
+ '<error id="geolocation"/>', html)
+ if check_geo:
+ # print "Geoblock detected, raising error message"
+ dialog = xbmcgui.Dialog()
+ dialog.ok(translation(30400), translation(30401))
+ raise
+ return retlist, match
+
+
+
+def ParseLiveStreams(channelname, providers):
+ if providers == '':
+ providers = [('ak', 'Akamai'), ('llnw', 'Limelight')]
+ streams = []
+
+ for provider_url, provider_name in providers:
+ # First we query the available streams from this website
+ if channelname in ['bbc_parliament', 'bbc_alba', 's4cpbs', 'bbc_one_london',
+ 'bbc_two_wales_digital', 'bbc_two_northern_ireland_digital',
+ 'bbc_two_scotland', 'bbc_one_cambridge', 'bbc_one_channel_islands',
+ 'bbc_one_east', 'bbc_one_east_midlands', 'bbc_one_east_yorkshire',
+ 'bbc_one_north_east', 'bbc_one_north_west', 'bbc_one_oxford',
+ 'bbc_one_south', 'bbc_one_south_east', 'bbc_one_south_west',
+ 'bbc_one_west', 'bbc_one_west_midlands', 'bbc_one_yorks']:
+ device = 'hls_tablet'
+ else:
+ device = 'abr_hdtv'
+
+ if channelname.startswith('sport_stream_'):
+ cast = "webcast"
+ else:
+ cast = "simulcast"
+
+ url = 'http://a.files.bbci.co.uk/media/live/manifesto/audio_video/%s/hls/uk/%s/%s/%s.m3u8' \
+ % (cast, device, provider_url, channelname)
+ html = OpenURL(url)
+ match = re.compile('#EXT-X-STREAM-INF:PROGRAM-ID=(.+?),BANDWIDTH=(.+?),CODECS="(.*?)",RESOLUTION=(.+?)\s*(.+?.m3u8)').findall(html)
+
+ # Add provider name to the stream list.
+ streams.extend([list(stream) + [provider_name] for stream in match])
+
+ # Convert bitrate to Mbps for further processing
+ for i in range(len(streams)):
+ streams[i][1] = round(int(streams[i][1])/1000000.0, 1)
+
+ # Return list sorted by bitrate
+ return sorted(streams, key=lambda x: (x[1]), reverse=True)
+
+
+def ParseLiveDASHStreams(channelname):
+ streams = []
+
+ url = "http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/pc/vpid/%s" % channelname
+ html = OpenURL(url)
+ # Parse the different streams and add them as new directory entries.
+ match = re.compile(
+ 'connection.+?href="(.+?)".+?supplier="(.+?)_live".+?transferFormat="(.+?)"'
+ ).findall(html)
+ unique = []
+ [unique.append(item) for item in match if item not in unique]
+ for mpd_url, supplier, transfer_format in unique:
+ tmp_sup = 0
+ tmp_br = 0
+ if transfer_format == 'dash':
+ if supplier in ['akamai_dash']:
+ tmp_sup = 1
+ elif supplier in ['ll_dash']:
+ tmp_sup = 2
+ streams.append((tmp_sup, 1, mpd_url, '1280x720'))
+
+ return streams
+
+
+def ScrapeAvailableStreams(url):
+ # Open page and retrieve the stream ID
+ html = OpenURL(url)
+ # Search for standard programmes.
+ stream_id_st = re.compile('"vpid":"(.+?)"').findall(html)
+ # Optionally, Signed programmes can be searched for. These have a different ID.
+ if ADDON.getSetting('search_signed') == 'true':
+ stream_id_sl = re.compile('data-download-sl="bbc-ipd:download/.+?/(.+?)/sd/').findall(html)
+ else:
+ stream_id_sl = []
+ # Optionally, Audio Described programmes can be searched for. These have a different ID.
+ if ADDON.getSetting('search_ad') == 'true':
+ url_ad = re.compile('<a href="(.+?)" class="version link watch-ad-on"').findall(html)
+ url_tmp = "http://www.bbc.co.uk%s" % url_ad[0]
+ html = OpenURL(url_tmp)
+ stream_id_ad = re.compile('"vpid":"(.+?)"').findall(html)
+ # print stream_id_ad
+ else:
+ stream_id_ad = []
+ return {'stream_id_st': stream_id_st, 'stream_id_sl': stream_id_sl, 'stream_id_ad': stream_id_ad}
+
+
+def CheckAutoplay(name, url, iconimage, plot, aired=None):
+ if ADDON.getSetting('streams_autoplay') == 'true':
+ AddMenuEntry(name, url, 202, iconimage, plot, '', aired=aired)
+ else:
+ AddMenuEntry(name, url, 122, iconimage, plot, '', aired=aired)
+
diff --git a/plugin.video.iplayerwww/resources/settings.xml b/plugin.video.iplayerwww/resources/settings.xml
new file mode 100644
index 0000000..af3ed8b
--- /dev/null
+++ b/plugin.video.iplayerwww/resources/settings.xml
@@ -0,0 +1,56 @@
+<settings>
+<category label="30100">
+ <setting label="30103" type="lsep" />
+ <setting id="bbc_id_enabled" label="30140" default="false" type="bool" />
+ <setting id="bbc_id_username" label="30141" default="" type="text" enable="eq(-1,true)" subsetting="true"/>
+ <setting id="bbc_id_password" label="30142" default="" type="text" enable="eq(-2,true) + !eq(-1,)" subsetting="true" option="hidden"/>
+ <setting id="bbc_id_autologin" label="30143" default="false" type="bool" subsetting="true" />
+ <setting id="kids_mode" label="30180" type="action" action="XBMC.RunPlugin(plugin://plugin.video.iplayerwww?mode=1)"/>
+ <setting label="30101" type="lsep" />
+ <setting id="search_signed" label="30110" type="bool" default="false" />
+ <setting id="search_ad" label="30120" type="bool" default="false" />
+ <setting id="subtitles" label="30130" type="bool" default="false" />
+</category>
+<category label="30500">
+ <setting label="30101" type="lsep" />
+ <setting id="suppress_incomplete" label="30150" type="bool" default="false" />
+ <setting id="scrape_atoz" label="30160" type="enum" lvalues="30161|30162" default="0" />
+ <setting id="paginate_episodes" label="30170" type="enum" lvalues="30171|30172" default="1" />
+ <setting id="redbutton_warning" label="30190" type="bool" default="true" />
+ <setting label="30102" type="lsep" />
+ <setting id="radio_paginate_episodes" label="30170" type="enum" lvalues="30171|30172" default="2" />
+ <setting label="30510" type="lsep" />
+ <setting id="menu_video_highlights" label="30511" type="bool" default="true"/>
+ <setting id="menu_video_channel_highlights" label="30512" type="bool" default="true"/>
+ <setting id="menu_video_most_popular" label="30513" type="bool" default="true"/>
+ <setting id="menu_video_az" label="30514" type="bool" default="true"/>
+ <setting id="menu_video_channel_az" label="30515" type="bool" default="true"/>
+ <setting id="menu_video_categories" label="30516" type="bool" default="true"/>
+ <setting id="menu_video_search" label="30517" type="bool" default="true"/>
+ <setting id="menu_video_live" label="30518" type="bool" default="true"/>
+ <setting id="menu_video_red_button" label="30519" type="bool" default="true"/>
+ <setting id="menu_video_watching" label="30520" type="bool" default="true"/>
+ <setting id="menu_video_added" label="30521" type="bool" default="true"/>
+ <setting id="menu_radio_live" label="30522" type="bool" default="true"/>
+ <setting id="menu_radio_az" label="30523" type="bool" default="true"/>
+ <setting id="menu_radio_categories" label="30524" type="bool" default="true"/>
+ <setting id="menu_radio_search" label="30525" type="bool" default="true"/>
+ <setting id="menu_radio_most_popular" label="30526" type="bool" default="true"/>
+ <setting id="menu_radio_added" label="30527" type="bool" default="true"/>
+ <setting id="menu_radio_following" label="30528" type="bool" default="true"/>
+</category>
+<category label="30200">
+ <setting label="30103" type="lsep" />
+ <setting id="streams_autoplay" label="30205" type="bool" default="true" />
+ <setting label="30101" type="lsep" />
+ <setting id="stream_protocol" label="30212" type="enum" values="DASH|HLS" default="0" />
+ <setting id="catchup_source" label="30210" type="enum" values="Any|Akamai|Limelight|Bidi" default="0" enable="eq(-3,true)" />
+ <setting id="catchup_bitrate" label="30211" type="enum" values="Highest|0.8 Mbps|1.0 Mbps|1.5 Mbps|1.8 Mbps|2.4 Mbps|3.1 Mbps|5.5 Mbps" default="0" enable="eq(-4,true) + eq(-2,1)" />
+ <setting id="live_source" label="30220" type="enum" values="Any|Akamai|Limelight" default="0" enable="eq(-5,true)" />
+ <setting id="live_bitrate" label="30230" type="enum" values="Highest|0.1 Mbps|0.2 Mbps|0.3 Mbps|0.6 Mbps|1.0 Mbps|1.8 Mbps|3.1 Mbps|5.5 Mbps" default="0" enable="eq(-6,true) + eq(-4,1)" />
+ <setting label="30102" type="lsep" />
+ <setting id="radio_source" label="30231" type="enum" values="Any|Akamai|Limelight" default="0" enable="eq(-8,true)" />
+ <setting id="radio_location" label="30232" type="enum" values="UK|International" default="0" enable="eq(-9,true)" />
+ <setting id="radio_live_bitrate" label="30233" type="enum" values="48 Kbps|96 Kbps|128 Kbps|320 Kbps" default="3" enable="eq(-10,true)" />
+</category>
+</settings>