コード例 #1
0
class TestPluginAkamaiHDPlugin(unittest.TestCase):
    def setUp(self):
        self.session = Livecli()

    def test_can_handle_url(self):
        should_match = [
            "akamaihd://https://example.com/index.mp3",
            "akamaihd://https://example.com/index.mp4",
        ]
        for url in should_match:
            self.assertTrue(AkamaiHDPlugin.can_handle_url(url))

        should_not_match = [
            "https://example.com/index.html",
        ]
        for url in should_not_match:
            self.assertFalse(AkamaiHDPlugin.can_handle_url(url))

    def _test_akamaihd(self, surl, url):
        plugin = self.session.resolve_url(surl)
        streams = plugin.streams()

        self.assertTrue("live" in streams)

        stream = streams["live"]
        self.assertTrue(isinstance(stream, AkamaiHDStream))
        self.assertEqual(stream.url, url)

    def test_plugin_akamaihd(self):
        self._test_akamaihd("akamaihd://http://hostname.se/stream",
                            "http://hostname.se/stream")

        self._test_akamaihd("akamaihd://hostname.se/stream",
                            "http://hostname.se/stream")
コード例 #2
0
class TestPluginRTMPPlugin(unittest.TestCase):
    def setUp(self):
        self.session = Livecli()

    def assertDictHas(self, a, b):
        for key, value in a.items():
            self.assertEqual(b[key], value)

    def test_can_handle_url(self):
        should_match = [
            "rtmp://https://example.com/",
            "rtmpe://https://example.com/",
            "rtmps://https://example.com/",
            "rtmpt://https://example.com/",
            "rtmpte://https://example.com/",
        ]
        for url in should_match:
            self.assertTrue(RTMPPlugin.can_handle_url(url))

        should_not_match = [
            "https://example.com/index.html",
        ]
        for url in should_not_match:
            self.assertFalse(RTMPPlugin.can_handle_url(url))

    def _test_rtmp(self, surl, url, params):
        plugin = self.session.resolve_url(surl)
        streams = plugin.streams()

        self.assertTrue("live" in streams)

        stream = streams["live"]
        self.assertTrue(isinstance(stream, RTMPStream))
        self.assertEqual(stream.params["rtmp"], url)
        self.assertDictHas(params, stream.params)

    def test_plugin_rtmp(self):
        self._test_rtmp("rtmp://hostname.se/stream",
                        "rtmp://hostname.se/stream", dict())

        self._test_rtmp("rtmp://hostname.se/stream live=1 qarg='a \\'string' noq=test",
                        "rtmp://hostname.se/stream", dict(live=True, qarg='a \'string', noq="test"))

        self._test_rtmp("rtmp://hostname.se/stream live=1 num=47",
                        "rtmp://hostname.se/stream", dict(live=True, num=47))

        self._test_rtmp("rtmp://hostname.se/stream conn=['B:1','S:authMe','O:1','NN:code:1.23','NS:flag:ok','O:0']",
                        "rtmp://hostname.se/stream",
                        dict(conn=['B:1', 'S:authMe', 'O:1', 'NN:code:1.23', 'NS:flag:ok', 'O:0']))
コード例 #3
0
class TestPluginHTTPStreamPlugin(unittest.TestCase):
    def setUp(self):
        self.session = Livecli()

    def assertDictHas(self, a, b):
        for key, value in a.items():
            self.assertEqual(b[key], value)

    def test_can_handle_url(self):
        should_match = [
            "httpstream://https://example.com/index.mp3",
            "httpstream://https://example.com/index.mp4",
        ]
        for url in should_match:
            self.assertTrue(HTTPStreamPlugin.can_handle_url(url))

        should_not_match = [
            "https://example.com/index.html",
        ]
        for url in should_not_match:
            self.assertFalse(HTTPStreamPlugin.can_handle_url(url))

    def _test_http(self, surl, url, params):
        plugin = self.session.resolve_url(surl)
        streams = plugin.streams()

        self.assertTrue("live" in streams)

        stream = streams["live"]
        self.assertTrue(isinstance(stream, HTTPStream))
        self.assertEqual(stream.url, url)
        self.assertDictHas(params, stream.args)

    def test_plugin_http(self):
        self._test_http(
            "httpstream://http://hostname.se/auth.php auth=('test','test2')",
            "http://hostname.se/auth.php", dict(auth=("test", "test2")))

        self._test_http(
            "httpstream://hostname.se/auth.php auth=('test','test2')",
            "http://hostname.se/auth.php", dict(auth=("test", "test2")))

        self._test_http(
            "httpstream://https://hostname.se/auth.php verify=False params={'key': 'a value'}",
            "https://hostname.se/auth.php?key=a+value",
            dict(verify=False, params=dict(key='a value')))
