Repository: streamlink/streamlink

Issue:
Nightly Hosting Costs
I have been reviewing the costs for hosting the nightly builds of streamlink on S3. For the month of April, `streamlink-lastest.exe` was downloaded 11,228 times and used 133GB of transfer bandwidth. The cost of transfers was $11.98 and the cost of storage and HTTP requests was $0.69, with a total cost of $12.67+tax. With the gaining popularity of `streamlink` I can only see that cost increasing, I would like to reduce the cost a bit ($15 a month is fairly expensive for me.) 

With the addition of `ffmpeg` we added some nice features, and the possibility for further future enhancements, but we also bloated the installer (and the git repo, as discussed on other issues.) The installer is approx. 38MB including the `ffmpeg.exe`, and approx. 15MB without `ffmpeg.exe`. 

I was thinking we could remove `ffmpeg` from the installer and either allow the user to install it themselves, or download the `ffmpeg.exe` on demand in the installer. I prefer the download during the installer option as it means the user doesn't have to install it manually but the installer is smaller (we can download `ffmpeg.exe` from github). 

Alternatively we could set up a different repo with scheduled builds on travis, to build the nightly periodically and automatically add a release to that repo. 

Thoughts @gravyboat, @bastimeyer, @cdrage? 

Discussion:

Patch:
diff --git a/.travis.yml b/.travis.yml
index c6871830..874d4514 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -39,6 +39,9 @@ addons:
     packages:
     - nsis
 
+before_deploy:
+  - ./script/bintrayconfig.sh
+
 deploy:
   - provider: releases
     api_key: "${RELEASES_API_KEY}"
@@ -54,15 +57,19 @@ deploy:
     on:
       tags: true
       condition: $BUILD_INSTALLER = yes
-  - provider: s3
+  - provider: bintray
+    file: build/bintray-nightly.json
+    user: "${BINTRAY_USER}"
+    key: "${BINTRAY_KEY}"
     skip_cleanup: true
-    local_dir: $STREAMLINK_INSTALLER_DIST_DIR
-    bucket: streamlink-builds
-    upload-dir: nightly/windows
     on:
       branch: master
-      condition: $BUILD_INSTALLER = yes
-    secret_access_key:
-      secure: 3abBcpXNxhWDJyznMA/wDmQ+lUSaJVwi62aXHjFxA7Zrz/CidbHmXkNedViWTOivMyVcM4Ypej4JD1V74Uo78GN09TICRbSc9fTF08O8Hu+hKiAcVWepfmuV54nyoQHY5mcxBPwgUZwnTwSYLzKXCqDEmQXrUrM313m4f2cbDSQgo6VPoCqE+U7JTVaUI6asutsErvK7vfw9EqGSumndRNjQdD0triubs5kh6WPv5STpKrkUNWX95fjBbkuf5hy+DIQN22hLpHYwqV3DoTnhWzbPy8/OnCKoUjEAspLehkjr86brwuls1UfR1uC2uG53O2Rb3CCYkZSVC7GKdEw0SnbLNVfbhy500a8AiPbWC0AV75PidXNEe+1zye4n/xKdx0KA8aRXr7v/89x7KibHNpwf6rdpx7axYhlsWcFcyfoOTZJ3thhu7ib9QYJZ7gkRVGWbawZU6I370I5sMJLIwmzBXgZ2y8jFk7LUNbb2GUS3LKcw5gRYOqo+ZEHFdiLpPYXFHUslTkpaMNoPCNUR7Tsa2JPfJ29yEDMsdDs2lJbP+Km8hAnpkyJfpfPcVUcl8Ootd1cTe8iPdD1nqt3KvIy4sCjkCxvQFnmGOTWFqXY/UDD+Yqo3IVOG1aLPR3UxhzHtPX6M5FxtNB22kv8hrbN/QxhBEo/SfNhGIGZJNdI=
-    access_key_id:
-      secure: sjjc1tM756TvFbD8+0VCs/MOLFRhY+VZv/lq7pvNldtbGdv21lcJguvgg0gUwUZ7SihceyL1YagfUlDU2vWS1N5Si04z0VUcHO4lbj9nUJ4CXy27pMNFuk92HmblxYQ4bJw/xVqzsrT9Zeh4tIrbV+F3lYXtg7FDqjpm5bqrUJ5ULyWbhax31s2k8JRVnG2fxVmJiU+j4zwgzCqeZJXQkvXsfdzSe88mLyIrl7Otfjlp1rySocxStQ8qlmkvRAmsYmsKriUq+VgtX21HFAIJqHQp4WbL15eh0aFFzbmORf7OMIKmsmkyeYYeM6xp+Jm9m7ZcWHZ+7siplVM/9ksmumN3+CGp6pogduZ5zFH4BtRQa9o8N2SWnPiPvMrpoyLbHbQhg/uhcMJkYI1yrzj/s9MEH7DVnDEMmgc/UKsyTC2PRwZcgqYYPHxqRVWXzvBWDUe1wtxdNyaXWWKdD/1E7u8XF2+zPTPaUNAYAF1rSi5GNF38uRNMC+QSuEm8wZ/32KXFN4m+RW3NtpT9YHI8MCo4ofVxpRUMcEUwPDF/jV5zW7Krz7LEeL5/zcHMcNay/Ls8e3eApQShAcLAx1jZo+EjznVGXtlRbH9ABXmPO5AcxUH5+20tMiMoj0q6hJen03X8Jx+OMmcDNDufDgPikSym3KVC3Lvj65lG4G9pAyY=
+      condition: $BUILD_INSTALLER = yes && $TRAVIS_EVENT_TYPE == cron
+  - provider: bintray
+    file: build/bintray-latest.json
+    user: "${BINTRAY_USER}"
+    key: "${BINTRAY_KEY}"
+    skip_cleanup: true
+    on:
+      branch: master
+      condition: $BUILD_INSTALLER = yes && $TRAVIS_EVENT_TYPE == cron
diff --git a/docs/install.rst b/docs/install.rst
index f7385939..b864ae00 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -239,7 +239,7 @@ Windows binaries
 
 You can download the latest stable Windows installer from the `GitHub Releases Page <https://github.com/streamlink/streamlink/releases/latest>`__.
 
