aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/livestreamer/utils.py
blob: 121a365aa1c34b2aa51be61d9e35133260d3c244 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
from .compat import is_win32
from .plugins import PluginError

import argparse
import hashlib
import hmac
import os
import requests
import tempfile
import zlib

if is_win32:
    from ctypes import windll, cast, c_ulong, c_void_p, byref

SWFKey = b"Genuine Adobe Flash Player 001"
RequestsConfig = { "danger_mode": True }

class ArgumentParser(argparse.ArgumentParser):
    def convert_arg_line_to_args(self, line):
        if len(line) == 0:
            return

        if line[0] == "#":
            return

        split = line.find("=")
        if split > 0:
            key = line[:split].strip()
            val = line[split+1:].strip()
            yield "--%s=%s" % (key, val)
        else:
            yield "--%s" % line

class NamedPipe(object):
    def __init__(self, name):
        self.fifo = None
        self.pipe = None

        if is_win32:
            self.path = os.path.join("\\\\.\\pipe", name)
            self.pipe = self._create_named_pipe(self.path)
        else:
            self.path = os.path.join(tempfile.gettempdir(), name)
            self._create_fifo(self.path)

    def _create_fifo(self, name):
        os.mkfifo(name, 0o660)

    def _create_named_pipe(self, path):
        PIPE_ACCESS_OUTBOUND = 0x00000002
        PIPE_TYPE_BYTE = 0x00000000
        PIPE_READMODE_BYTE = 0x00000000
        PIPE_WAIT = 0x00000000
        PIPE_UNLIMITED_INSTANCES = 255
        INVALID_HANDLE_VALUE = -1
        bufsize = 8192

        pipe = windll.kernel32.CreateNamedPipeA(path,
                                                PIPE_ACCESS_OUTBOUND,
                                                PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
                                                PIPE_UNLIMITED_INSTANCES,
                                                bufsize,
                                                bufsize,
                                                0,
                                                None)

        if pipe == INVALID_HANDLE_VALUE:
            raise IOError(("error code 0x{0:08X}").format(windll.kernel32.GetLastError()))

        return pipe

    def open(self, mode):
        if not self.pipe:
            self.fifo = open(self.path, mode)

    def write(self, data):
        if self.pipe:
            windll.kernel32.ConnectNamedPipe(self.pipe, None)
            written = c_ulong(0)
            windll.kernel32.WriteFile(self.pipe, cast(data, c_void_p),
                                      len(data), byref(written),
                                      None)
            return written
        else:
            return self.fifo.write(data)

    def close(self):
        if self.pipe:
            windll.kernel32.DisconnectNamedPipe(self.pipe)
        else:
            os.unlink(self.path)

class RingBuffer(object):
    def __init__(self):
        self.buffer = b""

    def read(self, size=0):
        if size < 1:
            ret = self.buffer[:]
            self.buffer = b""
        else:
            ret = self.buffer[:size]
            self.buffer = self.buffer[size:]

        return ret

    def write(self, data):
        self.buffer += data

    @property
    def length(self):
        return len(self.buffer)


def urlopen(url, method="get", exception=PluginError, **args):
    if "data" in args and args["data"] is not None:
        method = "post"

    try:
        res = requests.request(method, url, config=RequestsConfig, timeout=15, **args)
    except (requests.exceptions.RequestException, IOError) as err:
        raise exception(("Unable to open URL: {url} ({err})").format(url=url, err=str(err)))

    return res

def urlget(url, prefetch=True, **args):
    return urlopen(url, method="get", prefetch=prefetch,
                   **args)

def swfdecompress(data):
    if data[:3] == b"CWS":
        data = b"F" + data[1:8] + zlib.decompress(data[8:])

    return data

def swfverify(url):
    res = urlopen(url)
    swf = swfdecompress(res.content)

    h = hmac.new(SWFKey, swf, hashlib.sha256)

    return h.hexdigest(), len(swf)

def verifyjson(json, key):
    if not key in json:
        raise PluginError(("Missing '{0}' key in JSON").format(key))

    return json[key]

__all__ = ["ArgumentParser", "NamedPipe", "RingBuffer",
           "urlopen", "urlget", "swfdecompress", "swfverify", "verifyjson"]