コード例 #4
0
class TestSession(unittest.TestCase):
    PluginPath = os.path.join(os.path.dirname(__file__), "plugins")

    def setUp(self):
        self.session = Livecli()
        self.session.load_plugins(self.PluginPath)

    def test_exceptions(self):
        try:
            # Turn off the resolve.py plugin
            self.session.set_plugin_option("resolve", "turn_off", True)
            self.session.resolve_url("invalid url")
            self.assertTrue(False)
        except NoPluginError:
            self.assertTrue(True)

    def test_load_plugins(self):
        plugins = self.session.get_plugins()
        self.assertTrue(plugins["testplugin"])

    def test_builtin_plugins(self):
        plugins = self.session.get_plugins()
        self.assertTrue("twitch" in plugins)

    def test_resolve_url(self):
        plugins = self.session.get_plugins()
        plugin = self.session.resolve_url("http://test.se/channel")
        self.assertTrue(isinstance(plugin, Plugin))
        self.assertTrue(isinstance(plugin, plugins["testplugin"]))

    def test_resolve_url_priority(self):
        from tests.plugins.testplugin import TestPlugin

        class HighPriority(TestPlugin):
            @classmethod
            def priority(cls, url):
                return HIGH_PRIORITY

        class LowPriority(TestPlugin):
            @classmethod
            def priority(cls, url):
                return LOW_PRIORITY

        self.session.plugins = {
            "test_plugin": TestPlugin,
            "test_plugin_low": LowPriority,
            "test_plugin_high": HighPriority,
        }
        plugin = self.session.resolve_url_no_redirect("http://test.se/channel")
        plugins = self.session.get_plugins()

        self.assertTrue(isinstance(plugin, plugins["test_plugin_high"]))
        self.assertEqual(HIGH_PRIORITY, plugin.priority(plugin.url))

    def test_resolve_url_no_redirect(self):
        plugins = self.session.get_plugins()
        plugin = self.session.resolve_url_no_redirect("http://test.se/channel")
        self.assertTrue(isinstance(plugin, Plugin))
        self.assertTrue(isinstance(plugin, plugins["testplugin"]))

    def test_options(self):
        self.session.set_option("test_option", "option")
        self.assertEqual(self.session.get_option("test_option"), "option")
        self.assertEqual(self.session.get_option("non_existing"), None)

        self.assertEqual(self.session.get_plugin_option("testplugin", "a_option"), "default")
        self.session.set_plugin_option("testplugin", "another_option", "test")
        self.assertEqual(self.session.get_plugin_option("testplugin", "another_option"), "test")
        self.assertEqual(self.session.get_plugin_option("non_existing", "non_existing"), None)
        self.assertEqual(self.session.get_plugin_option("testplugin", "non_existing"), None)

    def test_plugin(self):
        plugin = self.session.resolve_url("http://test.se/channel")
        streams = plugin.streams()

        self.assertTrue("best" in streams)
        self.assertTrue("worst" in streams)
        self.assertTrue(streams["best"] is streams["1080p"])
        self.assertTrue(streams["worst"] is streams["350k"])
        self.assertTrue(isinstance(streams["rtmp"], RTMPStream))
        self.assertTrue(isinstance(streams["http"], HTTPStream))
        self.assertTrue(isinstance(streams["hls"], HLSStream))
        self.assertTrue(isinstance(streams["akamaihd"], AkamaiHDStream))

    def test_plugin_stream_types(self):
        plugin = self.session.resolve_url("http://test.se/channel")
        streams = plugin.streams(stream_types=["http", "rtmp"])

        self.assertTrue(isinstance(streams["480p"], HTTPStream))
        self.assertTrue(isinstance(streams["480p_rtmp"], RTMPStream))

        streams = plugin.streams(stream_types=["rtmp", "http"])

        self.assertTrue(isinstance(streams["480p"], RTMPStream))
        self.assertTrue(isinstance(streams["480p_http"], HTTPStream))

    def test_plugin_stream_sorted_excludes(self):
        plugin = self.session.resolve_url("http://test.se/channel")
        streams = plugin.streams(sorting_excludes=["1080p", "3000k"])

        self.assertTrue("best" in streams)
        self.assertTrue("worst" in streams)
        self.assertTrue(streams["best"] is streams["1500k"])

        streams = plugin.streams(sorting_excludes=[">=1080p", ">1500k"])
        self.assertTrue(streams["best"] is streams["1500k"])

        streams = plugin.streams(sorting_excludes=lambda q: not q.endswith("p"))
        self.assertTrue(streams["best"] is streams["3000k"])

    def test_plugin_support(self):
        plugin = self.session.resolve_url("http://test.se/channel")
        streams = plugin.streams()

        self.assertTrue("support" in streams)
        self.assertTrue(isinstance(streams["support"], HTTPStream))

    def test_version(self):
        # PEP440 - https://www.python.org/dev/peps/pep-0440/
        VERSION_PATTERN = r"""
            v?
            (?:
                (?:(?P<epoch>[0-9]+)!)?                           # epoch
                (?P<release>[0-9]+(?:\.[0-9]+)*)                  # release segment
                (?P<pre>                                          # pre-release
                    [-_\.]?
                    (?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview))
                    [-_\.]?
                    (?P<pre_n>[0-9]+)?
                )?
                (?P<post>                                         # post release
                    (?:-(?P<post_n1>[0-9]+))
                    |
                    (?:
                        [-_\.]?
                        (?P<post_l>post|rev|r)
                        [-_\.]?
                        (?P<post_n2>[0-9]+)?
                    )
                )?
                (?P<dev>                                          # dev release
                    [-_\.]?
                    (?P<dev_l>dev)
                    [-_\.]?
                    (?P<dev_n>[0-9]+)?
                )?
            )
            (?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
        """

        _version_re = re.compile(
            r"^\s*" + VERSION_PATTERN + r"\s*$",
            re.VERBOSE | re.IGNORECASE,
        )

        self.assertRegexpMatches(self.session.version, _version_re)