-Alternatively, you can download the latest `nightly Windows installer <https://streamlink-builds.s3.amazonaws.com/nightly/windows/streamlink-latest.exe>`__.
+Alternatively, you can download the latest `nightly Windows installer <https://dl.bintray.com/streamlink/streamlink-nightly/streamlink-latest.exe>`__.
 
 This is a installer which contains:
 
diff --git a/script/bintrayconfig.sh b/script/bintrayconfig.sh
new file mode 100755
index 00000000..f5c2f7ef
--- /dev/null
+++ b/script/bintrayconfig.sh
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+#
+# Script to generate bintray config for nightly builds
+#
+
+build_dir="$(pwd)/build"
+nsis_dir="${build_dir}/nsis"
+# get the dist directory from an environment variable, but default to the build/nsis directory
+dist_dir="${STREAMLINK_INSTALLER_DIST_DIR:-$nsis_dir}"
+
+# TODO: extract this common version detection code in to a separate function
+STREAMLINK_VERSION_PLAIN=$(python setup.py --version)
+# For travis nightly builds generate a version number with commit hash
+if [ -n "${TRAVIS_BRANCH}" ] && [ -z "${TRAVIS_TAG}" ]; then
+    STREAMLINK_VI_VERSION="${STREAMLINK_VERSION_PLAIN}.${TRAVIS_BUILD_NUMBER}"
+    STREAMLINK_INSTALLER="streamlink-${STREAMLINK_VERSION_PLAIN}-${TRAVIS_BUILD_NUMBER}-${TRAVIS_COMMIT:0:7}"
+    STREAMLINK_VERSION="${STREAMLINK_VERSION_PLAIN}+${TRAVIS_COMMIT:0:7}"
+else
+    STREAMLINK_VI_VERSION="${STREAMLINK_VERSION_PLAIN}.${TRAVIS_BUILD_NUMBER:-0}"
+    STREAMLINK_VERSION="${STREAMLINK_VERSION_PLAIN}"
+    STREAMLINK_INSTALLER="streamlink-${STREAMLINK_VERSION}"
+fi
+
+cat > "${build_dir}/bintray-latest.json" <<EOF
+{
+  "package": {
+    "subject": "streamlink",
+    "repo": "streamlink-nightly",
+    "name": "streamlink"
+  },
+
+  "version": {
+    "name": "latest",
+    "released": "$(date +'%Y-%m-%d')",
+    "desc": "Latest version of the installer (${STREAMLINK_VERSION})"
+  },
+
+  "files": [
+    {
+      "includePattern": "${dist_dir}/${STREAMLINK_INSTALLER}.exe",
+      "uploadPattern": "streamlink-latest.exe",
+      "matrixParams": {
+        "override": 1,
+        "publish": 1
+      }
+    }
+  ],
+
+  "publish": true
+}
+EOF
+
+echo "Wrote Bintray config to: ${build_dir}/bintray-latest.json"
+
+cat > "${build_dir}/bintray-nightly.json" <<EOF
+{
+  "package": {
+    "subject": "streamlink",
+    "repo": "streamlink-nightly",
+    "name": "streamlink"
+  },
+
+  "version": {
+    "name": "$(date +'%Y.%m.%d')",
+    "released": "$(date +'%Y-%m-%d')",
+    "desc": "Streamlink Nightly based on ${STREAMLINK_VERSION}"
+  },
+
+  "files": [
+    {
+      "includePattern": "${dist_dir}/${STREAMLINK_INSTALLER}.exe",
+      "uploadPattern": "streamlink-${STREAMLINK_VERSION_PLAIN}-$(date +'%Y%m%d').exe",
+      "matrixParams": {
+        "override": 1,
+        "publish": 1
+      }
+    }
+  ],
+
+  "publish": true
+}
+EOF
+
+echo "Wrote Bintray config to: ${build_dir}/bintray-nightly.json"
diff --git a/script/makeinstaller.sh b/script/makeinstaller.sh
index 935df9ee..259cce34 100755
--- a/script/makeinstaller.sh
+++ b/script/makeinstaller.sh
@@ -208,10 +208,5 @@ cp -r "win32/rtmpdump" "${nsis_dir}/"
 
 pynsist build/streamlink.cfg
 
