Repository: streamlink/streamlink
Pull request #1457 (2018-01-30 15:08:19)

[TF1] Fix plugin
TF1 group renamed some of her channels, NT1 & HD1, as "TFX" & "TF1 Séries Films". So I've updated plugin according to last TF1 changes.


Patch:
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
index 1a37ba45..2cfee5da 100644
--- a/ISSUE_TEMPLATE.md
+++ b/ISSUE_TEMPLATE.md
@@ -29,14 +29,17 @@
 2. ...
 3. ...
 
-### Environment details
+### Logs
 
-Operating system and version: ...
-Streamlink and Python version: ...
+_Logs are always required for a bug report, use `-l debug` [(help)](https://streamlink.github.io/cli.html#cmdoption-l)
+Make sure to **remove username and password**
+You can upload your logs to https://gist.github.com/ or_
 
-...
+```
+REPLACE THIS TEXT WITH YOUR LOG
+```
 
-### Comments, logs, screenshots, etc.
+### Comments, screenshots, etc.
 
 ...
 
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index 25d39504..00000000
--- a/debian/changelog
+++ /dev/null
@@ -1,5 +0,0 @@
-streamlink (0.0.2-1) unstable; urgency=low
-
-  * Initial release (Closes: #XXXXXXX)
-
- -- Stefan Breunig <stefan-debian@yrden.de>  Sun, 16 Oct 2016 11:37:03 +0100
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index ec635144..00000000
--- a/debian/compat
+++ /dev/null
@@ -1,1 +0,0 @@
-9
diff --git a/debian/control b/debian/control
deleted file mode 100644
index fe0e6fb5..00000000
--- a/debian/control
+++ /dev/null
@@ -1,108 +0,0 @@
-Source: streamlink
-Maintainer: Stefan Breunig <stefan-debian@yrden.de>
-Section: python
-Priority: optional
-Standards-Version: 3.9.6
-Build-Depends: debhelper (>= 9),
- dh-python,
- python,
- python3,
- python-setuptools,
- python3-setuptools,
- python-singledispatch,
- python3 (>= 3.4) | python3-singledispatch,
- python-requests,
- python3-requests,
- python-sphinx,
- python-concurrent.futures
-X-Python-Version: >= 2.6
-X-Python3-Version: >= 3.3
-Homepage: http://streamlink.io/
-Vcs-Git: git://github.com/streamlink/streamlink.git
-Vcs-Browser: https://github.com/streamlink/streamlink
-
-Package: python-streamlink
-Architecture: all
-Depends: ${misc:Depends},
- ${python:Depends},
- python-requests,
- python-singledispatch,
- python-concurrent.futures
-Recommends: rtmpdump,
-  python-crypto,
-  python-librtmp
-Suggests: streamlink
-Description: library to extract video streams from various services - Python 2
- The API lists available video qualities from URLs which are
- visible to a service’s users. It can extract any such quality
- and pass on the raw video data.
- .
- Currently most of the big streaming services are supported
- (e.g. Dailymotion, Livestream, Justin.tv, Twitch, YouTube Live
- and UStream) and more specialized content providers can be
- added easily using Streamlink’s plugin system.
- .
- This package makes Streamlink APIs accessible in Python 2.
-
-Package: python3-streamlink
-Architecture: all
-Depends: ${misc:Depends},
- ${python3:Depends},
- python3-requests,
- python3 (>= 3.4) | python3-singledispatch
-Recommends: rtmpdump,
-  python3-crypto,
-  python3-librtmp
-Suggests: streamlink
-Description: library to extract video streams from various services - Python 3
- The API lists available video qualities from URLs which are
- visible to a service’s users. It can extract any such quality
- and pass on the raw video data.
- .
- Currently most of the big streaming services are supported
- (e.g. Dailymotion, Livestream, Justin.tv, Twitch, YouTube Live
- and UStream) and more specialized content providers can be
- added easily using Streamlink’s plugin system.
- .
- This package makes Streamlink APIs accessible in Python 3.
-
-Package: python-streamlink-doc
-Section: doc
-Architecture: all
-Depends: ${sphinxdoc:Depends},
-  ${misc:Depends},
-  libjs-modernizr,
-  sphinx-rtd-theme-common
-Recommends: fonts-inconsolata,
-  fonts-lato
-Description: library to extract video streams from various services - documentation
- The API lists available video qualities from URLs which are
- visible to a service’s users. It can extract any such quality
- and pass on the raw video data.
- .
- Currently most of the big streaming services are supported
- (e.g. Dailymotion, Livestream, Justin.tv, Twitch, YouTube Live
- and UStream) and more specialized content providers can be
- added easily using Streamlink’s plugin system.
- .
- This package contains the Streamlink usage, API and plugins
- documentation in HTML format.
-
-Package: streamlink
-Section: video
-Architecture: all
-Depends: ${misc:Depends},
-  ${python:Depends},
-  python-streamlink (= ${source:Version})
-Suggests: python-streamlink-doc
-Description: CLI that pipes video streams to video players
- Streamlink is a Command Line Interface that extracts video
- streams from various services and hands them to a video player,
- such as VLC. The main purpose of Streamlink is to allow the
- user to avoid buggy and CPU heavy flash plugins but still
- be able to enjoy various streamed content.
- .
- Currently most of the big streaming services are supported
- (e.g. Dailymotion, Livestream, Justin.tv, Twitch, YouTube Live
- and UStream) and more specialized content providers can be
- added easily using Streamlink’s plugin system.
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index 0b725ccb..00000000
--- a/debian/copyright
+++ /dev/null
@@ -1,73 +0,0 @@
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-Upstream-Contact: Forrest Alvarez <forrest.alvarez+streamlinkdebian@gmail.com>
-Source: https://github.com/streamlink/streamlink
-Upstream-Name: streamlink
-
-Files: *
-Copyright: 2011-2016 Christopher Rosell <chrippa@tanuki.se>
-           2016-2018 Streamlink Team
-License: BSD-2-clause
-
-Files: src/streamlink/packages/pbs.py
-Copyright: 2011-2012 Andrew Moffat
-License: Expat
-
-Files: docs/_themes/sphinx_rtd_theme_violet/*
-Copyright: 2013 Dave Snider
-           2014 Christopher Rosell <chrippa@tanuki.se>
-License: Expat
-
-Files: docs/_themes/sphinx_rtd_theme_violet/search.html
-       docs/_themes/sphinx_rtd_theme_violet/layout_old.html
-Copyright: 2007-2013 the Sphinx Team
-License: BSD-2-clause
-
-Files: docs/_themes/sphinx_rtd_theme_violet/static/js/scrollspy.js
-Copyright: 2011-2014 Twitter, Inc.
-License: Expat
-
-Files: debian/*
-Copyright: 2014-2016, Stefan Breunig <stefan-debian@yrden.de>
-License: BSD-2-clause
-
-License: BSD-2-clause
- All rights reserved.
- .
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- .
- 1. Redistributions of source code must retain the above copyright notice, this
-    list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright notice,
-    this list of conditions and the following disclaimer in the documentation
-    and/or other materials provided with the distribution.
- .
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-License: Expat
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- .
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- .
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
diff --git a/debian/patches/remove_github_buttons b/debian/patches/remove_github_buttons
deleted file mode 100644
index e06c4692..00000000
--- a/debian/patches/remove_github_buttons
+++ /dev/null
@@ -1,22 +0,0 @@
-Description: Remove GitHub Buttons without replacement
- Upstream includes buttons to GitHub that display how
- often the project was starred. Due to privacy concerns
- these are removed from the documentation in Debian.
-Forwarded: not-needed
-Author: Stefan Breunig <stefan-debian@yrden.de>
-
-Index: packaging/docs/_themes/sphinx_rtd_theme_violet/layout.html
-===================================================================
---- packaging.orig/docs/_themes/sphinx_rtd_theme_violet/layout.html	2015-01-19 22:51:07.000000000 +0100
-+++ packaging/docs/_themes/sphinx_rtd_theme_violet/layout.html	2015-01-19 23:07:46.225297834 +0100
-@@ -176,10 +176,6 @@
-   {% endif %}
- 
- 
--  {% if theme_github_user and theme_github_repo %}
--  <script async defer id="github-bjs" src="https://buttons.github.io/buttons.js"></script>
--  {% endif %}
--
-   {%- block footer %} {% endblock %}
- 
- </body>
diff --git a/debian/patches/remove_new_version_check b/debian/patches/remove_new_version_check
deleted file mode 100644
index 753caba8..00000000
--- a/debian/patches/remove_new_version_check
+++ /dev/null
@@ -1,34 +0,0 @@
-Subject: Remove streamlink version check on startup
-Forwarded: not-needed
-Author: Stefan Breunig <stefan-debian@yrden.de>
-Bug-Debian: https://bugs.debian.org/750502
-
-Index: packaging/src/streamlink_cli/main.py
-===================================================================
---- packaging.orig/src/streamlink_cli/main.py	2015-05-02 22:17:15.771531546 +0200
-+++ packaging/src/streamlink_cli/main.py	2015-05-02 22:17:15.771531546 +0200
-@@ -866,9 +866,9 @@
-     setup_console()
-     setup_http_session()
-
--    if args.version_check or not args.no_version_check:
-+    if args.version_check:
-         with ignored(Exception):
--            check_version(force=args.version_check)
-+            check_version(force=True)
-
-     if args.plugins:
-         print_plugins()
-Index: packaging/src/streamlink_cli/argparser.py
-===================================================================
---- packaging.orig/src/streamlink_cli/argparser.py	2015-03-23 21:33:43.167182164 +0100
-+++ packaging/src/streamlink_cli/argparser.py	2015-05-02 22:18:38.469608924 +0200
-@@ -256,7 +256,7 @@
-     "--no-version-check",
-     action="store_true",
-     help="""
--    Do not check for new Streamlink releases.
-+    Unused, kept for compatibility reasons.
-     """
- )
- general.add_argument(
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644
index 40b2e941..00000000
--- a/debian/patches/series
+++ /dev/null
@@ -1,4 +0,0 @@
-remove_new_version_check
-remove_github_buttons
-use_debian_fonts
-use_debian_modernizr
diff --git a/debian/patches/use_debian_fonts b/debian/patches/use_debian_fonts
deleted file mode 100644
index c00a496a..00000000
--- a/debian/patches/use_debian_fonts
+++ /dev/null
@@ -1,19 +0,0 @@
-Description: Use Debian packaged fonts
- Remove Google Fonts link used by upstream and instead
- use local fonts. Except for "Roboto Slab", all are
- available on Debian.
-Forwarded: not-needed
-Author: Stefan Breunig <stefan-debian@yrden.de>
-
-Index: packaging/docs/_themes/sphinx_rtd_theme_violet/layout.html
-===================================================================
---- packaging.orig/docs/_themes/sphinx_rtd_theme_violet/layout.html	2015-01-19 22:50:36.613894253 +0100
-+++ packaging/docs/_themes/sphinx_rtd_theme_violet/layout.html	2015-01-19 22:50:59.298397091 +0100
-@@ -23,7 +23,6 @@
-   {% endif %}
- 
-   {# CSS #}
--  <link href='https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
- 
-   {# OPENSEARCH #}
-   {% if not embedded %}
diff --git a/debian/patches/use_debian_modernizr b/debian/patches/use_debian_modernizr
deleted file mode 100644
index 2fa2151e..00000000
--- a/debian/patches/use_debian_modernizr
+++ /dev/null
@@ -1,18 +0,0 @@
-Subject: Use Debian packaged modernizr
-Forwarded: not-needed
-Author: Stefan Breunig <stefan-debian@yrden.de>
-
-
-Index: packaging/docs/_themes/sphinx_rtd_theme_violet/layout.html
-===================================================================
---- packaging.orig/docs/_themes/sphinx_rtd_theme_violet/layout.html	2015-01-19 21:33:26.997832354 +0100
-+++ packaging/docs/_themes/sphinx_rtd_theme_violet/layout.html	2015-01-19 22:11:53.404504485 +0100
-@@ -68,7 +68,7 @@
-   {%- block extrahead %} {% endblock %}
- 
-   {# Keep modernizr in head - http://modernizr.com/docs/#installing #}
--  <script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js"></script>
-+  <script src="file:///usr/share/javascript/modernizr/modernizr.min.js"></script>
- 
- </head>
- 
diff --git a/debian/python-streamlink-doc.doc-base b/debian/python-streamlink-doc.doc-base
deleted file mode 100644
index ce641a8d..00000000
--- a/debian/python-streamlink-doc.doc-base
+++ /dev/null
@@ -1,9 +0,0 @@
-Document: python-streamlink-doc
-Title: streamlink documentation
-Author: Streamlink Team
-Abstract: The documentation explains the usage and how to write new plugins
-Section: Programming/Python
-
-Format: HTML
-Files: /usr/share/doc/python-streamlink-doc/html/*.html
-Index: /usr/share/doc/python-streamlink-doc/html/index.html
diff --git a/debian/python-streamlink-doc.docs b/debian/python-streamlink-doc.docs
deleted file mode 100644
index 41cf6521..00000000
--- a/debian/python-streamlink-doc.docs
+++ /dev/null
@@ -1,2 +0,0 @@
-requirements-docs.txt
-build/sphinx/html
diff --git a/debian/python-streamlink.install b/debian/python-streamlink.install
deleted file mode 100644
index 02a32673..00000000
--- a/debian/python-streamlink.install
+++ /dev/null
@@ -1,2 +0,0 @@
-usr/lib/python2*/*-packages/streamlink/*
-usr/lib/python2*/*-packages/streamlink-*.egg-info
diff --git a/debian/python3-streamlink.install b/debian/python3-streamlink.install
deleted file mode 100644
index d9527243..00000000
--- a/debian/python3-streamlink.install
+++ /dev/null
@@ -1,2 +0,0 @@
-usr/lib/python3*/*-packages/streamlink/*
-usr/lib/python3*/*-packages/streamlink-*.egg-info
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index 6a08e423..00000000
--- a/debian/rules
+++ /dev/null
@@ -1,19 +0,0 @@
-#! /usr/bin/make -f
-
-# Place streamlink executable in the correct place right away. This
-# also avoids overwrites by the Python 3 build, regardless of order.
-export PYBUILD_INSTALL_ARGS_python2 = --install-scripts usr/share/streamlink/
-
-%:
-	dh $@ --with python2,python3,sphinxdoc --buildsystem=pybuild
-
-override_dh_auto_install:
-	python setup.py build_sphinx -b man
-	python setup.py build_sphinx -b html
-	dh_auto_install
-
-override_dh_compress:
-	dh_compress -X.html
-
-override_dh_installchangelogs:
-	dh_installchangelogs CHANGELOG.md
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644
index 163aaf8d..00000000
--- a/debian/source/format
+++ /dev/null
@@ -1,1 +0,0 @@
-3.0 (quilt)
diff --git a/debian/streamlink.install b/debian/streamlink.install
deleted file mode 100644
index ea2ac8f6..00000000
--- a/debian/streamlink.install
+++ /dev/null
@@ -1,2 +0,0 @@
-usr/lib/python2.7/*-packages/streamlink_cli usr/share/streamlink
-usr/share/streamlink/streamlink
diff --git a/debian/streamlink.links b/debian/streamlink.links
deleted file mode 100644
index 8d5f2679..00000000
--- a/debian/streamlink.links
+++ /dev/null
@@ -1,1 +0,0 @@
-usr/share/streamlink/streamlink usr/bin/streamlink
diff --git a/debian/streamlink.manpages b/debian/streamlink.manpages
deleted file mode 100644
index ee7f722b..00000000
--- a/debian/streamlink.manpages
+++ /dev/null
@@ -1,1 +0,0 @@
-build/sphinx/man/streamlink.1
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index 4ebab9a1..00000000
--- a/debian/watch
+++ /dev/null
@@ -1,3 +0,0 @@
-version=3
-opts=uversionmangle=s/(rc|a|b|c)/~$1/ \
-http://pypi.debian.net/streamlink/streamlink-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
diff --git a/docs/_themes/sphinx_rtd_theme_violet/static/css/theme.css b/docs/_themes/sphinx_rtd_theme_violet/static/css/theme.css
index 6e112e53..88e1a1a2 100644
--- a/docs/_themes/sphinx_rtd_theme_violet/static/css/theme.css
+++ b/docs/_themes/sphinx_rtd_theme_violet/static/css/theme.css
@@ -4778,3 +4778,22 @@ a:visited:hover {
 .wy-table-responsive div.highlight pre {
   padding: 5px;
 }
+
+
+/* fix table layout on the install page */
+table.table-custom-layout {
+  table-layout: fixed;
+}
+table.table-custom-layout colgroup {
+  display: none;
+}
+table.table-custom-layout colgroup col {
+  width: auto;
+}
+table.table-custom-layout th:first-of-type {
+  width: 12rem;
+}
+table.table-custom-layout tbody tr td {
+  overflow: unset;
+  white-space: unset;
+}
diff --git a/docs/install.rst b/docs/install.rst
index 691bbd53..cfbeb0be 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -10,6 +10,8 @@ Installation
 Linux and BSD packages
 ----------------------
 
+.. rst-class:: table-custom-layout
+
 ==================================== ===========================================
 Distribution                         Installing
 ==================================== ===========================================
@@ -22,6 +24,16 @@ Distribution                         Installing
                                         # pacaur -S streamlink-git
 
                                      `Installing AUR packages`_
+`Debian (sid, testing)`_             .. code-block:: console
+
+                                        # apt update
+                                        # apt install streamlink
+`Debian (stable)`_                   .. code-block:: console
+
+                                        # wget -qO- "https://bintray.com/user/downloadSubjectPublicKey?username=amurzeau" | apt-key add -
+                                        # echo "deb https://dl.bintray.com/amurzeau/streamlink-debian stretch-backports main" | tee "/etc/apt/sources.list.d/streamlink.list"
+                                        # apt update
+                                        # apt install streamlink
 `Fedora`_                            .. code-block:: console
 
                                         # dnf install streamlink
@@ -35,12 +47,12 @@ Distribution                         Installing
 `NixOS`_                             `Installing NixOS packages`_
 `Solus`_                             .. code-block:: console
 
-                                        # eopkg install streamlink
+                                        $ sudo eopkg install streamlink
 `Ubuntu`_                            .. code-block:: console
 
-                                        # add-apt-repository ppa:nilarimogard/webupd8
-                                        # apt update
-                                        # apt install streamlink
+                                        $ sudo add-apt-repository ppa:nilarimogard/webupd8
+                                        $ sudo apt update
+                                        $ sudo apt install streamlink
 `Void`_                              .. code-block:: console
 
                                         # xbps-install streamlink
@@ -48,6 +60,8 @@ Distribution                         Installing
 
 .. _Arch Linux: https://www.archlinux.org/packages/community/any/streamlink/
 .. _Arch Linux (aur, git): https://aur.archlinux.org/packages/streamlink-git/
+.. _Debian (sid, testing): https://packages.debian.org/unstable/streamlink
+.. _Debian (stable): https://bintray.com/amurzeau/streamlink-debian/streamlink
 .. _Fedora: https://apps.fedoraproject.org/packages/python-streamlink
 .. _Gentoo Linux: https://packages.gentoo.org/package/net-misc/streamlink
 .. _NetBSD (pkgsrc): http://pkgsrc.se/multimedia/streamlink
@@ -59,9 +73,12 @@ Distribution                         Installing
 .. _Installing AUR packages: https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages
 .. _Installing NixOS packages: https://nixos.org/wiki/Install/remove_software#How_to_install_software
 
+
 Other platforms
 ---------------
 
+.. rst-class:: table-custom-layout
+
 ==================================== ===========================================
 Platform                             Installing
 ==================================== ===========================================
@@ -88,14 +105,19 @@ Microsoft Windows                    See `Windows binaries`_ and `Windows portab
 .. _Installing Homebrew packages: https://brew.sh
 .. _Installing Chocolatey packages: https://chocolatey.org
 
+
 Package maintainers
 -------------------
+
+.. rst-class:: table-custom-layout
+
 ==================================== ===========================================
 Distribution/Platform                Maintainer
 ==================================== ===========================================
 Arch                                 Giancarlo Razzolini <grazzolini at archlinux.org>
 Arch (aur, git)                      Josip Ponjavic <josipponjavic at gmail.com>
 Chocolatey                           Scott Walters <me at scowalt.com>
+Debian                               Alexis Murzeau <amubtdx at gmail.com>
 Fedora                               Mohamed El Morabity <melmorabity at fedoraproject.org>
 Gentoo                               soredake <fdsfgs at krutt.org>
 NetBSD                               Maya Rashish <maya at netbsd.org>
@@ -104,7 +126,8 @@ Solus                                Bryan T. Meyers <bmeyers at datadrake.com>
 Ubuntu                               Alin Andrei <andrew at webupd8.org>
 Void                                 wkuipers <wietse at kuiprs.nl>
 Windows binaries                     beardypig <beardypig at protonmail.com>
-Windows port. version                RosadinTV <RosadinTV at outlook.com>, beardypig <beardypig at protonmail.com>
+Windows port. version                RosadinTV <RosadinTV at outlook.com> |br|
+                                     beardypig <beardypig at protonmail.com>
 ==================================== ===========================================
 
 
@@ -129,6 +152,8 @@ manager, or by checking out the latest code with
 
 The commands listed here will also upgrade any existing version of Streamlink.
 
+.. rst-class:: table-custom-layout
+
 ==================================== ===========================================
 Version                              Installing
 ==================================== ===========================================
@@ -159,6 +184,8 @@ Dependencies
 
 To install Streamlink from source you will need these dependencies.
 
+.. rst-class:: table-custom-layout
+
 ==================================== ===========================================
 Name                                 Notes
 ==================================== ===========================================
@@ -250,7 +277,9 @@ Windows binaries
 .. important::
 
     Windows XP is not supported. |br|
-    Windows Vista requires at least SP2 to be installed. 
+    Windows Vista requires at least SP2 to be installed.
+
+.. rst-class:: table-custom-layout
 
 ==================================== ====================================
 Release                              Notes
@@ -289,19 +318,17 @@ To build the installer on your own, ``NSIS`` and ``pynsist`` need to be installe
 Windows portable version
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
+.. rst-class:: table-custom-layout
+
 ==================================== ===========================================
 Maintainer                           Links
 ==================================== ===========================================
-RosadinTV                            `Latest precompiled stable release`__
-
-                                     `Latest builder`__
-                                     
+RosadinTV                            `Latest precompiled stable release`__ |br|
+                                     `Latest builder`__ |br|
                                      `More info`__
 
-Beardypig                            `Latest precompiled stable release`__
-
-                                     `Latest builder`__
-
+Beardypig                            `Latest precompiled stable release`__ |br|
+                                     `Latest builder`__ |br|
                                      `More info`__
 ==================================== ===========================================
 
diff --git a/src/streamlink/plugins/afreeca.py b/src/streamlink/plugins/afreeca.py
index 89b02d3d..ec22e5ba 100644
--- a/src/streamlink/plugins/afreeca.py
+++ b/src/streamlink/plugins/afreeca.py
@@ -1,118 +1,170 @@
 import re
 
 from streamlink.plugin import Plugin
-from streamlink.plugin.api import http, validate
-from streamlink.stream import RTMPStream, HLSStream
-
-CHANNEL_INFO_URL = "http://live.afreecatv.com:8057/api/get_broad_state_list.php"
-KEEP_ALIVE_URL = "{server}/stream_keepalive.html"
-STREAM_INFO_URLS = {
-    "rtmp": "http://sessionmanager01.afreeca.tv:6060/broad_stream_assign.html",
-    "hls": "http://resourcemanager.afreeca.tv:9090/broad_stream_assign.html"
-}
-HLS_KEY_URL = "http://api.m.afreeca.com/broad/a/watch"
+from streamlink.plugin import PluginOptions
+from streamlink.plugin.api import http
+from streamlink.plugin.api import validate
+from streamlink.stream import HLSStream
+
+CHANNEL_API_URL = "http://live.afreecatv.com:8057/afreeca/player_live_api.php"
+STREAM_INFO_URLS = "{rmd}/broad_stream_assign.html"
 
 CHANNEL_RESULT_ERROR = 0
 CHANNEL_RESULT_OK = 1
 
+QUALITYS = ["original", "hd", "sd"]
+
+QUALITY_WEIGHTS = {
+    "original": 1080,
+    "hd": 720,
+    "sd": 480
+}
 
-_url_re = re.compile(r"http(s)?://(\w+\.)?afreeca(tv)?.com/(?P<username>\w+)(/\d+)?")
+_url_re = re.compile(r"http(s)?://(?P<cdn>\w+\.)?afreeca(tv)?\.com/(?P<username>\w+)(/\d+)?")
 
 _channel_schema = validate.Schema(
     {
         "CHANNEL": {
             "RESULT": validate.transform(int),
-            "BROAD_INFOS": [{
-                "list": [{
-                    "nBroadNo": validate.text
-                }]
-            }]
+            validate.optional("BPWD"): validate.text,
+            validate.optional("BNO"): validate.text,
+            validate.optional("RMD"): validate.text,
+            validate.optional("AID"): validate.text,
+            validate.optional("CDN"): validate.text
         }
     },
     validate.get("CHANNEL")
 )
+
 _stream_schema = validate.Schema(
     {
         validate.optional("view_url"): validate.url(
             scheme=validate.any("rtmp", "http")
-        )
+        ),
+        "stream_status": validate.text
     }
 )
 
 
 class AfreecaTV(Plugin):
+
+    login_url = "https://member.afreecatv.com:8111/login/LoginAction.php"
+
+    options = PluginOptions({
+        "username": None,
+        "password": None
+    })
+
     @classmethod
     def can_handle_url(self, url):
         return _url_re.match(url)
 
+    @classmethod
+    def stream_weight(cls, key):
+        weight = QUALITY_WEIGHTS.get(key)
+        if weight:
+            return weight, "afreeca"
+
+        return Plugin.stream_weight(key)
+
     def _get_channel_info(self, username):
-        headers = {
-            "Referer": self.url
-        }
-        params = {
-            "uid": username
+        data = {
+            "bid": username,
+            "mode": "landing",
+            "player_type": "html5"
         }
-        res = http.get(CHANNEL_INFO_URL, params=params, headers=headers)
 
+        res = http.post(CHANNEL_API_URL, data=data)
         return http.json(res, schema=_channel_schema)
 
-    def _get_hls_key(self, broadcast, username):
+    def _get_hls_key(self, broadcast, username, quality):
         headers = {
             "Referer": self.url
         }
+
         data = {
-            "bj_id": username,
-            "broad_no": broadcast
+            "bid": username,
+            "bno": broadcast,
+            "pwd": "",
+            "quality": quality,
+            "type": "pwd"
         }
-        res = http.post(HLS_KEY_URL, data=data, headers=headers)
-
-        return http.json(res)
+        res = http.post(CHANNEL_API_URL, data=data, headers=headers)
+        return http.json(res, schema=_channel_schema)
 
-    def _get_stream_info(self, broadcast, type):
+    def _get_stream_info(self, broadcast, quality, cdn, rmd):
         params = {
-            "return_type": "gs_cdn",
-            "use_cors": "true",
-            "cors_origin_url": "m.afreeca.com",
-            "broad_no": "{broadcast}-mobile-hd-{type}".format(**locals()),
-            "broad_key": "{broadcast}-flash-hd-{type}".format(**locals())
+            "return_type": cdn,
+            "broad_key": "{broadcast}-flash-{quality}-hls".format(**locals())
         }
-        res = http.get(STREAM_INFO_URLS[type], params=params)
+        res = http.get(STREAM_INFO_URLS.format(rmd=rmd), params=params)
         return http.json(res, schema=_stream_schema)
 
-    def _get_hls_stream(self, broadcast, username):
-        keyjson = self._get_hls_key(broadcast, username)
-        if keyjson["result"] != CHANNEL_RESULT_OK:
+    def _get_hls_stream(self, broadcast, username, quality, cdn, rmd):
+        keyjson = self._get_hls_key(broadcast, username, quality)
+
+        if keyjson["RESULT"] != CHANNEL_RESULT_OK:
             return
-        key = keyjson["data"]["hls_authentication_key"]
-        info = self._get_stream_info(broadcast, "hls")
+        key = keyjson["AID"]
+
+        info = self._get_stream_info(broadcast, quality, cdn, rmd)
+
         if "view_url" in info:
             return HLSStream(self.session, info["view_url"], params=dict(aid=key))
 
-    def _get_rtmp_stream(self, broadcast):
-        info = self._get_stream_info(broadcast, "rtmp")
-        if "view_url" in info:
-            params = dict(rtmp=info["view_url"])
-            return RTMPStream(self.session, params=params, redirect=True)
+    def _login(self, username, password):
+        data = {
+            "szWork": "login",
+            "szType": "json",
+            "szUid": username,
+            "szPassword": password,
+            "isSaveId": "true",
+            "isSavePw": "false",
+            "isSaveJoin": "false"
+        }
+
+        res = http.post(self.login_url, data=data)
+        res = http.json(res)
+        if res["RESULT"] == 1:
+            return True
+        else:
+            return False
 
     def _get_streams(self):
+        if not self.session.get_option("hls-segment-ignore-names"):
+            ignore_segment = ["_0", "_1", "_2"]
+            self.session.set_option("hls-segment-ignore-names", ignore_segment)
+
+        login_username = self.get_option("username")
+        login_password = self.get_option("password")
+        if login_username and login_password:
+            self.logger.debug("Attempting login as {0}".format(login_username))
+            if self._login(login_username, login_password):
+                self.logger.info("Successfully logged in as {0}".format(login_username))
+            else:
+                self.logger.info("Failed to login as {0}".format(login_username))
+
         match = _url_re.match(self.url)
         username = match.group("username")
 
         channel = self._get_channel_info(username)
-        if channel["RESULT"] != CHANNEL_RESULT_OK:
+        if channel.get("BPWD") == "Y":
+            self.logger.error("Stream is Password-Protected")
             return
-
-        broadcast = channel["BROAD_INFOS"][0]["list"][0]["nBroadNo"]
-        if not broadcast:
+        elif channel.get("RESULT") == -6:
+            self.logger.error("Login required")
+            return
+        elif channel.get("RESULT") != CHANNEL_RESULT_OK:
             return
 
-        flash_stream = self._get_rtmp_stream(broadcast)
-        if flash_stream:
-            yield "live", flash_stream
+        (broadcast, rmd, cdn) = (channel["BNO"], channel["RMD"], channel["CDN"])
+        if not (broadcast and rmd and cdn):
+            return
 
-        mobile_stream = self._get_hls_stream(broadcast, username)
-        if mobile_stream:
-            yield "live", mobile_stream
+        for qkey in QUALITYS:
+            hls_stream = self._get_hls_stream(broadcast, username, qkey, cdn, rmd)
+            if hls_stream:
+                yield qkey, hls_stream
 
 
 __plugin__ = AfreecaTV
diff --git a/src/streamlink/plugins/afreecatv.py b/src/streamlink/plugins/afreecatv.py
deleted file mode 100644
index d51a1873..00000000
--- a/src/streamlink/plugins/afreecatv.py
+++ /dev/null
@@ -1,53 +0,0 @@
-import re
-
-from streamlink.plugin import Plugin
-from streamlink.plugin.api import http, validate
-from streamlink.plugin.api.utils import parse_query
-from streamlink.stream import RTMPStream
-
-
-VIEW_LIVE_API_URL = "http://api.{region}/live/view_live.php"
-
-_url_re = re.compile(r"https?://(\w+\.)?(?P<region>afreecatv\.com\.tw|afreeca\.tv|afreecatv\.jp)/(?P<channel>[\w\-_]+)")
-
-_view_live_schema = validate.Schema(
-    {
-        "channel": {
-            "strm": [{
-                "bps": validate.text,
-                "purl": validate.url(scheme="rtmp")
-            }]
-        },
-    },
-    validate.get("channel"),
-    validate.get("strm")
-)
-
-
-class AfreecaTV(Plugin):
-    @classmethod
-    def can_handle_url(cls, url):
-        return _url_re.match(url)
-
-    def _get_streams(self):
-        match = _url_re.match(self.url)
-
-        params = {
-            "pt": "view",
-            "bid": match.group("channel")
-        }
-
-        res = http.get(VIEW_LIVE_API_URL.format(region=match.group("region")), params=params)
-
-        streams = http.json(res, schema=_view_live_schema)
-
-        for stream in streams:
-            stream_name = "{0}p".format(stream["bps"])
-            stream_params = {
-                "rtmp": stream["purl"],
-                "live": True
-            }
-            yield stream_name, RTMPStream(self.session, stream_params)
-
-
-__plugin__ = AfreecaTV
diff --git a/src/streamlink/plugins/bbciplayer.py b/src/streamlink/plugins/bbciplayer.py
index 0a5546c8..f0eb2822 100644
--- a/src/streamlink/plugins/bbciplayer.py
+++ b/src/streamlink/plugins/bbciplayer.py
@@ -2,46 +2,39 @@ from __future__ import print_function
 
 import base64
 import re
-from functools import partial
 from hashlib import sha1
 
+from streamlink.compat import parse_qsl, urlparse
 from streamlink.plugin import Plugin, PluginOptions
 from streamlink.plugin.api import http
 from streamlink.plugin.api import validate
 from streamlink.stream import HDSStream
 from streamlink.stream import HLSStream
-from streamlink.utils import parse_xml, parse_json
+from streamlink.utils import parse_json
 
 
 class BBCiPlayer(Plugin):
+    """
+    Allows streaming of live channels from bbc.co.uk/iplayer/live/* and of iPlayer programmes from
+    bbc.co.uk/iplayer/episode/*
+    """
     url_re = re.compile(r"""https?://(?:www\.)?bbc.co.uk/iplayer/
         (
             episode/(?P<episode_id>\w+)|
             live/(?P<channel_name>\w+)
         )
     """, re.VERBOSE)
-    mediator_re = re.compile(r'window\.mediatorDefer\s*=\s*page\([^,]*,\s*(\{.*?})\);', re.DOTALL)
+    mediator_re = re.compile(r'window\.mediatorDefer\s*=\s*page\([^,]*,\s*({.*?})\);', re.DOTALL)
     tvip_re = re.compile(r'event_master_brand=(\w+?)&')
-    account_locals_re = re.compile(r'window.bbcAccount.locals\s*=\s*(\{.*?});')
+    account_locals_re = re.compile(r'window.bbcAccount.locals\s*=\s*({.*?});')
     swf_url = "http://emp.bbci.co.uk/emp/SMPf/1.18.3/StandardMediaPlayerChromelessFlash.swf"
     hash = base64.b64decode(b"N2RmZjc2NzFkMGM2OTdmZWRiMWQ5MDVkOWExMjE3MTk5MzhiOTJiZg==")
     api_url = ("http://open.live.bbc.co.uk/mediaselector/6/select/"
                "version/2.0/mediaset/{platform}/vpid/{vpid}/format/json/atk/{vpid_hash}/asn/1/")
     platforms = ("pc", "iptv-all")
-    config_url = "http://www.bbc.co.uk/idcta/config"
+    session_url = "https://session.bbc.com/session"
     auth_url = "https://account.bbc.com/signin"
 
-    config_schema = validate.Schema(
-        validate.transform(parse_json),
-        {
-            "signin_url": validate.url(),
-            "identity": {
-                "cookieAgeDays": int,
-                "accessTokenCookieName": validate.text,
-                "idSignedInCookieName": validate.text
-            }
-        }
-    )
     mediator_schema = validate.Schema(
         {
             "episode": {
@@ -69,13 +62,47 @@ class BBCiPlayer(Plugin):
 
     @classmethod
     def can_handle_url(cls, url):
+        """ Confirm plugin can handle URL """
         return cls.url_re.match(url) is not None
 
     @classmethod
     def _hash_vpid(cls, vpid):
         return sha1(cls.hash + str(vpid).encode("utf8")).hexdigest()
 
+    @classmethod
+    def _extract_nonce(cls, http_result):
+        """
+        Given an HTTP response from the sessino endpoint, extract the nonce, so we can "sign" requests with it.
+        We don't really sign the requests in the traditional sense of a nonce, we just incude them in the auth requests.
+
+        :param http_result: HTTP response from the bbc session endpoint.
+        :type http_result: requests.Response
+        :return: nonce to "sign" url requests with
+        :rtype: string
+        """
+
+        # Extract the redirect URL from the last call
+        last_redirect_url = urlparse(http_result.history[-1].request.url)
+        last_redirect_query = dict(parse_qsl(last_redirect_url.query))
+        # Extract the nonce from the query string in the redirect URL
+        final_url = urlparse(last_redirect_query['goto'])
+        goto_url = dict(parse_qsl(final_url.query))
+        goto_url_query = parse_json(goto_url['state'])
+
+        # Return the nonce we can use for future queries
+        return goto_url_query['nonce']
+
     def find_vpid(self, url, res=None):
+        """
+        Find the Video Packet ID in the HTML for the provided URL
+
+        :param url: URL to download, if res is not provided.
+        :param res: Provide a cached version of the HTTP response to search
+        :type url: string
+        :type res: requests.Response
+        :return: Video Packet ID for a Programme in iPlayer
+        :rtype: string
+        """
         self.logger.debug("Looking for vpid on {0}", url)
         # Use pre-fetched page if available
         res = res or http.get(url)
@@ -103,28 +130,37 @@ class BBCiPlayer(Plugin):
                         for s in HLSStream.parse_variant_playlist(self.session, connection["href"]).items():
                             yield s
 
-    def login(self, ptrt_url, context="tvandiplayer"):
-        # get the site config, to find the signin url
-        config = http.get(self.config_url, params=dict(ptrt=ptrt_url), schema=self.config_schema)
-
-        res = http.get(config["signin_url"],
-                       params=dict(userOrigin=context, context=context),
-                       headers={"Referer": self.url})
-        m = self.account_locals_re.search(res.text)
-        if m:
-            auth_data = parse_json(m.group(1))
-            res = http.post(self.auth_url,
-                            params=dict(context=auth_data["userOrigin"],
-                                        ptrt=auth_data["ptrt"]["value"],
-                                        userOrigin=auth_data["userOrigin"],
-                                        nonce=auth_data["nonce"]),
-                            data=dict(jsEnabled="false", attempts=0, username=self.get_option("username"),
-                                      password=self.get_option("password")))
-            # redirects to ptrt_url on successful login
-            if res.url == ptrt_url:
-                return res
-        else:
-            self.logger.error("Could not authenticate, could not find the authentication nonce")
+    def login(self, ptrt_url):
+        """
+        Create session using BBC ID. See https://www.bbc.co.uk/usingthebbc/account/
+
+        :param ptrt_url: The snapback URL to redirect to after successful authentication
+        :type ptrt_url: string
+        :return: Whether authentication was successful
+        :rtype: bool
+        """
+        session_res = http.get(
+            self.session_url,
+            params=dict(ptrt=ptrt_url)
+        )
+
+        http_nonce = self._extract_nonce(session_res)
+
+        res = http.post(
+            self.auth_url,
+            params=dict(
+                ptrt=ptrt_url,
+                nonce=http_nonce
+            ),
+            data=dict(
+                jsEnabled=True,
+                username=self.get_option("username"),
+                password=self.get_option('password'),
+                attempts=0
+            ),
+            headers={"Referer": self.url})
+
+        return len(res.history) != 0
 
     def _get_streams(self):
         if not self.get_option("username"):
@@ -133,8 +169,7 @@ class BBCiPlayer(Plugin):
             return
         self.logger.info("A TV License is required to watch BBC iPlayer streams, see the BBC website for more "
                          "information: https://www.bbc.co.uk/iplayer/help/tvlicence")
-        page_res = self.login(self.url)
-        if not page_res:
+        if not self.login(self.url):
             self.logger.error("Could not authenticate, check your username and password")
             return
 
@@ -144,7 +179,7 @@ class BBCiPlayer(Plugin):
 
         if episode_id:
             self.logger.debug("Loading streams for episode: {0}", episode_id)
-            vpid = self.find_vpid(self.url, res=page_res)
+            vpid = self.find_vpid(self.url)
             if vpid:
                 self.logger.debug("Found VPID: {0}", vpid)
                 for s in self.mediaselector(vpid):
diff --git a/src/streamlink/plugins/ok_live.py b/src/streamlink/plugins/ok_live.py
new file mode 100644
index 00000000..32edad59
--- /dev/null
+++ b/src/streamlink/plugins/ok_live.py
@@ -0,0 +1,41 @@
+import urllib3
+import re
+
+from streamlink.plugin import Plugin
+from streamlink.plugin.api import http, validate
+from streamlink.plugin.api import useragents
+from streamlink.stream import HLSStream
+
+_url_re = re.compile(r"https?://(www\.)?ok\.ru/live/\d+")
+_vod_re = re.compile(r";(?P<hlsurl>[^;]+video\.m3u8)")
+
+_schema = validate.Schema(
+    validate.transform(_vod_re.search),
+    validate.any(
+        None,
+        validate.all(
+            validate.get("hlsurl"),
+            validate.url()
+        )
+    )
+)
+
+class OK_live(Plugin):
+    """
+    Support for ok.ru live stream: http://www.ok.ru/live/
+    """
+    @classmethod
+    def can_handle_url(cls, url):
+        return _url_re.match(url) is not None
+
+    def _get_streams(self):
+        headers = {
+            'User-Agent': useragents.CHROME,
+            'Referer': self.url
+        }
+
+        hls  = http.get(self.url, headers=headers, schema=_schema)
+        return HLSStream.parse_variant_playlist(self.session, hls, headers=headers)
+
+
+__plugin__ = OK_live
\ No newline at end of file
diff --git a/src/streamlink/plugins/orf_tvthek.py b/src/streamlink/plugins/orf_tvthek.py
index 0baaee5d..53d5b5e9 100644
--- a/src/streamlink/plugins/orf_tvthek.py
+++ b/src/streamlink/plugins/orf_tvthek.py
@@ -47,6 +47,8 @@ class ORFTVThek(Plugin):
             except KeyError:
                 continue
             stream = HLSStream.parse_variant_playlist(self.session, url)
+            # work around broken HTTP connection persistence by acquiring a new connection
+            http.close()
             streams.update(stream)
 
         return streams
diff --git a/src/streamlink/plugins/tf1.py b/src/streamlink/plugins/tf1.py
index 2b7e2628..b0235d60 100644
--- a/src/streamlink/plugins/tf1.py
+++ b/src/streamlink/plugins/tf1.py
@@ -8,13 +8,13 @@ from streamlink.stream import HLSStream
 
 
 class TF1(Plugin):
-    url_re = re.compile(r"https?://(?:www\.)?(?:tf1\.fr/(\w+)/direct|(lci).fr/direct)/?")
+    url_re = re.compile(r"https?://(?:www\.)?(?:tf1\.fr/(tf1|tmc|tfx|tf1-series-films)/direct|(lci).fr/direct)/?")
     embed_url = "http://www.wat.tv/embedframe/live{0}"
     embed_re = re.compile(r"urlLive.*?:.*?\"(http.*?)\"", re.MULTILINE)
     api_url = "http://www.wat.tv/get/{0}/591997"
     swf_url = "http://www.wat.tv/images/v70/PlayerLite.swf"
-    hds_channel_remap = {"tf1": "androidliveconnect", "lci": "androidlivelci"}
-    hls_channel_remap = {"lci": "LCI", "tf1": "V4"}
+    hds_channel_remap = {"tf1": "androidliveconnect", "lci": "androidlivelci", "tfx" : "nt1live", "tf1-series-films" : "hd1live" }
+    hls_channel_remap = {"lci": "LCI", "tf1": "V4", "tfx" : "nt1", "tf1-series-films" : "hd1" }
 
     @classmethod
     def can_handle_url(cls, url):
diff --git a/src/streamlink/stream/hls.py b/src/streamlink/stream/hls.py
index 88bc34a4..1532ffa4 100644
--- a/src/streamlink/stream/hls.py
+++ b/src/streamlink/stream/hls.py
@@ -1,3 +1,4 @@
+import re
 import struct
 from collections import defaultdict, namedtuple
 
@@ -36,11 +37,19 @@ class HLSStreamWriter(SegmentedStreamWriter):
         kwargs["retries"] = options.get("hls-segment-attempts")
         kwargs["threads"] = options.get("hls-segment-threads")
         kwargs["timeout"] = options.get("hls-segment-timeout")
+        kwargs["ignore_names"] = options.get("hls-segment-ignore-names")
         SegmentedStreamWriter.__init__(self, reader, *args, **kwargs)
 
         self.byterange_offsets = defaultdict(int)
         self.key_data = None
         self.key_uri = None
+        if self.ignore_names:
+            # creates a regex from a list of segment names,
+            # this will be used to ignore segments.
+            self.ignore_names = list(set(self.ignore_names))
+            self.ignore_names = "|".join(list(map(re.escape, self.ignore_names)))
+            self.ignore_names_re = re.compile(r"(?:{blacklist})\.ts".format(
+                    blacklist=self.ignore_names),  re.IGNORECASE)
 
     def create_decryptor(self, key, sequence):
         if key.method != "AES-128":
@@ -87,6 +96,11 @@ class HLSStreamWriter(SegmentedStreamWriter):
 
         try:
             request_params = self.create_request_params(sequence)
+            # skip ignored segment names
+            if self.ignore_names and self.ignore_names_re.search(sequence.segment.uri):
+                self.logger.debug("Skipping segment {0}".format(sequence.num))
+                return
+
             return self.session.http.get(sequence.segment.uri,
                                          timeout=self.timeout,
                                          exception=StreamError,
diff --git a/src/streamlink/stream/segmented.py b/src/streamlink/stream/segmented.py
index 613b1064..71cc17c6 100644
--- a/src/streamlink/stream/segmented.py
+++ b/src/streamlink/stream/segmented.py
@@ -68,7 +68,7 @@ class SegmentedStreamWriter(Thread):
     and finally writing the data to the buffer.
     """
 
-    def __init__(self, reader, size=20, retries=None, threads=None, timeout=None):
+    def __init__(self, reader, size=20, retries=None, threads=None, timeout=None, ignore_names=None):
         self.closed = False
         self.reader = reader
         self.stream = reader.stream
@@ -86,6 +86,7 @@ class SegmentedStreamWriter(Thread):
 
         self.retries = retries
         self.timeout = timeout
+        self.ignore_names = ignore_names
         self.executor = futures.ThreadPoolExecutor(max_workers=threads)
         self.futures = queue.Queue(size)
 
diff --git a/src/streamlink_cli/argparser.py b/src/streamlink_cli/argparser.py
index 0e734ccf..cfe26f44 100644
--- a/src/streamlink_cli/argparser.py
+++ b/src/streamlink_cli/argparser.py
@@ -708,6 +708,22 @@ transport.add_argument(
 
     Default is 10.0.
     """)
+transport.add_argument(
+    "--hls-segment-ignore-names",
+    metavar="NAMES",
+    type=comma_list,
+    help="""
+    A comma-delimited list of segment names that will not be fetched.
+
+    Example: --hls-segment-ignore-names 000,001,002
+
+    This will ignore every segment that ends with 000.ts, 001.ts and 002.ts
+
+    Default is None.
+
+    Note: The --hls-timeout must be increased, to a time that is longer than the ignored break.
+    """
+)
 transport.add_argument(
     "--hls-audio-select",
     type=str,
@@ -1356,6 +1372,20 @@ plugin.add_argument(
     and reauthenticate.
     """
 )
+plugin.add_argument(
+    "--afreeca-username",
+    metavar="USERNAME",
+    help="""
+    The username used to register with afreecatv.com.
+    """
+)
+plugin.add_argument(
+    "--afreeca-password",
+    metavar="PASSWORD",
+    help="""
+    A afreecatv.com account password to use with --afreeca-username.
+    """
+)
 
 # Deprecated options
 stream.add_argument(
diff --git a/src/streamlink_cli/main.py b/src/streamlink_cli/main.py
index e094fc52..c9128c24 100644
--- a/src/streamlink_cli/main.py
+++ b/src/streamlink_cli/main.py
@@ -1,5 +1,6 @@
 import errno
 import os
+import platform
 import requests
 import sys
 import signal
@@ -9,8 +10,11 @@ from contextlib import closing
 from distutils.version import StrictVersion
 from functools import partial
 from itertools import chain
+from socks import __version__ as socks_version
 from time import sleep
+from websocket import __version__ as websocket_version
 
+from streamlink import __version__ as streamlink_version
 from streamlink import (Streamlink, StreamError, PluginError,
                         NoPluginError)
 from streamlink.cache import Cache
@@ -719,6 +723,9 @@ def setup_options():
     if args.hls_segment_timeout:
         streamlink.set_option("hls-segment-timeout", args.hls_segment_timeout)
 
+    if args.hls_segment_ignore_names:
+        streamlink.set_option("hls-segment-ignore-names", args.hls_segment_ignore_names)
+
     if args.hls_timeout:
         streamlink.set_option("hls-timeout", args.hls_timeout)
 
@@ -960,6 +967,17 @@ def setup_plugin_options():
         streamlink.set_plugin_option("zattoo", "purge_credentials",
                                      args.zattoo_purge_credentials)
 
+    if args.afreeca_username:
+        streamlink.set_plugin_option("afreeca", "username", args.afreeca_username)
+
+    if args.afreeca_username and not args.afreeca_password:
+        afreeca_password = console.askpass("Enter afreecatv account password: ")
+    else:
+        afreeca_password = args.afreeca_password
+
+    if afreeca_password:
+        streamlink.set_plugin_option("afreeca", "password", afreeca_password)
+
     # Deprecated options
     if args.jtv_legacy_names:
         console.logger.warning("The option --jtv/twitch-legacy-names is "
@@ -992,6 +1010,26 @@ def check_root():
             console.logger.info("streamlink is running as root! Be careful!")
 
 
+def log_current_versions():
+    """Show current installed versions"""
+    if args.loglevel == "debug":
+        # MAC OS X
+        if sys.platform == "darwin":
+            os_version = "macOS {0}".format(platform.mac_ver()[0])
+        # Windows
+        elif sys.platform.startswith("win"):
+            os_version = "{0} {1}".format(platform.system(), platform.release())
+        # linux / other
+        else:
+            os_version = platform.platform()
+
+        console.logger.debug("OS:         {0}".format(os_version))
+        console.logger.debug("Python:     {0}".format(platform.python_version()))
+        console.logger.debug("Streamlink: {0}".format(streamlink_version))
+        console.logger.debug("Requests({0}), Socks({1}), Websocket({2})".format(
+            requests.__version__, socks_version, websocket_version))
+
+
 def check_version(force=False):
     cache = Cache(filename="cli.json")
     latest_version = cache.get("latest_version")
@@ -1031,6 +1069,7 @@ def main():
     setup_console()
     setup_http_session()
     check_root()
+    log_current_versions()
 
     if args.version_check or (not args.no_version_check and args.auto_version_check):
         with ignored(Exception):


Test patch:
diff --git a/tests/test_plugin_afreeca.py b/tests/test_plugin_afreeca.py
new file mode 100644
index 00000000..fffc7cab
--- /dev/null
+++ b/tests/test_plugin_afreeca.py
@@ -0,0 +1,22 @@
+import unittest
+
+from streamlink.plugins.afreeca import AfreecaTV
+
+
+class TestPluginAfreecaTV(unittest.TestCase):
+    def test_can_handle_url(self):
+        should_match = [
+            "http://afreecatv.com/exampleuser",
+            "http://afreecatv.com/exampleuser/123123123",
+            "http://play.afreecatv.com/exampleuser",
+            "http://play.afreecatv.com/exampleuser/123123123",
+        ]
+        for url in should_match:
+            self.assertTrue(AfreecaTV.can_handle_url(url))
+
+        should_not_match = [
+            "http://www.afreecatv.com.tw",
+            "http://www.afreecatv.jp",
+        ]
+        for url in should_not_match:
+            self.assertFalse(AfreecaTV.can_handle_url(url))
diff --git a/tests/test_plugin_bbciplayer.py b/tests/test_plugin_bbciplayer.py
index ba3bb23c..cef746fe 100644
--- a/tests/test_plugin_bbciplayer.py
+++ b/tests/test_plugin_bbciplayer.py
@@ -1,12 +1,18 @@
+import json
+import logging
 import unittest
 
+from requests import Response, Request
+
+from streamlink.compat import urlencode
 from streamlink.plugins.bbciplayer import BBCiPlayer
 
 
 class TestPluginBBCiPlayer(unittest.TestCase):
     def test_can_handle_url(self):
         # should match
-        self.assertTrue(BBCiPlayer.can_handle_url("http://www.bbc.co.uk/iplayer/episode/b00ymh67/madagascar-1-island-of-marvels"))
+        self.assertTrue(
+            BBCiPlayer.can_handle_url("http://www.bbc.co.uk/iplayer/episode/b00ymh67/madagascar-1-island-of-marvels"))
         self.assertTrue(BBCiPlayer.can_handle_url("http://www.bbc.co.uk/iplayer/live/bbcone"))
 
         # shouldn't match
@@ -19,3 +25,24 @@ class TestPluginBBCiPlayer(unittest.TestCase):
             "71c345435589c6ddeea70d6f252e2a52281ecbf3",
             BBCiPlayer._hash_vpid("1234567890")
         )
+
+    def test_extract_nonce(self):
+        mock_nonce = "mock-nonce-nse"
+
+        last_response = Response()
+        last_response.request = Request('GET', "http://example.com/?" + urlencode(dict(
+            goto="http://example.com/?" + urlencode(dict(
+                state=json.dumps(dict(nonce=mock_nonce))
+            ))
+        )))
+
+        mock_response = Response()
+        mock_response.history = [
+            Response(),  # Add some extra dummy responses in to make sure we always get the last
+            Response(),
+            last_response
+        ]
+
+        self.assertEqual(BBCiPlayer._extract_nonce(mock_response), mock_nonce)
+
+
diff --git a/tests/test_plugin_ok_live.py b/tests/test_plugin_ok_live.py
new file mode 100644
index 00000000..60a02bcc
--- /dev/null
+++ b/tests/test_plugin_ok_live.py
@@ -0,0 +1,15 @@
+import unittest
+
+from streamlink.plugins.ok_live import OK_live
+
+
+class TestPluginOK_live(unittest.TestCase):
+    def test_can_handle_url(self):
+        # should match
+        self.assertTrue(OK_live.can_handle_url("https://ok.ru/live/12345"))
+        self.assertTrue(OK_live.can_handle_url("http://ok.ru/live/12345"))
+        self.assertTrue(OK_live.can_handle_url("http://www.ok.ru/live/12345"))
+
+        # shouldn't match
+        self.assertFalse(OK_live.can_handle_url("http://www.tvcatchup.com/"))
+        self.assertFalse(OK_live.can_handle_url("http://www.youtube.com/"))
diff --git a/tests/test_plugin_tf1.py b/tests/test_plugin_tf1.py
index e8783242..77afd8d8 100644
--- a/tests/test_plugin_tf1.py
+++ b/tests/test_plugin_tf1.py
@@ -7,12 +7,16 @@ class TestPluginTF1(unittest.TestCase):
     def test_can_handle_url(self):
         # should match
         self.assertTrue(TF1.can_handle_url("http://tf1.fr/tf1/direct/"))
+        self.assertTrue(TF1.can_handle_url("http://tf1.fr/tfx/direct/"))
+        self.assertTrue(TF1.can_handle_url("http://tf1.fr/tf1-series-films/direct/"))
         self.assertTrue(TF1.can_handle_url("http://lci.fr/direct"))
         self.assertTrue(TF1.can_handle_url("http://www.lci.fr/direct"))
         self.assertTrue(TF1.can_handle_url("http://tf1.fr/tmc/direct"))
 
         # shouldn't match
         self.assertFalse(TF1.can_handle_url("http://tf1.fr/direct"))
+#       self.assertFalse(TF1.can_handle_url("http://tf1.fr/nt1/direct")) NOTE : TF1 redirect old channel names to new ones (for now).
+#       self.assertFalse(TF1.can_handle_url("http://tf1.fr/hd1/direct"))
         self.assertFalse(TF1.can_handle_url("http://www.tf1.fr/direct"))
         self.assertFalse(TF1.can_handle_url("http://www.tvcatchup.com/"))
         self.assertFalse(TF1.can_handle_url("http://www.youtube.com/"))