Repository: streamlink/streamlink

Issue:
LCI not covered by TF1 home page anymore
### Checklist

- [ ] This is a bug report.
- [ ] This is a feature request.
- [x] This is a plugin (improvement) request.
- [ ] I have read the contribution guidelines.

### Description

I pointed out since a while that tf1 plugin doesn't cover anymore LCI from tf1.fr home website. It did before. 

### Expected / Actual behavior

Here I point out result with older tf1 plugin : 

```
streamlink "https://www.tf1.fr/lci/direct"
[cli][info] Found matching plugin tf1 for URL https://www.tf1.fr/lci/direct
error: Unable to open URL: http://lcilivhlshdslive-lh.akamaihd.net/z/lci_1@30158
5/manifest.f4m?hdnea=st=1526479986~exp=1526481786~acl=/*~hmac=207f41547435bb3422
e9f51af166cae855bdbb387ac875524827deb528999d9e (403 Client Error: Forbidden for
url: http://lcilivhlshdslive-lh.akamaihd.net/z/lci_1@301585/manifest.f4m?hdnea=s
t=1526479986~exp=1526481786~acl=/*~hmac=207f41547435bb3422e9f51af166cae855bdbb38
7ac875524827deb528999d9e&g=DSCLJVQYJHGR&hdcore=3.1.0)
```

The latest tf1 plugin gives such result : 

```
streamlink "https://www.tf1.fr/lci/direct"
[cli][info] Found matching plugin resolve for URL https://www.tf1.fr/lci/direct
[plugin.resolve][info] Found iframes:
Traceback (most recent call last):
  File "C:\Program Files\Python27\Scripts\streamlink-script.py", line 11, in <mo
dule>
    load_entry_point('streamlink==0.12.1+8.ge2a5546', 'console_scripts', 'stream
link')()
  File "c:\program files\python27\lib\site-packages\streamlink_cli\main.py", lin
e 1113, in main
    handle_url()
  File "c:\program files\python27\lib\site-packages\streamlink_cli\main.py", lin
e 505, in handle_url
    streams = fetch_streams(plugin)
  File "c:\program files\python27\lib\site-packages\streamlink_cli\main.py", lin
e 402, in fetch_streams
    sorting_excludes=args.stream_sorting_excludes)
  File "c:\program files\python27\lib\site-packages\streamlink\plugin\plugin.py"
, line 385, in get_streams
    return self.streams(*args, **kwargs)
  File "c:\program files\python27\lib\site-packages\streamlink\plugin\plugin.py"
, line 288, in streams
    ostreams = self._get_streams()
  File "c:\program files\python27\lib\site-packages\streamlink\plugins\resolve.p
y", line 480, in _get_streams
IndexError: list index out of range
```

### Reproduction steps / Explicit stream URLs to test

1. ` streamlink "https://www.tf1.fr/lci/direct"`

### Logs

```
streamlink -l debug
[cli][debug] OS:         Windows 7
[cli][debug] Python:     2.7.13
[cli][debug] Streamlink: 0.12.1+8.ge2a5546
[cli][debug] Requests(2.18.4), Socks(1.6.7), Websocket(0.46.0)
```

Both tests were made with the latest streamlink build, I just replaced newer tf1 plugin with older. 
Thanks for up. 



Discussion:
The url is hard coded in

https://github.com/streamlink/streamlink/blob/b340b1e3dbb5b8dca59fcd01c224fc31f80f7402/src/streamlink/plugins/tf1.py#L12

it can't find the `lci` part

Ref https://github.com/streamlink/streamlink/pull/1457

---

and you should use the path `%APPDATA%\streamlink\plugins` for any custom plugins.

https://streamlink.github.io/cli.html#sideloading-plugins

Resolving patch:
diff --git a/src/streamlink/plugins/tf1.py b/src/streamlink/plugins/tf1.py
--- a/src/streamlink/plugins/tf1.py
+++ b/src/streamlink/plugins/tf1.py
@@ -9,13 +9,20 @@
 
 
 class TF1(Plugin):
-    url_re = re.compile(r"https?://(?:www\.)?(?:tf1\.fr/(tf1|tmc|tfx|tf1-series-films)/direct|(lci).fr/direct)/?")
+    url_re = re.compile(r"https?://(?:www\.)?(?:tf1\.fr/([\w-]+)/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", "tfx" : "nt1live", "tf1-series-films" : "hd1live" }
-    hls_channel_remap = {"lci": "LCI", "tf1": "V4", "tfx" : "nt1", "tf1-series-films" : "hd1" }
+    hds_channel_remap = {"tf1": "androidliveconnect",
+                         "lci": "androidlivelci",
+                         "tfx": "nt1live",
+                         "hd1": "hd1live",  # renamed to tfx
+                         "tf1-series-films": "hd1live"}
+    hls_channel_remap = {"lci": "LCI",
+                         "tf1": "V4",
+                         "tfx": "nt1",
+                         "tf1-series-films": "hd1"}
 
     @classmethod
     def can_handle_url(cls, url):
@@ -23,6 +30,7 @@ def can_handle_url(cls, url):
 
     def _get_hds_streams(self, channel):
         channel = self.hds_channel_remap.get(channel, "{0}live".format(channel))
+        self.logger.debug("Using HDS channel name: {0}".format(channel))
         manifest_url = http.get(self.api_url.format(channel),
                                 params={"getURL": 1},
                                 headers={"User-Agent": useragents.FIREFOX}).text


Test patch:
diff --git a/tests/test_plugin_tf1.py b/tests/test_plugin_tf1.py
--- a/tests/test_plugin_tf1.py
+++ b/tests/test_plugin_tf1.py
@@ -12,11 +12,11 @@ def test_can_handle_url(self):
         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"))
+        self.assertTrue(TF1.can_handle_url("http://tf1.fr/lci/direct"))
 
+    def test_can_handle_url_negative(self):
         # 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/"))