-# Make a copy of this build for the "latest" nightly
-if [ -n "${TRAVIS_BRANCH}" ] && [ -z "${TRAVIS_TAG}" ]; then
-    cp "${dist_dir}/${STREAMLINK_INSTALLER}.exe" "${dist_dir}/streamlink-latest.exe"
-fi
-
 echo "Success!" 1>&2
 echo "The installer should be in ${dist_dir}." 1>&2
diff --git a/src/streamlink/plugins/pcyourfreetv.py b/src/streamlink/plugins/pcyourfreetv.py
index c116823b..968ac7d3 100644
--- a/src/streamlink/plugins/pcyourfreetv.py
+++ b/src/streamlink/plugins/pcyourfreetv.py
@@ -1,17 +1,19 @@
 import re
 
-from streamlink.compat import unquote
+from streamlink.compat import parse_qsl, unquote
 from streamlink.plugin import Plugin, PluginOptions
 from streamlink.plugin.api import http
 from streamlink.stream import HLSStream
 
 
 class PCYourFreeTV(Plugin):
+    LIVE_TV_URL = 'http://pc-yourfreetv.com/indexlivetv.php?page_id=1'
+
     _login_url = 'http://pc-yourfreetv.com/home.php'
-    _url_re = re.compile(r'http://pc-yourfreetv\.com/index_player\.php\?channel=.+?&page_id=\d+')
-    _iframe_re = re.compile(r"<iframe .*?\bsrc='(?P<iframe>.+?)'.*?</iframe>")
+    _url_re = re.compile(r'http://pc-yourfreetv\.com/indexplayer\.php\?channel=.+?&page_id=\d+')
+    _token_re = re.compile(r'\bsrc="indexplayer\.php\?channel=.+?&(?P<tokens>.+?)"')
     _player_re = re.compile(r"<script language=JavaScript>m='(?P<player>.+?)'", re.DOTALL)
-    _video_url_re = re.compile(r'new YourFreeTV.Player\({source: "(?P<video_url>[^"]+?)".+?}.*\);', re.DOTALL)
+    _video_url_re = re.compile(r"jwplayer\('.+?'\)\.setup\({.+?file: \"(?P<video_url>[^\"]+?)\".+?}\);", re.DOTALL)
 
     options = PluginOptions({
         'username': None,
@@ -46,13 +48,14 @@ class PCYourFreeTV(Plugin):
         if self.login(username, password):
             self.logger.info("Successfully logged in as {0}", username)
 
-        # Retrieve URL iframe
-        res = http.get(self.url)
-        match = self._iframe_re.search(res.text)
+        # Get a fresh authorization token
+        res = http.get(self.LIVE_TV_URL)
+        match = self._token_re.search(res.text)
         if match is None:
             return
 
-        res = http.get(match.group('iframe'))
+        # Retrieve URL page and search for stream data
+        res = http.get(self.url, params=parse_qsl(match.group('tokens')))
         match = self._player_re.search(res.text)
         if match is None:
             return


Test patch:
diff --git a/tests/test_plugin_pcyourfreetv.py b/tests/test_plugin_pcyourfreetv.py
index d1ee96dc..f5664754 100644
--- a/tests/test_plugin_pcyourfreetv.py
+++ b/tests/test_plugin_pcyourfreetv.py
@@ -6,13 +6,13 @@ from streamlink.plugins.pcyourfreetv import PCYourFreeTV
 class TestPluginPCYourFreeTV(unittest.TestCase):
     def test_can_handle_url(self):
         # should match
-        self.assertTrue(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/index_player.php?channel=das%20erste&page_id=41"))
-        self.assertTrue(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/index_player.php?channel=srf%20eins&page_id=41"))
-        self.assertTrue(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/index_player.php?channel=bbc%20one&page_id=41"))
-        self.assertTrue(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/index_player.php?channel=tf1&page_id=41"))
+        self.assertTrue(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/indexplayer.php?channel=das%20erste&page_id=41"))
+        self.assertTrue(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/indexplayer.php?channel=srf%20eins&page_id=41"))
+        self.assertTrue(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/indexplayer.php?channel=bbc%20one&page_id=41"))
+        self.assertTrue(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/indexplayer.php?channel=tf1&page_id=41"))
 
         # shouldn't match
         self.assertFalse(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/home.php"))
-        self.assertFalse(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/index_livetv.php?page_id=1"))
+        self.assertFalse(PCYourFreeTV.can_handle_url("http://pc-yourfreetv.com/indexlivetv.php?page_id=1"))
         self.assertFalse(PCYourFreeTV.can_handle_url("http://tvcatchup.com/"))
         self.assertFalse(PCYourFreeTV.can_handle_url("http://youtube.com/"))