Enigma2 plugin to to play various online streams (mostly Latvian).

itv.py 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #!/usr/bin/env python3
  2. # itv.py
  3. # Copyright (C) 2015 Jashandeep Sohi <jashandeep.s.sohi@gmail.com>
  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, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. import argparse
  18. from lxml import etree
  19. import requests
  20. import re
  21. from sys import stderr
  22. from os import path
  23. import subprocess
  24. LIVESTREAMER = "localhost:88"
  25. if __name__ == "__main__":
  26. arg_parser = argparse.ArgumentParser(
  27. description = "ITV downloader",
  28. )
  29. arg_parser.add_argument(
  30. "url",
  31. help = "programme url",
  32. action = "store",
  33. )
  34. arg_parser.add_argument(
  35. "filename",
  36. help = "output filename",
  37. action = "store",
  38. nargs = "?",
  39. )
  40. arg_parser.add_argument(
  41. "-d", "--dir",
  42. help = "download directory (default: '%(default)s')",
  43. action = "store",
  44. default = ".",
  45. )
  46. arg_parser.add_argument(
  47. "-s", "--start",
  48. help = "start at given time (default: '%(default)s')",
  49. action = "store",
  50. default = 0,
  51. type = int,
  52. )
  53. arg_parser.add_argument(
  54. "-e", "--end",
  55. help = "end at given time (default: '%(default)s')",
  56. action = "store",
  57. default = -1,
  58. type = int,
  59. )
  60. arg_parser.add_argument(
  61. "-p", "--proxy",
  62. help = "proxy address (e.g. 'http://2.13.1.2:1234', "
  63. "'socks://3.1.2.3:1234', etc)",
  64. action = "store",
  65. default = ""
  66. )
  67. resume_group = arg_parser.add_mutually_exclusive_group()
  68. resume_group.add_argument(
  69. "-r", "--resume",
  70. help = "attempt to resume (set by default)",
  71. action = "store_true",
  72. default = True
  73. )
  74. resume_group.add_argument(
  75. "-R", "--no-resume",
  76. help = "don't attempt to resume (overwrites old file)",
  77. action = "store_false",
  78. dest = "resume"
  79. )
  80. args = arg_parser.parse_args()
  81. session = requests.Session()
  82. session.headers.update({
  83. "User-Agent": "Mozilla/5.0 (Windows NT 5.1; rv:32.0) Gecko/20100101 "
  84. "Firefox/32.0",
  85. })
  86. page_req = session.get(args.url)
  87. pid_match = re.search('data-video-id="(.*?)"', page_req.text)
  88. try:
  89. pid = pid_match.group(1).replace("\\", "")
  90. except:
  91. arg_parser.error("could not parse video pid from page")
  92. soap_msg1 = """
  93. <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/" xmlns:itv="http://schemas.datacontract.org/2004/07/Itv.BB.Mercury.Common.Types" xmlns:com="http://schemas.itv.com/2009/05/Common">
  94. <soapenv:Header/>
  95. <soapenv:Body>
  96. <tem:GetPlaylist>
  97. <tem:request>
  98. <itv:ProductionId>{pid}</itv:ProductionId>
  99. <itv:RequestGuid>FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF</itv:RequestGuid>
  100. <itv:Vodcrid>
  101. <com:Id/>
  102. <com:Partition>itv.com</com:Partition>
  103. </itv:Vodcrid>
  104. </tem:request>
  105. <tem:userInfo>
  106. <itv:Broadcaster>Itv</itv:Broadcaster>
  107. <itv:GeoLocationToken>
  108. <itv:Token/>
  109. </itv:GeoLocationToken>
  110. <itv:RevenueScienceValue>ITVPLAYER.12.18.4</itv:RevenueScienceValue>
  111. <itv:SessionId/>
  112. <itv:SsoToken/>
  113. <itv:UserToken/>
  114. </tem:userInfo>
  115. <tem:siteInfo>
  116. <itv:AdvertisingRestriction>None</itv:AdvertisingRestriction>
  117. <itv:AdvertisingSite>ITV</itv:AdvertisingSite>
  118. <itv:AdvertisingType>Any</itv:AdvertisingType>
  119. <itv:Area>ITVPLAYER.VIDEO</itv:Area>
  120. <itv:Category/>
  121. <itv:Platform>DotCom</itv:Platform>
  122. <itv:Site>ItvCom</itv:Site>
  123. </tem:siteInfo>
  124. <tem:deviceInfo>
  125. <itv:ScreenSize>Big</itv:ScreenSize>
  126. </tem:deviceInfo>
  127. <tem:playerInfo>
  128. <itv:Version>2</itv:Version>
  129. </tem:playerInfo>
  130. </tem:GetPlaylist>
  131. </soapenv:Body>
  132. </soapenv:Envelope>
  133. """
  134. soap_msg2="""
  135. <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/" xmlns:itv="http://schemas.datacontract.org/2004/07/Itv.BB.Mercury.Common.Types" xmlns:com="http://schemas.itv.com/2009/05/Common">
  136. <soapenv:Header/>
  137. <soapenv:Body>
  138. <tem:GetPlaylist>
  139. <tem:request>
  140. <itv:ProductionId/>
  141. <itv:RequestGuid>A26C4B97-C300-F83A-C5A6-2C0FD584D9B8</itv:RequestGuid>
  142. <itv:Vodcrid>
  143. <com:Id>{pid}</com:Id>
  144. <com:Partition>itv.com</com:Partition>
  145. </itv:Vodcrid>
  146. </tem:request>
  147. <tem:userInfo>
  148. <itv:Broadcaster>Itv</itv:Broadcaster>
  149. <itv:DM>584e4d9a-fa89-4398-9033-f6ed4dead584</itv:DM>
  150. <itv:RevenueScienceValue>ITVPLAYER.2.18.10.+build.21057aabd9</itv:RevenueScienceValue>
  151. <itv:SessionId/>
  152. <itv:SsoToken/>
  153. <itv:UserToken/>
  154. </tem:userInfo>
  155. <tem:siteInfo>
  156. <itv:AdvertisingRestriction>None</itv:AdvertisingRestriction>
  157. <itv:AdvertisingSite>ITV</itv:AdvertisingSite>
  158. <itv:AdvertisingType>Any</itv:AdvertisingType>
  159. <itv:Area>ITVPLAYER.VIDEO</itv:Area>
  160. <itv:Category/>
  161. <itv:Platform>DotCom</itv:Platform>
  162. <itv:Site>ItvCom</itv:Site>
  163. </tem:siteInfo>
  164. <tem:deviceInfo>
  165. <itv:ScreenSize>Big</itv:ScreenSize>
  166. </tem:deviceInfo>
  167. <tem:playerInfo>
  168. <itv:Version>2</itv:Version>
  169. </tem:playerInfo>
  170. </tem:GetPlaylist>
  171. </soapenv:Body>
  172. </soapenv:Envelope>
  173. """
  174. if re.search("sim\d",pid):
  175. soap_msg = soap_msg2.format(pid = pid)
  176. is_live = True
  177. else:
  178. soap_msg = soap_msg1.format(pid = pid)
  179. is_live = False
  180. media_req = session.post(
  181. "http://mercury.itv.com/PlaylistService.svc",
  182. headers = {
  183. "Referer": "http://www.itv.com/mercury/Mercury_VideoPlayer.swf?v=1.6.479/[[DYNAMIC]]/2",
  184. "Content-type": "text/xml; charset=utf-8",
  185. "SOAPAction": "http://tempuri.org/PlaylistService/GetPlaylist"
  186. },
  187. data = soap_msg,
  188. proxies = {"http": args.proxy} if args.proxy else None,
  189. timeout = 20
  190. )
  191. media_req_tree = etree.fromstring(media_req.text)
  192. #print media_req.text
  193. title = media_req_tree.xpath("//ProgrammeTitle/text()")[0] if media_req_tree.xpath("//ProgrammeTitle/text()") else "title"
  194. ep_title = media_req_tree.xpath("//EpisodeTitle/text()")[0] if media_req_tree.xpath("//EpisodeTitle/text()") else "episode"
  195. media_files = media_req_tree.xpath("//VideoEntries/Video/MediaFiles")[0]
  196. rtmp_url = media_files.xpath("@base")[0]
  197. media_file = media_files.xpath(
  198. "MediaFile[not(../MediaFile/@bitrate > @bitrate)]"
  199. )[0]
  200. bitrate = int(media_file.xpath("@bitrate")[0]) // 1000
  201. rtmp_playpath = media_file.xpath("URL/text()")[0]
  202. file_path = path.join(
  203. args.dir,
  204. args.filename if args.filename else re.sub(
  205. "[\W_]+",
  206. "-",
  207. "-".join(map(str, filter(None, (
  208. title,
  209. ep_title,
  210. bitrate,
  211. args.start,
  212. args.end if args.end > 0 else None
  213. ))))
  214. ).lower() + ".flv"
  215. )
  216. print "http://%s/%s"%(LIVESTREAMER,rtmp_url),
  217. print "playpath="+rtmp_playpath,
  218. print "pageUrl="+args.url,
  219. if is_live: print "live",
  220. print "swfVfy="+"http://www.itv.com/mercury/Mercury_VideoPlayer.swf"
  221. cmd = [
  222. "rtmpdump",
  223. "-r", rtmp_url,
  224. "--playpath", rtmp_playpath,
  225. "--pageUrl", args.url,
  226. "--swfVfy", "http://www.itv.com/mercury/Mercury_VideoPlayer.swf"
  227. "?v=1.6.479/[[DYNAMIC]]/2"
  228. "--flashVer", "WIN 15,0,0,189",
  229. "--flv", file_path,
  230. "--resume" if args.resume else "",
  231. "--start={}".format(args.start),
  232. "--stop={}".format(args.end) if args.end > 0 else ""
  233. ]
  234. url2 = "http://%s/%s playpath=%s pageUrl=%s swfVfy=http://www.itv.com/mercury/Mercury_VideoPlayer.swf%s"%(LIVESTREAMER,rtmp_url,rtmp_playpath,args.url," live" if is_live else "")
  235. cmd2 = [r"c:\Program Files\VideoLAN\VLC\vlc.exe",url2]
  236. if is_live:
  237. cmd.append("--live")
  238. #print cmd2
  239. print url2
  240. print '\nrtmpdump -r "%s" --playpath "%s" --pageUrl "%s" --swfVfy "http://www.itv.com/mercury/Mercury_VideoPlayer.swf"%s -o output.flv\n'%(rtmp_url,rtmp_playpath,args.url," live" if is_live else "")
  241. proc = subprocess.Popen(cmd2)
  242. try:
  243. proc.communicate()
  244. except KeyboardInterrupt:
  245. pass
  246. # vim: tabstop=2 expandtab