Project

General

Profile

Create an addon that combine tvh and iptvsimple » htsp.py

awaw awaw, 2016-04-23 17:13

 
1
#!/usr/bin/env python
2
#
3
# Copyright (C) 2012 Adam Sutton <[email protected]>
4
#
5
# This program is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation, version 3 of the License.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
#
17
"""
18
This is a very simple HTSP client library written in python mainly just
19
for demonstration purposes.
20

    
21
Much of the code is pretty rough, but might help people get started
22
with communicating with HTSP server
23
"""
24

    
25
# ###########################################################################
26
# Utilities
27
# ###########################################################################
28

    
29
def int2bin ( i ):
30
  return chr(i >> 24 & 0xFF) + chr(i >> 16 & 0xFF)\
31
       + chr(i >> 16 & 0xFF) + chr(i & 0xFF)
32

    
33
def bin2int ( d ):
34
  return (ord(d[0]) << 24) + (ord(d[1]) << 16)\
35
       + (ord(d[2]) <<  8) + ord(d[3])
36

    
37
# ###########################################################################
38
# HTSMSG Binary handler
39
#
40
# Note: this will work with the python native types:
41
#   dict => HMF_MAP
42
#   list => HMF_LIST
43
#   str  => HMF_STR
44
#   int  => HMF_S64
45
#
46
# Note: BIN/STR are both equated to str in python
47
# ###########################################################################
48

    
49
# HTSMSG types
50
HMF_MAP  = 1
51
HMF_S64  = 2
52
HMF_STR  = 3
53
HMF_BIN  = 4
54
HMF_LIST = 5
55

    
56
# Convert python to HTSMSG type
57
def hmf_type ( f ):
58
  if type(f) == list:
59
    return HMF_MAP
60
  elif type(f) == dict:
61
    return HMF_LIST
62
  elif type(f) == str:
63
    return HMF_STR
64
  elif type(f) == int:
65
    return HMF_S64
66
  else:
67
    raise Exception('invalid type')
68

    
69
# Size for field
70
def _htsmsg_binary_count ( f ):
71
  ret = 0
72
  if type(f) == str:
73
    ret = ret + len(f)
74
  elif type(f) == int:
75
    while (f):
76
      ret = ret + 1
77
      f   = f >> 8
78
  elif type(f) == list or type(f) == map:
79
    ret = ret + htsmsg_binary_count(f)
80
  else:
81
    raise Exception('invalid data type')
82
  return ret
83
  
84
# Recursively determine size of message
85
def htsmsg_binary_count ( msg ):
86
  ret = 0
87
  lst = type(msg) == list
88
  for f in msg:
89
    ret = ret + 6
90
    if not lst: 
91
      ret = ret + len(f)
92
      f   = msg[f]
93
    ret = ret + _htsmsg_binary_count(f)
94
  return ret
95

    
96
# Write out field in binary form
97
def htsmsg_binary_write ( msg ):
98
  ret = ''
99
  lst = type(msg) == list
100
  for f in msg:
101
    na = ''
102
    if not lst:
103
      na = f
104
      f  = msg[f]
105
    ret = ret + chr(hmf_type(f))
106
    ret = ret + chr(len(na) & 0xFF)
107
    l   = _htsmsg_binary_count(f)
108
    ret = ret + int2bin(l)
109
    ret = ret + na
110

    
111
    if type(f) == list or type(f) == dict:
112
      ret = ret + htsmsg_binary_write(f)
113
    elif type(f) == str:
114
      ret = ret + f
115
    elif type(f) == int:
116
      while f:
117
        ret = ret + chr(f & 0xFF)
118
        f   = f >> 8
119
    else:
120
      raise Exception('invalid type')
121
  return ret
122

    
123
# Serialize a htsmsg
124
def htsmsg_binary_serialize ( msg ):
125
  cnt  = htsmsg_binary_count(msg)
126
  return int2bin(cnt) + htsmsg_binary_write(msg)
127

    
128
# Deserialize an htsmsg
129
def htsmsg_binary_deserialize ( data ):
130
  msg = {}
131
  while len(data) > 5:
132
    typ  = ord(data[0])
133
    nlen = ord(data[1])
134
    dlen = bin2int(data[2:6])
135
    data = data[6:]
136

    
137
    if len < nlen + dlen: raise Exception('not enough data')
138
    
139
    name = data[:nlen]
140
    data = data[nlen:]
141
    if typ in [ HMF_STR, HMF_BIN ]:
142
      item = data[:dlen]
143
    elif typ == HMF_S64:
144
      item = 0
145
      i    = dlen - 1
146
      while i >= 0:
147
        item = (item << 8) | ord(data[i])
148
        i    = i - 1
149
    elif typ in [ HMF_LIST, HMF_MAP ]:
150
      item = htsmsg_binary_deserialize(data[:dlen])
151
    else:
152
      raise Exception('invalid data type %d' % typ)
153
    msg[name] = item
154
    data      = data[dlen:]
155
  return msg
156
  
157
# ###########################################################################
158
# HTSP Client
159
# ###########################################################################
160

    
161
HTSP_PROTO_VERSION = 5
162

    
163
# Create passwd digest
164
def htsp_digest ( user, passwd, chal ):
165
  import hashlib
166
  ret = hashlib.sha1(passwd + chal).digest()
167
  return ret
168

    
169
# Client object
170
class HTSPClient:
171

    
172
  # Setup connection
173
  def __init__ ( self, addr, name = 'HTSP PyClient' ):
174

    
175
    # Setup
176
    self._sock = socket.create_connection(addr)
177
    self._sock.settimeout(1.0)
178
    self._name = name
179
    self._auth = None
180

    
181
    # Handshake
182
    self.hello()
183

    
184
  # Send
185
  def _send ( self, func, args ):
186
    args['method'] = func
187
    self._sock.send(htsmsg_binary_serialize(args))
188

    
189
  # Receive
190
  def _recv ( self ):
191
    num  = bin2int(self._sock.recv(4))
192
    data = ''
193
    while len(data) != num:
194
      tmp  = self._sock.recv(num - len(data))
195
      if not tmp: raise Exception('htsp_recv() failed')
196
      data = data + tmp
197
    return htsmsg_binary_deserialize(data)
198

    
199
  # Setup
200
  def hello ( self ):
201
    args = {
202
      'htspversion' : HTSP_PROTO_VERSION,
203
      'clientname'  : self._name
204
    }
205
    self._send('hello', args)
206
    resp = self._recv()
207

    
208
    # Validate
209
    if resp['htspversion'] != HTSP_PROTO_VERSION:
210
      raise Exception('version mismatch')
211
    self._auth = resp['challenge']
212
    
213
    # Output info
214
    print 'Connected to: %s (%s)' % (resp['servername'], resp['serverversion'])
215
    return resp
216

    
217
  # Authenticate
218
  def authenticate ( self, user, passwd = None ):
219
    args = {
220
      'challenge' : self._auth,
221
      'username'  : user,
222
    }
223
    if passwd:
224
      args['digest'] = htsp_digest(user, passwd, self._auth)
225
    self._send('authenticate', args)
226
    resp = self._recv()
227

    
228
    # Validate
229
    import pprint
230
    pprint.pprint(resp)
231

    
232
#
233
# Test
234
#
235

    
236
if __name__ == '__main__':
237
  import sys, socket
238
  host = 'localhost'
239
  if len(sys.argv) > 1: host = sys.argv[1]
240
  htsp = HTSPClient((host, 9982))
241
  htsp.authenticate('xbmc', 'xbmc')
(1-1/5)