Repository: streamlink/streamlink

Issue:
Plugin keep init if running too many sessions in the same time
### Checklist

- [X] This is a bug report and not a different kind of issue
- [X] [I have read the contribution guidelines](https://github.com/streamlink/streamlink/blob/master/CONTRIBUTING.md#contributing-to-streamlink)
- [X] [I have checked the list of open and recently closed bug reports](https://github.com/streamlink/streamlink/issues?q=is%3Aissue+label%3A%22bug%22)
- [X] [I have checked the commit log of the master branch](https://github.com/streamlink/streamlink/commits/master)

### Streamlink version

Latest stable release

### Description

Plugin:
```py
# encoding: utf-8
import re
from streamlink.plugin import Plugin, PluginArgument, PluginArguments, pluginmatcher

@pluginmatcher(re.compile(
    r'(http)?(s)?(://)?(www\.)?test.com/(?P<channel>[^/]+)'
))
class Test(Plugin):
    def __init__(self, url: str) -> None:
        self.url = url
        
        try:
            self.load_cookies()
        except RuntimeError:
            pass  # unbound cannot load

        self.lock = self.session.get_option("lock")
        with self.lock:
            print("__init__", self.url)
            
    def _get_streams(self):
        with self.lock:
            print("_get_streams", self.url)

__plugin__ = Test
```

reproduced code:
```
import streamlink
import time
import threading

lock = threading.Lock()

def GetStream(url):
    session = streamlink.Streamlink()
    session.set_option('lock', lock)
    session.options.set('lock', lock)
    while True:
        streams = session.streams(url)
        time.sleep(10)

count = 200
threading_pool = []
for i in range(count):
    thread = threading.Thread(target = GetStream, args = ('test.com/{}'.format(i) , ))
    threading_pool.append(thread)

for thread in threading_pool:
    thread.start()
    
for thread in threading_pool:
    thread.join()
```


I just found out it's because of "@lru_cache(maxsize=128)" in session.py
After I change it to 1024, everything is fine.
Anyway, there should be a warning, queue is full or what
I just upgrade Streamlink then realize some feature is broken, LOL

### Debug log

```text
```



Discussion:
Related: #4091
The url resolver should return plugin classes and not plugin instances. That would fix this issue, too.
I've removed the debug log, because it's causing massive lag with all the parsed links. Might be caused by the refined-github browser extension, but I don't care. The log's not important.

Resolving patch:
diff --git a/src/streamlink/session.py b/src/streamlink/session.py
--- a/src/streamlink/session.py
+++ b/src/streamlink/session.py
@@ -3,7 +3,7 @@
 from collections import OrderedDict
 from functools import lru_cache
 from socket import AF_INET, AF_INET6
-from typing import Dict, Optional, Type
+from typing import Dict, Optional, Tuple, Type
 
 import requests
 import requests.packages.urllib3.util.connection as urllib3_connection
@@ -341,10 +341,10 @@ def get_plugin_option(self, plugin, key):
             return plugin.get_option(key)
 
     @lru_cache(maxsize=128)
-    def resolve_url(self, url: str, follow_redirect: bool = True) -> Plugin:
+    def resolve_url(self, url: str, follow_redirect: bool = True) -> Tuple[Type[Plugin], str]:
         """Attempts to find a plugin that can use this URL.
 
-        The default protocol (http) will be prefixed to the URL if
+        The default protocol (https) will be prefixed to the URL if
         not specified.
 
         Raises :exc:`NoPluginError` on failure.
@@ -373,11 +373,12 @@ def resolve_url(self, url: str, follow_redirect: bool = True) -> Plugin:
                     priority = prio
 
         if candidate:
-            return candidate(url)
+            return candidate, url
 
         if follow_redirect:
             # Attempt to handle a redirect URL
             try:
+                # noinspection PyArgumentList
                 res = self.http.head(url, allow_redirects=True, acceptable_status=[501])
 
                 # Fall back to GET request if server doesn't handle HEAD.
@@ -391,10 +392,10 @@ def resolve_url(self, url: str, follow_redirect: bool = True) -> Plugin:
 
         raise NoPluginError
 
-    def resolve_url_no_redirect(self, url):
+    def resolve_url_no_redirect(self, url: str) -> Tuple[Type[Plugin], str]:
         """Attempts to find a plugin that can use this URL.
 
-        The default protocol (http) will be prefixed to the URL if
+        The default protocol (https) will be prefixed to the URL if
         not specified.
 
         Raises :exc:`NoPluginError` on failure.
@@ -404,7 +405,7 @@ def resolve_url_no_redirect(self, url):
         """
         return self.resolve_url(url, follow_redirect=False)
 
-    def streams(self, url, **params):
+    def streams(self, url: str, **params):
         """Attempts to find a plugin and extract streams from the *url*.
 
         *params* are passed to :func:`Plugin.streams`.
@@ -412,7 +413,9 @@ def streams(self, url, **params):
         Raises :exc:`NoPluginError` if no plugin is found.
         """
 
-        plugin = self.resolve_url(url)
+        pluginclass, resolved_url = self.resolve_url(url)
+        plugin = pluginclass(resolved_url)
+
         return plugin.streams(**params)
 
     def get_plugins(self):
diff --git a/src/streamlink_cli/main.py b/src/streamlink_cli/main.py
--- a/src/streamlink_cli/main.py
+++ b/src/streamlink_cli/main.py
@@ -13,7 +13,7 @@
 from itertools import chain
 from pathlib import Path
 from time import sleep
-from typing import List
+from typing import Dict, List, Type
 
 import requests
 from socks import __version__ as socks_version
@@ -24,7 +24,7 @@
 from streamlink.cache import Cache
 from streamlink.exceptions import FatalPluginError
 from streamlink.plugin import Plugin, PluginOptions
-from streamlink.stream.stream import StreamIO
+from streamlink.stream.stream import Stream, StreamIO
 from streamlink.stream.streamprocess import StreamProcess
 from streamlink.utils.named_pipe import NamedPipe
 from streamlink_cli.argparser import build_parser
@@ -44,10 +44,11 @@
 args = None
 console: ConsoleOutput = None
 output: Output = None
-plugin: Plugin = None
 stream_fd: StreamIO = None
 streamlink: Streamlink = None
 
+Streams = Dict[str, Stream]
+
 log = logging.getLogger("streamlink.cli")
 
 
@@ -174,7 +175,7 @@ def iter_http_requests(server, player):
             continue
 
 
-def output_stream_http(plugin, initial_streams, formatter: Formatter, external=False, port=0):
+def output_stream_http(plugin: Plugin, initial_streams: Streams, formatter: Formatter, external=False, port=0):
     """Continuously output the stream over HTTP."""
     global output
 
@@ -395,7 +396,7 @@ def read_stream(stream, output, prebuffer, formatter: Formatter, chunk_size=8192
         log.info("Stream ended")
 
 
-def handle_stream(plugin, streams, stream_name):
+def handle_stream(plugin: Plugin, streams: Streams, stream_name: str) -> None:
     """Decides what to do with the selected stream.
 
     Depending on arguments it can be one of these:
@@ -465,14 +466,14 @@ def handle_stream(plugin, streams, stream_name):
                 break
 
 
-def fetch_streams(plugin):
+def fetch_streams(plugin: Plugin) -> Streams:
     """Fetches streams using correct parameters."""
 
     return plugin.streams(stream_types=args.stream_types,
                           sorting_excludes=args.stream_sorting_excludes)
 
 
-def fetch_streams_with_retry(plugin, interval, count):
+def fetch_streams_with_retry(plugin: Plugin, interval: float, count: int) -> Streams:
     """Attempts to fetch streams repeatedly
        until some are returned or limit hit."""
 
@@ -504,7 +505,7 @@ def fetch_streams_with_retry(plugin, interval, count):
     return streams
 
 
-def resolve_stream_name(streams, stream_name):
+def resolve_stream_name(streams: Streams, stream_name: str) -> str:
     """Returns the real stream name of a synonym."""
 
     if stream_name in STREAM_SYNONYMS and stream_name in streams:
@@ -515,7 +516,7 @@ def resolve_stream_name(streams, stream_name):
     return stream_name
 
 
-def format_valid_streams(plugin, streams):
+def format_valid_streams(plugin: Plugin, streams: Streams) -> str:
     """Formats a dict of streams.
 
     Filters out synonyms and displays them next to
@@ -560,8 +561,9 @@ def handle_url():
     """
 
     try:
-        plugin = streamlink.resolve_url(args.url)
-        setup_plugin_options(streamlink, plugin)
+        pluginclass, resolved_url = streamlink.resolve_url(args.url)
+        setup_plugin_options(streamlink, pluginclass)
+        plugin = pluginclass(resolved_url)
         log.info(f"Found matching plugin {plugin.module} for URL {args.url}")
 
         if args.retry_max or args.retry_streams:
@@ -571,8 +573,7 @@ def handle_url():
                 retry_streams = args.retry_streams
             if args.retry_max:
                 retry_max = args.retry_max
-            streams = fetch_streams_with_retry(plugin, retry_streams,
-                                               retry_max)
+            streams = fetch_streams_with_retry(plugin, retry_streams, retry_max)
         else:
             streams = fetch_streams(plugin)
     except NoPluginError:
@@ -683,9 +684,9 @@ def setup_config_args(parser, ignore_unknown=False):
     if streamlink and args.url:
         # Only load first available plugin config
         with ignored(NoPluginError):
-            plugin = streamlink.resolve_url(args.url)
+            pluginclass, resolved_url = streamlink.resolve_url(args.url)
             for config_file in CONFIG_FILES:
-                config_file = config_file.with_name(f"{config_file.name}.{plugin.module}")
+                config_file = config_file.with_name(f"{config_file.name}.{pluginclass.module}")
                 if not config_file.is_file():
                     continue
                 if type(config_file) is DeprecatedPath:
@@ -881,7 +882,7 @@ def setup_plugin_args(session, parser):
         plugin.options = PluginOptions(defaults)
 
 
-def setup_plugin_options(session, plugin):
+def setup_plugin_options(session: Streamlink, plugin: Type[Plugin]):
     """Sets Streamlink plugin options."""
     pname = plugin.module
     required = OrderedDict({})


Test patch:
diff --git a/tests/plugins/test_stream.py b/tests/plugins/test_stream.py
--- a/tests/plugins/test_stream.py
+++ b/tests/plugins/test_stream.py
@@ -1,6 +1,8 @@
 import unittest
 from unittest.mock import patch
 
+import requests_mock
+
 from streamlink import Streamlink
 from streamlink.plugin.plugin import parse_params, stream_weight
 from streamlink.stream.akamaihd import AkamaiHDStream
@@ -13,12 +15,18 @@ class TestPluginStream(unittest.TestCase):
     def setUp(self):
         self.session = Streamlink()
 
+    def resolve_url(self, url):
+        with requests_mock.Mocker() as mock:
+            mock.register_uri(requests_mock.ANY, requests_mock.ANY, text="")
+            pluginclass, resolved_url = self.session.resolve_url(url)
+            return pluginclass(resolved_url)
+
     def assertDictHas(self, a, b):
         for key, value in a.items():
             self.assertEqual(b[key], value)
 
     def _test_akamaihd(self, surl, url):
-        plugin = self.session.resolve_url(surl)
+        plugin = self.resolve_url(surl)
         streams = plugin.streams()
 
         self.assertTrue("live" in streams)
@@ -31,7 +39,7 @@ def _test_akamaihd(self, surl, url):
     def _test_hls(self, surl, url, mock_parse):
         mock_parse.return_value = {}
 
-        plugin = self.session.resolve_url(surl)
+        plugin = self.resolve_url(surl)
         streams = plugin.streams()
 
         self.assertIn("live", streams)
@@ -45,7 +53,7 @@ def _test_hls(self, surl, url, mock_parse):
     def _test_hlsvariant(self, surl, url, mock_parse):
         mock_parse.return_value = {"best": HLSStream(self.session, url)}
 
-        plugin = self.session.resolve_url(surl)
+        plugin = self.resolve_url(surl)
         streams = plugin.streams()
 
         mock_parse.assert_called_with(self.session, url)
@@ -58,7 +66,7 @@ def _test_hlsvariant(self, surl, url, mock_parse):
         self.assertEqual(stream.url, url)
 
     def _test_rtmp(self, surl, url, params):
-        plugin = self.session.resolve_url(surl)
+        plugin = self.resolve_url(surl)
         streams = plugin.streams()
 
         self.assertIn("live", streams)
@@ -69,7 +77,7 @@ def _test_rtmp(self, surl, url, params):
         self.assertDictHas(params, stream.params)
 
     def _test_http(self, surl, url, params):
-        plugin = self.session.resolve_url(surl)
+        plugin = self.resolve_url(surl)
         streams = plugin.streams()
 
         self.assertIn("live", streams)
diff --git a/tests/test_cli_main.py b/tests/test_cli_main.py
--- a/tests/test_cli_main.py
+++ b/tests/test_cli_main.py
@@ -25,7 +25,19 @@
     setup_config_args
 )
 from streamlink_cli.output import FileOutput, PlayerOutput
-from tests.plugin.testplugin import TestPlugin as FakePlugin
+from tests.plugin.testplugin import TestPlugin as _TestPlugin
+
+
+class FakePlugin(_TestPlugin):
+    module = "fake"
+    arguments = []
+    _streams = {}
+
+    def streams(self, *args, **kwargs):
+        return self._streams
+
+    def _get_streams(self):  # pragma: no cover
+        pass
 
 
 class TestCLIMain(unittest.TestCase):
@@ -71,7 +83,7 @@ def test_format_valid_streams(self):
             "best": c
         }
         self.assertEqual(
-            format_valid_streams(FakePlugin, streams),
+            format_valid_streams(_TestPlugin, streams),
             ", ".join([
                 "audio",
                 "720p (worst)",
@@ -87,7 +99,7 @@ def test_format_valid_streams(self):
             "best-unfiltered": c
         }
         self.assertEqual(
-            format_valid_streams(FakePlugin, streams),
+            format_valid_streams(_TestPlugin, streams),
             ", ".join([
                 "audio",
                 "720p (worst-unfiltered)",
@@ -102,10 +114,9 @@ class TestCLIMainJsonAndStreamUrl(unittest.TestCase):
     def test_handle_stream_with_json_and_stream_url(self, console, args):
         stream = Mock()
         streams = dict(best=stream)
+
         plugin = FakePlugin("")
-        plugin.module = "fake"
-        plugin.arguments = []
-        plugin.streams = Mock(return_value=streams)
+        plugin._streams = streams
 
         handle_stream(plugin, streams, "best")
         self.assertEqual(console.msg.mock_calls, [])
@@ -138,12 +149,11 @@ def test_handle_stream_with_json_and_stream_url(self, console, args):
     def test_handle_url_with_json_and_stream_url(self, console, args):
         stream = Mock()
         streams = dict(worst=Mock(), best=stream)
-        plugin = FakePlugin("")
-        plugin.module = "fake"
-        plugin.arguments = []
-        plugin.streams = Mock(return_value=streams)
 
-        with patch("streamlink_cli.main.streamlink", resolve_url=Mock(return_value=plugin)):
+        class _FakePlugin(FakePlugin):
+            _streams = streams
+
+        with patch("streamlink_cli.main.streamlink", resolve_url=Mock(return_value=(_FakePlugin, ""))):
             handle_url()
             self.assertEqual(console.msg.mock_calls, [])
             self.assertEqual(console.msg_json.mock_calls, [call(
@@ -382,7 +392,7 @@ def test_handle_stream_output_stream(self, args: Mock, mock_output_stream: Mock)
         args.player_continuous_http = False
         mock_output_stream.return_value = True
 
-        plugin = FakePlugin("")
+        plugin = _TestPlugin("")
         plugin.author = "AUTHOR"
         plugin.category = "CATEGORY"
         plugin.title = "TITLE"
@@ -410,7 +420,7 @@ def subject(cls, config_files, **args):
         def resolve_url(name):
             if name == "noplugin":
                 raise NoPluginError()
-            return Mock(module="testplugin")
+            return Mock(module="testplugin"), name
 
         session = Mock()
         session.resolve_url.side_effect = resolve_url
diff --git a/tests/test_plugins_input.py b/tests/test_plugins_input.py
--- a/tests/test_plugins_input.py
+++ b/tests/test_plugins_input.py
@@ -52,6 +52,7 @@ def test_set_via_session(self):
             session = Streamlink({"user-input-requester": console_input})
             session.load_plugins(os.path.join(os.path.dirname(__file__), "plugin"))
 
-            p = session.resolve_url("http://test.se/channel")
+            pluginclass, resolved_url = session.resolve_url("http://test.se/channel")
+            p = pluginclass(resolved_url)
             self.assertEqual("username", p.input_ask("username"))
             self.assertEqual("password", p.input_ask_password("password"))
diff --git a/tests/test_session.py b/tests/test_session.py
--- a/tests/test_session.py
+++ b/tests/test_session.py
@@ -4,6 +4,7 @@
 from socket import AF_INET, AF_INET6
 from unittest.mock import Mock, call, patch
 
+import requests_mock
 from requests.packages.urllib3.util.connection import allowed_gai_family
 
 from streamlink import NoPluginError, Streamlink
@@ -20,8 +21,19 @@ def _get_streams(self):
 
 
 class TestSession(unittest.TestCase):
+    mocker: requests_mock.Mocker
+
     plugin_path = os.path.join(os.path.dirname(__file__), "plugin")
 
+    def setUp(self):
+        self.mocker = requests_mock.Mocker()
+        self.mocker.register_uri(requests_mock.ANY, requests_mock.ANY, text="")
+        self.mocker.start()
+
+    def tearDown(self):
+        self.mocker.stop()
+        Streamlink.resolve_url.cache_clear()
+
     def subject(self, load_plugins=True):
         session = Streamlink()
         if load_plugins:
@@ -29,9 +41,18 @@ def subject(self, load_plugins=True):
 
         return session
 
-    def test_exceptions(self):
-        session = self.subject()
-        self.assertRaises(NoPluginError, session.resolve_url, "invalid url", follow_redirect=False)
+    @staticmethod
+    def _resolve_url(method, *args, **kwargs) -> Plugin:
+        pluginclass, resolved_url = method(*args, **kwargs)
+        return pluginclass(resolved_url)
+
+    def resolve_url(self, session: Streamlink, url: str, *args, **kwargs) -> Plugin:
+        return self._resolve_url(session.resolve_url, url, *args, **kwargs)
+
+    def resolve_url_no_redirect(self, session: Streamlink, url: str, *args, **kwargs) -> Plugin:
+        return self._resolve_url(session.resolve_url_no_redirect, url, *args, **kwargs)
+
+    # ----
 
     def test_load_plugins(self):
         session = self.subject()
@@ -76,11 +97,45 @@ def test_load_plugins_syntaxerror(self, mock_log, mock_load_module):
     def test_resolve_url(self):
         session = self.subject()
         plugins = session.get_plugins()
-        plugin = session.resolve_url("http://test.se/channel")
-        self.assertTrue(isinstance(plugin, Plugin))
-        self.assertTrue(isinstance(plugin, plugins["testplugin"]))
+
+        pluginclass, resolved_url = session.resolve_url("http://test.se/channel")
+        self.assertTrue(issubclass(pluginclass, Plugin))
+        self.assertIs(pluginclass, plugins["testplugin"])
+        self.assertEqual(resolved_url, "http://test.se/channel")
         self.assertTrue(hasattr(session.resolve_url, "cache_info"), "resolve_url has a lookup cache")
 
+    def test_resolve_url__noplugin(self):
+        session = self.subject()
+        self.mocker.get("http://invalid2", status_code=301, headers={"Location": "http://invalid3"})
+
+        self.assertRaises(NoPluginError, session.resolve_url, "http://invalid1")
+        self.assertRaises(NoPluginError, session.resolve_url, "http://invalid2")
+
+    def test_resolve_url__redirected(self):
+        session = self.subject()
+        plugins = session.get_plugins()
+        self.mocker.head("http://redirect1", status_code=501)
+        self.mocker.get("http://redirect1", status_code=301, headers={"Location": "http://redirect2"})
+        self.mocker.head("http://redirect2", status_code=301, headers={"Location": "http://test.se/channel"})
+
+        pluginclass, resolved_url = session.resolve_url("http://redirect1")
+        self.assertTrue(issubclass(pluginclass, Plugin))
+        self.assertIs(pluginclass, plugins["testplugin"])
+        self.assertEqual(resolved_url, "http://test.se/channel")
+
+    def test_resolve_url_no_redirect(self):
+        session = self.subject()
+        plugins = session.get_plugins()
+
+        pluginclass, resolved_url = session.resolve_url_no_redirect("http://test.se/channel")
+        self.assertTrue(issubclass(pluginclass, Plugin))
+        self.assertIs(pluginclass, plugins["testplugin"])
+        self.assertEqual(resolved_url, "http://test.se/channel")
+
+    def test_resolve_url_no_redirect__noplugin(self):
+        session = self.subject()
+        self.assertRaises(NoPluginError, session.resolve_url_no_redirect, "http://invalid")
+
     def test_resolve_url_scheme(self):
         @pluginmatcher(re.compile("http://insecure"))
         class PluginHttp(EmptyPlugin):
@@ -96,13 +151,13 @@ class PluginHttps(EmptyPlugin):
             "secure": PluginHttps,
         }
 
-        self.assertRaises(NoPluginError, session.resolve_url, "insecure")
-        self.assertIsInstance(session.resolve_url("http://insecure"), PluginHttp)
-        self.assertRaises(NoPluginError, session.resolve_url, "https://insecure")
+        self.assertRaises(NoPluginError, self.resolve_url, session, "insecure")
+        self.assertIsInstance(self.resolve_url(session, "http://insecure"), PluginHttp)
+        self.assertRaises(NoPluginError, self.resolve_url, session, "https://insecure")
 
-        self.assertIsInstance(session.resolve_url("secure"), PluginHttps)
-        self.assertRaises(NoPluginError, session.resolve_url, "http://secure")
-        self.assertIsInstance(session.resolve_url("https://secure"), PluginHttps)
+        self.assertIsInstance(self.resolve_url(session, "secure"), PluginHttps)
+        self.assertRaises(NoPluginError, self.resolve_url, session, "http://secure")
+        self.assertIsInstance(self.resolve_url(session, "https://secure"), PluginHttps)
 
     def test_resolve_url_priority(self):
         @pluginmatcher(priority=HIGH_PRIORITY, pattern=re.compile(
@@ -136,10 +191,10 @@ class NoPriority(EmptyPlugin):
             "low": LowPriority,
             "no": NoPriority,
         }
-        no = session.resolve_url_no_redirect("no")
-        low = session.resolve_url_no_redirect("low")
-        normal = session.resolve_url_no_redirect("normal")
-        high = session.resolve_url_no_redirect("high")
+        no = self.resolve_url_no_redirect(session, "no")
+        low = self.resolve_url_no_redirect(session, "low")
+        normal = self.resolve_url_no_redirect(session, "normal")
+        high = self.resolve_url_no_redirect(session, "high")
 
         self.assertIsInstance(no, HighPriority)
         self.assertIsInstance(low, HighPriority)
@@ -151,7 +206,7 @@ class NoPriority(EmptyPlugin):
             "no": NoPriority,
         }
         with self.assertRaises(NoPluginError):
-            session.resolve_url_no_redirect("no")
+            self.resolve_url_no_redirect(session, "no")
 
     @patch("streamlink.session.log")
     def test_resolve_deprecated(self, mock_log: Mock):
@@ -182,19 +237,12 @@ def priority(cls, url):
             "dep-high": DeprecatedHighPriority,
         }
 
-        self.assertIsInstance(session.resolve_url_no_redirect("low"), DeprecatedHighPriority)
+        self.assertIsInstance(self.resolve_url_no_redirect(session, "low"), DeprecatedHighPriority)
         self.assertEqual(mock_log.info.mock_calls, [
             call("Resolved plugin dep-normal-one with deprecated can_handle_url API"),
             call("Resolved plugin dep-high with deprecated can_handle_url API")
         ])
 
-    def test_resolve_url_no_redirect(self):
-        session = self.subject()
-        plugin = session.resolve_url_no_redirect("http://test.se/channel")
-        plugins = session.get_plugins()
-        self.assertTrue(isinstance(plugin, Plugin))
-        self.assertTrue(isinstance(plugin, plugins["testplugin"]))
-
     def test_options(self):
         session = self.subject()
         session.set_option("test_option", "option")
@@ -209,7 +257,7 @@ def test_options(self):
 
     def test_plugin(self):
         session = self.subject()
-        plugin = session.resolve_url("http://test.se/channel")
+        plugin = self.resolve_url(session, "http://test.se/channel")
         streams = plugin.streams()
 
         self.assertTrue("best" in streams)
@@ -223,7 +271,7 @@ def test_plugin(self):
 
     def test_plugin_stream_types(self):
         session = self.subject()
-        plugin = session.resolve_url("http://test.se/channel")
+        plugin = self.resolve_url(session, "http://test.se/channel")
         streams = plugin.streams(stream_types=["http", "rtmp"])
 
         self.assertTrue(isinstance(streams["480p"], HTTPStream))
@@ -236,7 +284,7 @@ def test_plugin_stream_types(self):
 
     def test_plugin_stream_sorting_excludes(self):
         session = self.subject()
-        plugin = session.resolve_url("http://test.se/channel")
+        plugin = self.resolve_url(session, "http://test.se/channel")
 
         streams = plugin.streams(sorting_excludes=[])
         self.assertTrue("best" in streams)
@@ -268,7 +316,7 @@ def test_plugin_stream_sorting_excludes(self):
         self.assertTrue(streams["worst-unfiltered"] is streams["350k"])
         self.assertTrue(streams["best-unfiltered"] is streams["1080p"])
 
-        plugin = session.resolve_url("http://test.se/UnsortableStreamNames")
+        plugin = self.resolve_url(session, "http://test.se/UnsortableStreamNames")
         streams = plugin.streams()
         self.assertFalse("best" in streams)
         self.assertFalse("worst" in streams)