m3u8 proxy for shortcut.lv streams

ltcproxy.py 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. #!/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. Shortcut.lv proxy server
  5. usage: %s start|stop|restart|manualstart [options]
  6. -p PORT - port number
  7. -s WSGI_SERVER - wsgi server - wsgiref,cheroot,mtwsgi,waitress...
  8. -d - debug printout
  9. -r - remote debug mode (ptvsd)"""
  10. __version__ = "0.1b"
  11. import os, sys, time
  12. import urllib,urlparse, urllib2, requests
  13. from urllib import unquote, quote
  14. import re, json
  15. import ConfigParser, getopt
  16. import arrow
  17. from diskcache import Cache
  18. import daemonize
  19. import bottle
  20. from bottle import Bottle, hook, response, route, request, run
  21. cunicode = lambda s: s.decode("utf8") if isinstance(s, str) else s
  22. cstr = lambda s: s.encode("utf8") if isinstance(s, unicode) else s
  23. headers2dict = lambda h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
  24. headers0 = headers2dict("""
  25. User-Agent: Shortcut.lv v2.9.1 / Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-G900FD Build/KOT49H)
  26. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
  27. """)
  28. url0 = "https://manstv.lattelecom.tv/api/v1.7/get/content/"
  29. cur_directory = os.path.dirname(os.path.realpath(__file__))
  30. cache_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "cache")
  31. if not os.path.exists(cache_dir):
  32. os.mkdir(cache_dir)
  33. CFG_FILE = "ltcproxy.cfg" # TODO - iespēja uzdot kā parametru
  34. config = ConfigParser.ConfigParser()
  35. proxy_cfg_file = os.path.join(cur_directory, CFG_FILE)
  36. DEBUG = False
  37. PORT_NUMBER = 8881
  38. REDIRECT = False
  39. CACHE = True
  40. KEY = ["0000","00000"]
  41. SERVER = "wsgiref"
  42. WORKERS = 10
  43. LTC_USER = "user"
  44. LTC_PASSWORD = "password"
  45. UID = "5136baee57505694"
  46. if not os.path.exists(proxy_cfg_file):
  47. config.add_section("ltcproxy")
  48. config.set("ltcproxy", "debug", DEBUG)
  49. config.set("ltcproxy", "port", PORT_NUMBER)
  50. config.set("ltcproxy", "redirect", REDIRECT)
  51. config.set("ltcproxy", "cache", CACHE)
  52. config.set("ltcproxy", "key", " ".join(KEY))
  53. config.set("ltcproxy", "wsgi", SERVER)
  54. config.set("ltcproxy", "workers", WORKERS)
  55. config.set("ltcproxy", "ltc_user", LTC_USER)
  56. config.set("ltcproxy", "ltc_password", LTC_PASSWORD)
  57. config.write(open(proxy_cfg_file, "w"))
  58. else:
  59. config.read(proxy_cfg_file)
  60. DEBUG = config.getboolean("ltcproxy", "debug")
  61. PORT_NUMBER = config.getint("ltcproxy", "port")
  62. REDIRECT = config.getboolean("ltcproxy", "redirect")
  63. CACHE = config.getboolean("ltcproxy", "cache")
  64. KEY = config.get("ltcproxy", "key").split(" ")
  65. SERVER = config.get("ltcproxy", "wsgi")
  66. WORKERS = config.getint("ltcproxy", "workers")
  67. LTC_USER = config.get("ltcproxy", "ltc_user")
  68. LTC_PASSWORD = config.get("ltcproxy", "ltc_password")
  69. s = Cache(cache_dir)
  70. app = Bottle()
  71. token = None
  72. ########################################################################################
  73. @app.hook('before_request')
  74. def set_globals():
  75. global s, headers0, token
  76. key = request.path.split("/")[1]
  77. if not key in KEY:
  78. print "Error: Wrong key - %s"% key
  79. raise bottle.HTTPError(500, "Wrong key")
  80. s = Cache(cache_dir)
  81. if "token" in s and s["token"]:
  82. token = s["token"]
  83. else:
  84. token = login(LTC_USER, LTC_PASSWORD)
  85. if token:
  86. s.set("token", token, expire=3600*24*1) # pēc 1d ekspirejas
  87. print "** %s: token=%s" % (request.remote_addr,token)
  88. else:
  89. print "Can not login %s/%s"%(LTC_USER, LTC_PASSWORD)
  90. raise bottle.HTTPError(500, "Can not login")
  91. # @app.route('/playstream/<url:re:.*>')
  92. ### Live playlist ###
  93. @app.route("/<key>/live/<ch>/")
  94. def get_live(key, ch):
  95. global s, token, headers0
  96. path0, rest = hls_split(request.url)
  97. response.content_type = "application/x-mpegURL" # r.headers["content-type"] # 'application/vnd.apple.mpegurl' # application/x-mpegURL
  98. if "c"+ch in s and "m"+ch in s:
  99. stream_url2 = s["c"+ch]
  100. mediaid = s["m"+ch]
  101. print "** %s: serving live playlist for %s (%s) from cache" % (request.remote_addr,path0,mediaid )
  102. else:
  103. stream_url2, mediaid = refresh_live_chunklist_url(ch)
  104. print "** %s: getting ive playlist for %s (%s)" % (request.remote_addr,path0,mediaid )
  105. stream_url2 += token
  106. if REDIRECT:
  107. bottle.redirect(stream_url2, 307)
  108. for i in range(3):
  109. r2 = requests.get(stream_url2,headers=headers0)
  110. if r2.status_code == 200:
  111. break
  112. time.sleep(1)
  113. else:
  114. print "Error %s getting live chunklist %s"% (r2.status_code,stream_url2)
  115. raise bottle.HTTPError(r2.status_code)
  116. return r2.content
  117. ### Live TS chunk ###
  118. @app.route("/<key>/live/<ch>/<tail>")
  119. def get_live_chunk(key, ch, tail):
  120. global s, token, headers0
  121. path0, rest = hls_split(request.url)
  122. chid = re.search("resource_id=c-(\\w+)",rest).group(1)
  123. chunkid = re.search("(\d+)\.ts", request.url).group(1)
  124. path2 = ch + "/" + chunkid
  125. if CACHE and path2 in s:
  126. print "** %s: serving live ts %s from cache" % (request.remote_addr,path2)
  127. f = s.get(path2, read=True)
  128. response.headers["Content-Type"] = s[path2+"@"] #'video/MP2T'
  129. response.headers["Content-Length"] = s[path2+"#"]
  130. while True:
  131. chunk = f.read(8192)
  132. if not chunk:
  133. break
  134. yield chunk
  135. else: # no cache
  136. if ch in s:
  137. stream_url = s[ch]
  138. mediaid= s["m"+ch]
  139. else:
  140. refresh_live_chunklist_url(ch)
  141. if ch in s:
  142. stream_url = s[ch]
  143. mediaid= s["m"+ch]
  144. else:
  145. print "No stream_url %s in cache" % path0
  146. raise bottle.HTTPError(500)
  147. base0, rest0 = hls_base(stream_url)
  148. rest2 = "media_%s_%s.ts?resource_id=c-%s&auth_token=app_" % (mediaid, chunkid, chid)
  149. url = base0 + rest2 + token
  150. url2 = hls_base(stream_url)[0] + rest
  151. headers = dict(request.headers)
  152. del headers["Host"]
  153. # headers["Authorization"] = "Bearer " + token
  154. print "** %s: getting live ts from %s(%s)- %s" % (request.remote_addr, path2, mediaid,url[:40])
  155. if DEBUG:
  156. print "=== Request headers ==="
  157. print_headers(headers)
  158. r = requests.get(url, stream=True, headers=headers0)
  159. if r.status_code <> 200:
  160. r = requests.get(url, stream=True, headers=headers0) # try once more
  161. if r.status_code <> 200:
  162. # Refresh chunklist
  163. print "## %s: Refreshing chunklist/mediaid for live channel %s" %(request.remote_addr, ch)
  164. chunklist_url, mediaid = refresh_live_chunklist_url(ch)
  165. rest2 = "media_%s_%s.ts?resource_id=c-%s&auth_token=app_" % (mediaid, chunkid, chid)
  166. url = base0 + rest2 + token
  167. url2 = chunklist_url + token
  168. print "** %s: getting live ts from %s(%s)- %s" % (request.remote_addr, path2, mediaid,url[:40])
  169. r = requests.get(url, stream=True, headers=headers0)
  170. if r.status_code <> 200:
  171. print "Error %s opening stream \n%s" %(r.status_code,url)
  172. print url2
  173. raise bottle.HTTPError(r.status_code, "Error opening stream "+url)
  174. content = ""
  175. response.content_type = r.headers["content-type"] # 'application/vnd.apple.mpegurl' #
  176. # response.headers.clear()
  177. for k in r.headers:
  178. response.headers[k] = r.headers[k]
  179. if DEBUG:
  180. print "=== Response headers ==="
  181. print_headers(response.headers)
  182. for chunk in r.iter_content(chunk_size=8192):
  183. if chunk:
  184. content += chunk
  185. yield chunk
  186. if len(content) <> int(r.headers["content-length"]):
  187. print "Content length problem"
  188. if CACHE:
  189. s.set(path2, content, expire=3600, read=True)
  190. s.set(path2+"#", len(content), expire=3600, read=True)
  191. s.set(path2+"@", r.headers["Content-Type"], expire=3600, read=True)
  192. ### Archive playlist ###
  193. @app.route("/<key>/live/<ch>/<ts>/")
  194. def get_archive(key, ch, ts):
  195. global s, token, headers0
  196. path0, rest = hls_split(request.url)
  197. start = int(ts) + 60 * 5
  198. epg = get_epg(ch, start)
  199. print "** %s: getting archive playlist for channel %s" % (request.remote_addr,path0)
  200. if epg:
  201. epgid = epg["id"]
  202. epg_start = int(epg["attributes"]["unix-start"])
  203. epg_stop = int(epg["attributes"]["unix-stop"])
  204. epg_title = epg["attributes"]["title"]
  205. else:
  206. print "EPG not found"
  207. raise bottle.HTTPError(500, "EPG not found")
  208. stream_url = epg_get_stream_url(epgid)
  209. if REDIRECT:
  210. bottle.redirect(stream_url, 307)
  211. # Getting chunklist
  212. stream_url2, mediaid = refresh_epg_chunklist_url(stream_url)
  213. r2 = requests.get(stream_url2)
  214. if r2.status_code <> 200:
  215. print "Error %s getting archive chunklist %s"% (r2.status_code,stream_url2)
  216. raise bottle.HTTPError(r2.status_code)
  217. result = re.findall(r"#EXTINF:([\d\.]+),\n(.+)", r2.content)
  218. ll = 0
  219. i = 0
  220. for chunk_len, chunk_url in result:
  221. ll += float(chunk_len)
  222. if ll > (start - epg_start):
  223. break
  224. i += 1
  225. result2 =result[i:]
  226. content = re.search("(^.+?)#EXTINF", r2.content, re.DOTALL).group(1)
  227. for chunk_len, chunk_url in result2:
  228. content += "#EXTINF:%s,\n" % chunk_len
  229. content += chunk_url + "\n"
  230. content += "#EXT-X-ENDLIST"
  231. response.content_type = r2.headers["content-type"] # 'application/vnd.apple.mpegurl' #
  232. return content
  233. def live_get_stream_url(ch):
  234. global s, token, headers0
  235. if ch in s:
  236. stream_url = s[ch]
  237. stream_url += token
  238. else:
  239. # Getting live stream url
  240. url = url0 + "live-streams/%s?include=quality&auth_token=app_%s" % (ch, token)
  241. headers = headers0.copy()
  242. headers["Authorization"] = "Bearer " + token
  243. r = requests.get(url, headers=headers)
  244. if r.status_code <> 200:
  245. print "Error getting epg stream url "+url
  246. raise bottle.HTTPError(r.status_code, "Error getting epg stream url "+url)
  247. js = json.loads(r.content)
  248. stream_url = js["data"][0]["attributes"]["stream-url"]
  249. stream_url0 = stream_url.replace(token, "")
  250. s.set(ch, stream_url0, expire=3600*24*7, read=False)
  251. return str(stream_url)
  252. def epg_get_stream_url(epgid):
  253. global s, token, headers0
  254. if epgid in s:
  255. stream_url = s[epgid]
  256. stream_url += token
  257. else:
  258. # Getting epg stream url
  259. url = url0 + "record-streams/%s?include=quality&auth_token=app_%s" % (epgid, token)
  260. headers = headers0.copy()
  261. headers["Authorization"] = "Bearer " + token
  262. r = requests.get(url, headers=headers)
  263. if r.status_code <> 200:
  264. print "Error getting epg stream url "+url
  265. raise bottle.HTTPError(r.status_code, "Error getting epg stream url "+url)
  266. js = json.loads(r.content)
  267. stream_url = js["data"][0]["attributes"]["stream-url"]
  268. stream_url0 = stream_url.replace(token, "")
  269. s.set(epgid, stream_url0, expire=3600*24*7, read=False)
  270. return str(stream_url)
  271. def refresh_live_chunklist_url(ch):
  272. global s, token, headers0
  273. stream_url = live_get_stream_url(ch)
  274. r = requests.get(stream_url)
  275. if r.status_code <> 200:
  276. print "Error %s getting live chunklist %s"% (r.status_code,stream_url)
  277. raise bottle.HTTPError(r.status_code)
  278. chid = re.search("resource_id=c\\-(\\w+)",stream_url).group(1)
  279. rest2 = re.search("chunklist.+$", r.content).group(0).replace(token,"")
  280. mediaid = re.search("chunklist_(.+?)\\.m3u8",rest2).group(1)
  281. base2 = hls_base(stream_url)[0]
  282. stream_url2 = base2 + rest2 # chunlist url
  283. s.set("m"+ch, mediaid, expire=3600*24*7, read=False)
  284. s.set("c"+ch, stream_url2, expire=3600*24*7, read=False)
  285. return stream_url2,mediaid
  286. def refresh_epg_chunklist_url(stream_url):
  287. global s, token, headers0
  288. r = requests.get(stream_url)
  289. if r.status_code <> 200:
  290. print "Error %s getting archive chunklist %s"% (r.status_code,stream_url)
  291. raise bottle.HTTPError(r.status_code)
  292. epgid = re.search("resource_id=a-(\\d+)",stream_url).group(1)
  293. rest2 = re.search("chunklist.+$", r.content).group(0)
  294. mediaid = re.search("chunklist_(.+?)\\.m3u8",rest2).group(1)
  295. s.set("m"+epgid, mediaid, expire=3600*24*7, read=False)
  296. base2 = hls_base(stream_url)[0]
  297. stream_url2 = base2 + rest2 # chunlist url
  298. return stream_url2,mediaid
  299. ### Archive ts chunk ###
  300. @app.route("/<key>/live/<ch>/<ts>/<tail>")
  301. def get_archive_chunk(key, ch, ts, tail):
  302. global s, token, headers0
  303. path0, rest = hls_split(request.url)
  304. epgid = re.search("resource_id=a-(\\d+)",rest).group(1)
  305. chunkid = re.search("(\\d+)\\.ts", rest).group(1)
  306. path2 = epgid + "/" + chunkid
  307. if CACHE and path2 in s:
  308. print "** %s: serving archive ts from cache %s" % (request.remote_addr,path2)
  309. f = s.get(path2, read=True)
  310. response.headers["Content-Type"] = s[path2+"@"] #'video/MP2T'
  311. response.headers["Content-Length"] = s[path2+"#"]
  312. while True:
  313. chunk = f.read(8192)
  314. if not chunk:
  315. break
  316. yield chunk
  317. else: # No cache
  318. stream_url = epg_get_stream_url(epgid)
  319. if "m"+epgid in s:
  320. mediaid= s["m"+epgid]
  321. else:
  322. chunklist_url, mediaid = refresh_epg_chunklist_url(stream_url)
  323. base0, rest0 = hls_base(stream_url)
  324. #media_w76603200_0.ts?resource_id=a-6559656352477&auth_token=app_
  325. rest2 = "media_%s_%s.ts?resource_id=a-%s&auth_token=app_" % (mediaid, chunkid, epgid)
  326. url = base0 + rest2 + token
  327. print "** %s: getting archive ts from %s(%s) - %s" % (request.remote_addr,path2, mediaid, rest2[:rest2.index("?")])
  328. #print url
  329. headers = dict(request.headers)
  330. del headers["Host"]
  331. # headers["Authorization"] = "Bearer " + token
  332. r = requests.get(url, stream=True, headers=headers)
  333. if r.status_code <> 200:
  334. r = requests.get(url, stream=True, headers=headers) # try once more
  335. if r.status_code <> 200:
  336. # Refresh chunklist
  337. print "## %s: Refreshing chunklist/mediaid for epg %s" %(request.remote_addr, epgid)
  338. chunklist_url, mediaid = refresh_epg_chunklist_url(stream_url)
  339. rest2 = "media_%s_%s.ts?resource_id=a-%s&auth_token=app_" % (mediaid, chunkid, epgid)
  340. url = base0 + rest2 + token
  341. print "** %s: getting archive ts from %s(%s) - %s" % (request.remote_addr, path2, mediaid, rest2[:rest2.index("?")])
  342. r = requests.get(url, stream=True, headers=headers0)
  343. if r.status_code <> 200:
  344. print "Error %s opening stream \n%s" %(r.status_code,url)
  345. raise bottle.HTTPError(r.status_code, "Error opening stream "+url)
  346. content = ""
  347. response.content_type = r.headers["content-type"] # 'application/vnd.apple.mpegurl' #
  348. # response.headers.clear()
  349. for k in r.headers:
  350. response.headers[k] = r.headers[k]
  351. if DEBUG:
  352. print_headers(response.headers)
  353. for chunk in r.iter_content(chunk_size=8192):
  354. if chunk:
  355. content += chunk
  356. yield chunk
  357. if CACHE:
  358. path2 = epgid + "/" + chunkid
  359. s.set(path2, content, expire=3600, read=True)
  360. s.set(path2+"#", len(content), expire=3600, read=True)
  361. s.set(path2+"@", r.headers["Content-Type"], expire=3600, read=True)
  362. @app.route("/<key>/vod/<ch>/")
  363. def get_vod(key, ch):
  364. global s, token, headers0
  365. path0, rest = hls_split(request.url)
  366. if path0 in s:
  367. stream_url = s[path0] + token
  368. print "** %s: getting vod to %s from cache (%s)" % (request.remote_addr, path0)
  369. else:
  370. url = url0 + "vod-streams/%s?include=language,subtitles,quality &auth_token=app_%s" % (ch, token)
  371. headers = headers0.copy()
  372. headers["Authorization"] = "Bearer " + token
  373. r = requests.get(url, headers=headers)
  374. if r.status_code <> 200:
  375. raise bottle.HTTPError(r.status_code, "Error opening stream "+url)
  376. js = json.loads(r.content)
  377. stream_url = js["data"][0]["attributes"]["stream-url"]
  378. stream_url0 = stream_url.replace(token, "")
  379. s.set(path0, stream_url0, expire=3600*24*7, read=False)
  380. print "** %s: changing vod to %s (%s)" % (request.remote_addr, path0)
  381. if True: # REDIRECT:
  382. bottle.redirect(stream_url, 307)
  383. r = requests.get(stream_url)
  384. if r.status_code <> 200:
  385. raise bottle.HTTPError(r.status_code)
  386. response.content_type = r.headers["content-type"] # 'application/vnd.apple.mpegurl' #
  387. return r.content
  388. def get_epg(ch, start):
  389. url = url0 + "epgs/?filter[channel]=%s&filter[utFrom]=%s&filter[utTo]=%s&include=channel&page[size]=40page[number]=1" % (ch, start, start )
  390. r = requests.get(url)
  391. if r.status_code <> 200:
  392. raise bottle.HTTPError(500, "EPG not found")
  393. js = json.loads(r.content)
  394. if "data" in js:
  395. for epg in js["data"]:
  396. if int(epg["id"]) < 0:
  397. continue
  398. else:
  399. break
  400. return epg
  401. else:
  402. return None
  403. ####################################################################
  404. # Run WSGI server
  405. def start(server,port):
  406. print "*** Starting ltcproxy ***"
  407. if login(LTC_USER, LTC_PASSWORD):
  408. print "Logged in, user: %s" % LTC_USER
  409. else:
  410. print "Can not login %s" % (LTC_USER)
  411. options = {}
  412. if server == "mtwsgi":
  413. import mtwsgi
  414. server = mtwsgi.MTServer
  415. options = {"thread_count": WORKERS,}
  416. run(app=app,server=server, host='0.0.0.0',
  417. port=port,
  418. reloader=False,
  419. quiet=False,
  420. plugins=None,
  421. debug=True,
  422. config=None,
  423. **options)
  424. def login(user,password):
  425. """Login in to site, get token"""
  426. # Dabūjam tokenu
  427. url = "https://manstv.lattelecom.tv/api/v1.7/post/user/users/%s" % user
  428. params = "uid=UID&password=%s&" % (password)
  429. headers = headers2dict("""
  430. User-Agent: Shortcut.lv v2.9.1 / Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-G900FD Build/KOT49H)
  431. Content-Type: application/x-www-form-urlencoded; charset=UTF-8
  432. Host: manstv.lattelecom.tv
  433. """ )
  434. try:
  435. r = urllib2.Request(url, data=params, headers=headers)
  436. u = urllib2.urlopen(r)
  437. content = u.read()
  438. u.close()
  439. except Exception as ex:
  440. if DEBUG:
  441. print "Login error: %s - %s" % (ex.code, ex.msg)
  442. print ex.hdrs
  443. return None
  444. if u and "token" in content:
  445. token = re.search('"token":"(.+?)"', content).group(1)
  446. return token
  447. else:
  448. if DEBUG:
  449. print "Error searching token in response"
  450. print content
  451. return False
  452. def refresh_token(token):
  453. """Refresh"""
  454. url = "https://manstv.lattelecom.tv/api/v1.7/post/user/refresh-token/"
  455. params = "uid=UID&token=%s&" % (token)
  456. headers = headers2dict("""
  457. User-Agent: Shortcut.lv v2.9.1 / Dalvik/1.6.0 (Linux; U; Android 4.4.2; SM-G900FD Build/KOT49H)
  458. Content-Type: application/x-www-form-urlencoded; charset=UTF-8
  459. Host: manstv.lattelecom.tv
  460. """ )
  461. try:
  462. r = urllib2.Request(url, data=params, headers=headers)
  463. u = urllib2.urlopen(r)
  464. content = u.read()
  465. u.close()
  466. except Exception as ex:
  467. return None
  468. if r and "token" in content:
  469. token2 = re.search('"token":"(.+?)"', content).group(1)
  470. return token2
  471. else:
  472. return False
  473. def print_headers(headers):
  474. for h in headers:
  475. print "%s: %s"%(h,headers[h])
  476. def del_headers(headers0,tags):
  477. headers = headers0.copy()
  478. for t in tags:
  479. if t in headers:
  480. del headers[t]
  481. if t.lower() in headers:
  482. del headers[t.lower()]
  483. return headers
  484. def hls_split(url):
  485. pp = urlparse.urlsplit(url)
  486. path0 = pp.path[:pp.path.rindex("/")+1]
  487. path0 = path0[path0.index("/", 1):]
  488. rest = pp.path[pp.path.rindex("/")+1:] + "?" + pp.query
  489. return path0, rest
  490. def hls_base(url):
  491. base = url.split("?")[0]
  492. base = "/".join(base.split("/")[0:-1])+ "/"
  493. rest = url.replace(base, "")
  494. return base, rest
  495. #########################################################################################
  496. if __name__ == '__main__':
  497. # 1561839586
  498. # get_epg("101", 1561839586)
  499. try:
  500. opts, args = getopt.gnu_getopt(sys.argv[1:], "p:s:dr", ["port=","server=","--debug"])
  501. except getopt.GetoptError as err:
  502. print str(err)
  503. print str(__doc__)
  504. sys.exit(2)
  505. opts = dict(opts)
  506. if not len(args):
  507. print str(__doc__)
  508. sys.exit(2)
  509. if "-r" in opts:
  510. print "Enabling remote debuging (ptvsd)"
  511. import ptvsd
  512. ptvsd.enable_attach(address = ('0.0.0.0', 5678),redirect_output=False)
  513. if "-d" in opts:
  514. print "Enabling debuging mode (more output)"
  515. DEBUG = True
  516. pid = "/var/run/ltcproxy.pid"
  517. daemon = daemonize.Daemon(start, pid)
  518. server = opts["-s"] if "-s" in opts else SERVER
  519. port = opts["-p"] if "-p" in opts else PORT_NUMBER
  520. if "start" == args[0]:
  521. s.clear()
  522. daemon.start(server,port)
  523. daemon.is_running()
  524. elif "stop" == args[0]:
  525. daemon.stop()
  526. elif "restart" == args[0]:
  527. s.clear()
  528. daemon.restart()
  529. daemon.is_running()
  530. elif "manualstart" == args[0]:
  531. s.clear()
  532. start(server,port)
  533. elif "status" == args[0]:
  534. daemon.is_running()
  535. else:
  536. print "Unknown command"
  537. print str(__doc__)
  538. sys.exit(2)
  539. sys.exit(0)