| 1 |
from plugins.video.video import Video, VideoDetails |
|---|
| 2 |
import mind |
|---|
| 3 |
import config |
|---|
| 4 |
|
|---|
| 5 |
import xmpp |
|---|
| 6 |
|
|---|
| 7 |
import threading |
|---|
| 8 |
import urllib2 |
|---|
| 9 |
import os.path |
|---|
| 10 |
import shutil |
|---|
| 11 |
import os.path |
|---|
| 12 |
import time |
|---|
| 13 |
import os |
|---|
| 14 |
import urlparse |
|---|
| 15 |
import urllib |
|---|
| 16 |
import xml.etree.ElementTree as ElementTree |
|---|
| 17 |
import Queue |
|---|
| 18 |
import logging |
|---|
| 19 |
|
|---|
| 20 |
CLASS_NAME = 'WebVideo' |
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
class WebVideo(Video): |
|---|
| 24 |
|
|---|
| 25 |
CONTENT_TYPE = 'x-not-for/tivo' |
|---|
| 26 |
|
|---|
| 27 |
def init(self): |
|---|
| 28 |
self.__logger = logging.getLogger('pyTivo.webvideo') |
|---|
| 29 |
self.work_queue = Queue.Queue() |
|---|
| 30 |
self.download_thread_num = 1 |
|---|
| 31 |
self.in_progress = {} |
|---|
| 32 |
self.in_progress_lock = threading.Lock() |
|---|
| 33 |
|
|---|
| 34 |
self.startXMPP() |
|---|
| 35 |
self.startWorkerThreads() |
|---|
| 36 |
|
|---|
| 37 |
def startXMPP(self): |
|---|
| 38 |
m = mind.getMind() |
|---|
| 39 |
xmpp_info = m.getXMPPLoginInfo() |
|---|
| 40 |
|
|---|
| 41 |
jid=xmpp.protocol.JID(xmpp_info['username'] + '/pyTivo') |
|---|
| 42 |
cl=xmpp.Client( |
|---|
| 43 |
server=xmpp_info['server'], |
|---|
| 44 |
port=xmpp_info['port'], |
|---|
| 45 |
debug=[], |
|---|
| 46 |
) |
|---|
| 47 |
self.__logger.debug('Connecting to %s:%s' % (xmpp_info['server'], xmpp_info['port'])) |
|---|
| 48 |
cl.connect() |
|---|
| 49 |
cl.RegisterHandler('message', self.processMessage) |
|---|
| 50 |
self.__logger.debug('Loging in as %s/pyTivo' % xmpp_info['username']) |
|---|
| 51 |
cl.auth(user=jid.getNode(), password=config.getTivoPassword(), resource='pyTivo') |
|---|
| 52 |
|
|---|
| 53 |
cl.sendInitPresence(requestRoster=0) |
|---|
| 54 |
|
|---|
| 55 |
for user_name in xmpp_info['presence_list']: |
|---|
| 56 |
self.__logger.debug('Sending presence to %s' % user_name) |
|---|
| 57 |
jid=xmpp.protocol.JID(user_name) |
|---|
| 58 |
cl.sendPresence(jid) |
|---|
| 59 |
|
|---|
| 60 |
t = threading.Thread(target=self.processXMPP, args=(cl,)) |
|---|
| 61 |
t.setDaemon(True) |
|---|
| 62 |
t.start() |
|---|
| 63 |
|
|---|
| 64 |
def startWorkerThreads(self): |
|---|
| 65 |
for i in range(self.download_thread_num): |
|---|
| 66 |
t = threading.Thread(target=self.processDlRequest, name='webvideo downloader') |
|---|
| 67 |
t.setDaemon(True) |
|---|
| 68 |
t.start() |
|---|
| 69 |
|
|---|
| 70 |
t = threading.Thread(target=self.watchQueue, name='webvideo queue watcher') |
|---|
| 71 |
t.setDaemon(True) |
|---|
| 72 |
t.start() |
|---|
| 73 |
|
|---|
| 74 |
def processXMPP(self, client): |
|---|
| 75 |
while client.Process(3): |
|---|
| 76 |
pass |
|---|
| 77 |
|
|---|
| 78 |
def processMessage(self, sess, mess): |
|---|
| 79 |
self.__logger.debug('Got message\n %s' % mess.getBody()) |
|---|
| 80 |
xmpp_action = ElementTree.fromstring(mess.getBody()) |
|---|
| 81 |
|
|---|
| 82 |
method_name = 'xmpp_' + xmpp_action.findtext('action').lower() |
|---|
| 83 |
if not hasattr(self, method_name): |
|---|
| 84 |
return False |
|---|
| 85 |
|
|---|
| 86 |
method = getattr(self, method_name) |
|---|
| 87 |
method(xmpp_action) |
|---|
| 88 |
|
|---|
| 89 |
def watchQueue(self): |
|---|
| 90 |
while True: |
|---|
| 91 |
self.xmpp_cdsupdate() |
|---|
| 92 |
time.sleep(60*15) |
|---|
| 93 |
|
|---|
| 94 |
def xmpp_cdsupdate(self, xml=None): |
|---|
| 95 |
m = mind.getMind() |
|---|
| 96 |
|
|---|
| 97 |
self.in_progress_lock.acquire() |
|---|
| 98 |
try: |
|---|
| 99 |
for request in m.getDownloadRequests(): |
|---|
| 100 |
if not request['bodyOfferId'] in self.in_progress: |
|---|
| 101 |
self.__logger.debug('Adding request to queue, %s' % request) |
|---|
| 102 |
self.in_progress[request['bodyOfferId']] = True |
|---|
| 103 |
self.work_queue.put(request) |
|---|
| 104 |
finally: |
|---|
| 105 |
self.in_progress_lock.release() |
|---|
| 106 |
|
|---|
| 107 |
def processDlRequest(self): |
|---|
| 108 |
|
|---|
| 109 |
while True: |
|---|
| 110 |
data = self.work_queue.get() |
|---|
| 111 |
|
|---|
| 112 |
for share_name, settings in config.getShares(): |
|---|
| 113 |
if settings['type'] == 'webvideo': |
|---|
| 114 |
break |
|---|
| 115 |
self.__logger.debug('Processing request: %s' % data) |
|---|
| 116 |
|
|---|
| 117 |
path = settings['path'] |
|---|
| 118 |
file_name = os.path.join(path, '%s-%s' % (data['bodyOfferId'].replace(':', '-'),data['url'].split('/')[-1])) |
|---|
| 119 |
|
|---|
| 120 |
self.downloadFile(data['url'], file_name) |
|---|
| 121 |
|
|---|
| 122 |
tsn = data['bodyId'] |
|---|
| 123 |
file_info = VideoDetails() |
|---|
| 124 |
file_info.update(self.metadata_full(file_name, tsn)) |
|---|
| 125 |
|
|---|
| 126 |
import socket |
|---|
| 127 |
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
|---|
| 128 |
s.connect(('tivo.com',123)) |
|---|
| 129 |
ip = s.getsockname()[0] |
|---|
| 130 |
port = config.getPort() |
|---|
| 131 |
|
|---|
| 132 |
data['url'] = 'http://%s:%s' % (ip, port) + urllib.quote('/%s/%s' % (share_name, os.path.split(file_name)[-1])) |
|---|
| 133 |
data['duration'] = file_info['duration'] / 1000 |
|---|
| 134 |
data['size'] = file_info['size'] |
|---|
| 135 |
|
|---|
| 136 |
self.__logger.debug('Complete request: %s' % data) |
|---|
| 137 |
|
|---|
| 138 |
m = mind.getMind() |
|---|
| 139 |
m.completeDownloadRequest(data) |
|---|
| 140 |
|
|---|
| 141 |
self.in_progress_lock.acquire() |
|---|
| 142 |
try: |
|---|
| 143 |
del self.in_progress[data['bodyOfferId']] |
|---|
| 144 |
finally: |
|---|
| 145 |
self.in_progress_lock.release() |
|---|
| 146 |
|
|---|
| 147 |
def downloadFile(self, url, file_path): |
|---|
| 148 |
self.__logger.info('Downloading %s to %s' % (url, file_path)) |
|---|
| 149 |
|
|---|
| 150 |
outfile = open(file_path, 'awb') |
|---|
| 151 |
size = os.path.getsize(file_path) |
|---|
| 152 |
r = urllib2.Request(url) |
|---|
| 153 |
if size: |
|---|
| 154 |
r.add_header('Range', 'bytes=%s-' % size) |
|---|
| 155 |
|
|---|
| 156 |
try: |
|---|
| 157 |
infile = urllib2.urlopen(r) |
|---|
| 158 |
except urllib2.HTTPError, e: |
|---|
| 159 |
if not e.code == 416: |
|---|
| 160 |
raise |
|---|
| 161 |
infile = urllib2.urlopen(url) |
|---|
| 162 |
if int(infile.info()['Content-Length']) == size: |
|---|
| 163 |
self.__logger.debug('File was alraedy done. %s' % url) |
|---|
| 164 |
return |
|---|
| 165 |
else: |
|---|
| 166 |
self.__logger.debug('File was not done but could not resume. %s' % url) |
|---|
| 167 |
outfile.close() |
|---|
| 168 |
outfile = open(file_path, 'wb') |
|---|
| 169 |
|
|---|
| 170 |
shutil.copyfileobj(infile, outfile, 8192) |
|---|
| 171 |
|
|---|
| 172 |
self.__logger.info('Done downloading %s to %s' % (url, file_path)) |
|---|
| 173 |
|
|---|
| 174 |
def send_file(self, handler, container, name): |
|---|
| 175 |
Video.send_file(self, handler, container, name) |
|---|
| 176 |
|
|---|
| 177 |
o = urlparse.urlparse("http://fake.host" + handler.path) |
|---|
| 178 |
path = urllib.unquote(o[2]) |
|---|
| 179 |
file_path = container['path'] + path[len(name) + 1:] |
|---|
| 180 |
if os.path.exists(file_path): |
|---|
| 181 |
self.__logger.info('Deleting file %s' % file_path) |
|---|
| 182 |
os.unlink(file_path) |
|---|
| 183 |
|
|---|