コード例 #5
0
ファイル: multi_server.py プロジェクト: longsack/livecli
def _play_stream(HTTPBase, redirect=False):
    """Creates a livecli session and plays the stream."""
    session = Livecli()
    session.set_logprefix("[ID-{0}]".format(str(int(time()))[4:]))
    logger = session.logger.new_module("livecli-server")
    session.set_loglevel("info")

    logger.info("User-Agent: {0}".format(HTTPBase.headers.get("User-Agent", "???")))
    logger.info("Client: {0}".format(HTTPBase.client_address))
    logger.info("Address: {0}".format(HTTPBase.address_string()))

    # Load custom user plugins
    if os.path.isdir(PLUGINS_DIR):
        session.load_plugins(PLUGINS_DIR)

    old_data = parse_qsl(urlparse(HTTPBase.path).query)
    data = []
    for k, v in old_data:
        data += [(unquote_plus(k), unquote_plus(v))]

    data_other, session = command_session(session, data)

    url = data_other.get("url")
    if not url:
        HTTPBase._headers(404, "text/html")
        logger.error("No URL provided.")
        return

    plugin = session.resolve_url(url)
    logger.info("Found matching plugin {0} for URL {1}",
                plugin.module, url)

    # set cache size
    try:
        cache = data_other.get("cache") or 4096
    except TypeError:
        cache = 4096

    # set loglevel
    loglevel = data_other.get("l") or data_other.get("loglevel") or "debug"
    session.set_loglevel(loglevel)

    # find streams
    try:
        if redirect is True:
            streams = session.streams(url, stream_types=["hls", "http"])
        elif data_other.get("stream-types"):
            streams = session.streams(url, stream_types=data_other.get("stream-types"))
        else:
            streams = session.streams(url)
    except Exception as e:
        HTTPBase._headers(404, "text/html")
        logger.error("No Stream Found!")
        return

    if not streams:
        HTTPBase._headers(404, "text/html")
        return

    # set quality
    quality = (data_other.get("q")
               or data_other.get("quality")
               or data_other.get("stream")
               or data_other.get("default-stream")
               or ["best"])

    stream_name = "best"

    validstreams = format_valid_streams(plugin, streams)
    for stream_name in quality:
        if stream_name in streams:
            logger.info("Available streams: {0}", validstreams)
            stream_name = resolve_stream_name(streams, stream_name)
            break

    try:
        stream = streams[stream_name]
        logger.debug("Valid quality: {0}".format(stream_name))
    except KeyError:
        logger.debug("Invald quality: '{0}', using 'best' instead".format(stream_name))
        stream = streams["best"]

    if not isinstance(stream, (HDSStream, HTTPStream, MuxedStream)):
        # allow only http based streams: HDS HLS HTTP
        # RTMP is not supported
        logger.debug("only HTTP, HLS, HDS or MuxedStreams are supported.")
        logger.debug(str(type(stream)))
        HTTPBase._headers(404, "text/html")
        return

    if redirect is True:
        logger.info("301 - URL: {0}".format(stream.url))
        HTTPBase.send_response(301)
        HTTPBase.send_header("Location", stream.url)
        HTTPBase.end_headers()
        logger.info("301 - done")
        return

    hls_session_reload = data_other.get("hls-session-reload")
    if hls_session_reload:
        livecli_cache = Cache(
            filename="streamdata.json",
            key_prefix="cache:{0}".format(stream.url)
        )
        livecli_cache.set("cache_stream_name", stream_name, (int(hls_session_reload) + 60))
        livecli_cache.set("cache_url", url, (int(hls_session_reload) + 60))
        session.set_option("hls-session-reload", int(hls_session_reload))

    try:
        fd = stream.open()
    except StreamError as err:
        HTTPBase._headers(404, "text/html")
        logger.error("Could not open stream: {0}".format(err))
        return

    HTTPBase._headers(200, "video/unknown")
    try:
        logger.debug("Pre-buffering {0} bytes".format(cache))
        while True:
            buff = fd.read(cache)
            if not buff:
                logger.error("No Data!")
                break
            HTTPBase.wfile.write(buff)
        HTTPBase.wfile.close()
    except socket.error as e:
        if isinstance(e.args, tuple):
            if e.errno == errno.EPIPE:
                # remote peer disconnected
                logger.info("Detected remote disconnect")
                pass
            else:
                logger.error(str(e))
        else:
            logger.error(str(e))

    fd.close()
    logger.info("Stream ended")
    fd = None
コード例 #6
0
ファイル: test_plugin_hls.py プロジェクト: longsack/livecli
class TestPluginHLSPlugin(unittest.TestCase):
    def setUp(self):
        self.session = Livecli()

    def test_can_handle_url(self):
        should_match = [
            "https://example.com/index.m3u8",
            "https://example.com/index.m3u8?test=true",
            "hls://*****:*****@patch('livecli.stream.HLSStream.parse_variant_playlist')
    def _test_hls(self, surl, url, mock_parse):
        mock_parse.return_value = {}

        plugin = self.session.resolve_url(surl)
        streams = plugin.streams()

        self.assertTrue("live" in streams)
        mock_parse.assert_called_with(self.session, url)

        stream = streams["live"]
        self.assertTrue(isinstance(stream, HLSStream))
        self.assertEqual(stream.url, url)

    @patch('livecli.stream.HLSStream.parse_variant_playlist')
    def _test_hlsvariant(self, surl, url, mock_parse):
        mock_parse.return_value = {"best": HLSStream(self.session, url)}

        plugin = self.session.resolve_url(surl)
        streams = plugin.streams()

        mock_parse.assert_called_with(self.session, url)

        self.assertFalse("live" in streams)
        self.assertTrue("best" in streams)

        stream = streams["best"]
        self.assertTrue(isinstance(stream, HLSStream))
        self.assertEqual(stream.url, url)

    def test_plugin_hls(self):
        self._test_hls("hls://https://hostname.se/playlist.m3u8",
                       "https://hostname.se/playlist.m3u8")

        self._test_hls("hls://hostname.se/playlist.m3u8",
                       "http://hostname.se/playlist.m3u8")

        self._test_hlsvariant("hls://hostname.se/playlist.m3u8",
                              "http://hostname.se/playlist.m3u8")

        self._test_hlsvariant("hls://https://hostname.se/playlist.m3u8",
                              "https://hostname.se/playlist.m3u8")
コード例 #7
0
class TestSession(unittest.TestCase):
    PluginPath = os.path.join(os.path.dirname(__file__), "plugins")

    def setUp(self):
        self.session = Livecli()
        self.session.load_plugins(self.PluginPath)

    # def test_exceptions(self):
    #     try:
    #         self.session.resolve_url("invalid url")
    #         self.assertTrue(False)
    #     except NoPluginError:
    #         self.assertTrue(True)

    def test_load_plugins(self):
        plugins = self.session.get_plugins()
        self.assertTrue(plugins["testplugin"])

    def test_builtin_plugins(self):
        plugins = self.session.get_plugins()
        self.assertTrue("twitch" in plugins)

    def test_resolve_url(self):
        plugins = self.session.get_plugins()
        channel = self.session.resolve_url("http://test.se/channel")
        self.assertTrue(isinstance(channel, Plugin))
        self.assertTrue(isinstance(channel, plugins["testplugin"]))

    def test_resolve_url_priority(self):
        from tests.plugins.testplugin import TestPlugin

        class HighPriority(TestPlugin):
            @classmethod
            def priority(cls, url):
                return HIGH_PRIORITY

        class LowPriority(TestPlugin):
            @classmethod
            def priority(cls, url):
                return LOW_PRIORITY

        self.session.plugins = {
            "test_plugin": TestPlugin,
            "test_plugin_low": LowPriority,
            "test_plugin_high": HighPriority,
        }
        channel = self.session.resolve_url_no_redirect("http://test.se/channel")
        plugins = self.session.get_plugins()

        self.assertTrue(isinstance(channel, plugins["test_plugin_high"]))
        self.assertEqual(HIGH_PRIORITY, channel.priority(channel.url))

    def test_resolve_url_no_redirect(self):
        plugins = self.session.get_plugins()
        channel = self.session.resolve_url_no_redirect("http://test.se/channel")
        self.assertTrue(isinstance(channel, Plugin))
        self.assertTrue(isinstance(channel, plugins["testplugin"]))

    def test_options(self):
        self.session.set_option("test_option", "option")
        self.assertEqual(self.session.get_option("test_option"), "option")
        self.assertEqual(self.session.get_option("non_existing"), None)

        self.assertEqual(self.session.get_plugin_option("testplugin", "a_option"), "default")
        self.session.set_plugin_option("testplugin", "another_option", "test")
        self.assertEqual(self.session.get_plugin_option("testplugin", "another_option"), "test")
        self.assertEqual(self.session.get_plugin_option("non_existing", "non_existing"), None)
        self.assertEqual(self.session.get_plugin_option("testplugin", "non_existing"), None)

    def test_plugin(self):
        channel = self.session.resolve_url("http://test.se/channel")
        streams = channel.get_streams()

        self.assertTrue("best" in streams)
        self.assertTrue("worst" in streams)
        self.assertTrue(streams["best"] is streams["1080p"])
        self.assertTrue(streams["worst"] is streams["350k"])
        self.assertTrue(isinstance(streams["rtmp"], RTMPStream))
        self.assertTrue(isinstance(streams["http"], HTTPStream))
        self.assertTrue(isinstance(streams["hls"], HLSStream))
        self.assertTrue(isinstance(streams["akamaihd"], AkamaiHDStream))

    def test_plugin_stream_types(self):
        channel = self.session.resolve_url("http://test.se/channel")
        streams = channel.get_streams(stream_types=["http", "rtmp"])

        self.assertTrue(isinstance(streams["480p"], HTTPStream))
        self.assertTrue(isinstance(streams["480p_rtmp"], RTMPStream))

        streams = channel.get_streams(stream_types=["rtmp", "http"])

        self.assertTrue(isinstance(streams["480p"], RTMPStream))
        self.assertTrue(isinstance(streams["480p_http"], HTTPStream))

    def test_plugin_stream_sorted_excludes(self):
        channel = self.session.resolve_url("http://test.se/channel")
        streams = channel.get_streams(sorting_excludes=["1080p", "3000k"])

        self.assertTrue("best" in streams)
        self.assertTrue("worst" in streams)
        self.assertTrue(streams["best"] is streams["1500k"])

        streams = channel.get_streams(sorting_excludes=[">=1080p", ">1500k"])
        self.assertTrue(streams["best"] is streams["1500k"])

        streams = channel.get_streams(sorting_excludes=lambda q: not q.endswith("p"))
        self.assertTrue(streams["best"] is streams["3000k"])

    def test_plugin_support(self):
        channel = self.session.resolve_url("http://test.se/channel")
        streams = channel.get_streams()

        self.assertTrue("support" in streams)
        self.assertTrue(isinstance(streams["support"], HTTPStream))
コード例 #8
0
class TestPluginStream(unittest.TestCase):
    def setUp(self):
        self.session = Livecli()

    def assertDictHas(self, a, b):
        for key, value in a.items():
            self.assertEqual(b[key], value)

    def _test_akamaihd(self, surl, url):
        channel = self.session.resolve_url(surl)
        streams = channel.get_streams()

        self.assertTrue("live" in streams)

        stream = streams["live"]
        self.assertTrue(isinstance(stream, AkamaiHDStream))
        self.assertEqual(stream.url, url)

    @patch('livecli.stream.HLSStream.parse_variant_playlist')
    def _test_hls(self, surl, url, mock_parse):
        mock_parse.return_value = {}

        channel = self.session.resolve_url(surl)
        streams = channel.get_streams()

        self.assertTrue("live" in streams)
        mock_parse.assert_called_with(self.session, url)

        stream = streams["live"]
        self.assertTrue(isinstance(stream, HLSStream))
        self.assertEqual(stream.url, url)

    @patch('livecli.stream.HLSStream.parse_variant_playlist')
    def _test_hlsvariant(self, surl, url, mock_parse):
        mock_parse.return_value = {"best": HLSStream(self.session, url)}

        channel = self.session.resolve_url(surl)
        streams = channel.get_streams()

        mock_parse.assert_called_with(self.session, url)

        self.assertFalse("live" in streams)
        self.assertTrue("best" in streams)

        stream = streams["best"]
        self.assertTrue(isinstance(stream, HLSStream))
        self.assertEqual(stream.url, url)

    def _test_rtmp(self, surl, url, params):
        channel = self.session.resolve_url(surl)
        streams = channel.get_streams()

        self.assertTrue("live" in streams)

        stream = streams["live"]
        self.assertTrue(isinstance(stream, RTMPStream))
        self.assertEqual(stream.params["rtmp"], url)
        self.assertDictHas(params, stream.params)

    def _test_http(self, surl, url, params):
        channel = self.session.resolve_url(surl)
        streams = channel.get_streams()

        self.assertTrue("live" in streams)

        stream = streams["live"]
        self.assertTrue(isinstance(stream, HTTPStream))
        self.assertEqual(stream.url, url)
        self.assertDictHas(params, stream.args)

    def test_plugin_rtmp(self):
        self._test_rtmp("rtmp://hostname.se/stream",
                        "rtmp://hostname.se/stream", dict())

        self._test_rtmp("rtmp://hostname.se/stream live=1 qarg='a \\'string' noq=test",
                        "rtmp://hostname.se/stream", dict(live=True, qarg='a \'string', noq="test"))

        self._test_rtmp("rtmp://hostname.se/stream live=1 num=47",
                        "rtmp://hostname.se/stream", dict(live=True, num=47))

        self._test_rtmp("rtmp://hostname.se/stream conn=['B:1','S:authMe','O:1','NN:code:1.23','NS:flag:ok','O:0']",
                        "rtmp://hostname.se/stream",
                        dict(conn=['B:1', 'S:authMe', 'O:1', 'NN:code:1.23', 'NS:flag:ok', 'O:0']))

    def test_plugin_hls(self):
        self._test_hls("hls://https://hostname.se/playlist.m3u8",
                       "https://hostname.se/playlist.m3u8")

        self._test_hls("hls://hostname.se/playlist.m3u8",
                       "http://hostname.se/playlist.m3u8")

        self._test_hlsvariant("hls://hostname.se/playlist.m3u8",
                              "http://hostname.se/playlist.m3u8")

        self._test_hlsvariant("hls://https://hostname.se/playlist.m3u8",
                              "https://hostname.se/playlist.m3u8")

    def test_plugin_akamaihd(self):
        self._test_akamaihd("akamaihd://http://hostname.se/stream",
                            "http://hostname.se/stream")

        self._test_akamaihd("akamaihd://hostname.se/stream",
                            "http://hostname.se/stream")

    def test_plugin_http(self):
        self._test_http("httpstream://http://hostname.se/auth.php auth=('test','test2')",
                        "http://hostname.se/auth.php", dict(auth=("test", "test2")))

        self._test_http("httpstream://hostname.se/auth.php auth=('test','test2')",
                        "http://hostname.se/auth.php", dict(auth=("test", "test2")))

        self._test_http("httpstream://https://hostname.se/auth.php verify=False params={'key': 'a value'}",
                        "https://hostname.se/auth.php?key=a+value", dict(verify=False, params=dict(key='a value')))

    def test_parse_params(self):
        self.assertEqual(
            dict(verify=False, params=dict(key="a value")),
            parse_params("""verify=False params={'key': 'a value'}""")
        )
        self.assertEqual(
            dict(verify=False),
            parse_params("""verify=False""")
        )
        self.assertEqual(
            dict(conn=['B:1', 'S:authMe', 'O:1', 'NN:code:1.23', 'NS:flag:ok', 'O:0']),
            parse_params(""""conn=['B:1', 'S:authMe', 'O:1', 'NN:code:1.23', 'NS:flag:ok', 'O:0']""")
        )

    def test_stream_weight(self):
        self.assertEqual(
            (720, "pixels"),
            stream_weight("720p"))
        self.assertEqual(
            (721, "pixels"),
            stream_weight("720p+"))
        self.assertEqual(
            (780, "pixels"),
            stream_weight("720p60"))

        self.assertTrue(
            stream_weight("720p+") > stream_weight("720p"))
        self.assertTrue(
            stream_weight("720p") == stream_weight("720p"))
        self.assertTrue(
            stream_weight("720p_3000k") > stream_weight("720p_2500k"))
        self.assertTrue(
            stream_weight("720p60_3000k") > stream_weight("720p_3000k"))
        self.assertTrue(
            stream_weight("720p_3000k") < stream_weight("720p+_3000k"))

        self.assertTrue(
            stream_weight("3000k") > stream_weight("2500k"))