Making tivo push optional.
[pyTivo.git] / plugin.py
blob77157d22addae711c647040d344808b6ad5ef823
1 import os, shutil, random, threading, urllib
2 from urlparse import urlparse
4 if os.path.sep == '/':
5 quote = urllib.quote
6 unquote = urllib.unquote_plus
7 else:
8 quote = lambda x: urllib.quote(x.replace(os.path.sep, '/'))
9 unquote = lambda x: urllib.unquote_plus(x).replace('/', os.path.sep)
11 class Error:
12 CONTENT_TYPE = 'text/html'
14 def GetPlugin(name):
15 module_name = '.'.join(['plugins', name, name])
16 module = __import__(module_name, globals(), locals(), name)
17 plugin = getattr(module, module.CLASS_NAME)()
18 return plugin
19 #except ImportError:
20 # print 'Error no', name, 'plugin exists. Check the type ' \
21 # 'setting for your share.'
22 # return Error
24 class Plugin(object):
26 random_lock = threading.Lock()
28 CONTENT_TYPE = ''
30 def __new__(cls, *args, **kwds):
31 it = cls.__dict__.get('__it__')
32 if it is not None:
33 return it
34 cls.__it__ = it = object.__new__(cls)
35 it.init(*args, **kwds)
36 return it
38 def init(self):
39 pass
41 def send_file(self, handler, container, name):
42 o = urlparse("http://fake.host" + handler.path)
43 path = unquote(o[2])
44 handler.send_response(200)
45 handler.end_headers()
46 f = file(container['path'] + path[len(name) + 1:], 'rb')
47 shutil.copyfileobj(f, handler.wfile)
49 def get_local_base_path(self, handler, query):
51 subcname = query['Container'][0]
52 container = handler.server.containers[subcname.split('/')[0]]
54 return os.path.normpath(container['path'])
56 def get_local_path(self, handler, query):
58 subcname = query['Container'][0]
59 container = handler.server.containers[subcname.split('/')[0]]
61 path = os.path.normpath(container['path'])
62 for folder in subcname.split('/')[1:]:
63 if folder == '..':
64 return False
65 path = os.path.join(path, folder)
66 return path
68 def item_count(self, handler, query, cname, files, last_start=0):
69 """Return only the desired portion of the list, as specified by
70 ItemCount, AnchorItem and AnchorOffset. 'files' is either a
71 list of strings, OR a list of objects with a 'name' attribute.
72 """
73 totalFiles = len(files)
74 index = 0
76 if totalFiles and query.has_key('ItemCount'):
77 count = int(query['ItemCount'][0])
79 if query.has_key('AnchorItem'):
80 bs = '/TiVoConnect?Command=QueryContainer&Container='
81 local_base_path = self.get_local_base_path(handler, query)
83 anchor = query['AnchorItem'][0]
84 if anchor.startswith(bs):
85 anchor = anchor.replace(bs, '/', 1)
86 anchor = unquote(anchor)
87 anchor = anchor.replace(os.path.sep + cname, local_base_path, 1)
88 if not '://' in anchor:
89 anchor = os.path.normpath(anchor)
91 if type(files[0]) == str:
92 filenames = files
93 else:
94 filenames = [x.name for x in files]
95 try:
96 index = filenames.index(anchor, last_start)
97 except ValueError:
98 if last_start:
99 try:
100 index = filenames.index(anchor, 0, last_start)
101 except ValueError:
102 print 'Anchor not found:', anchor
103 else:
104 print 'Anchor not found:', anchor # just use index = 0
106 if count > 0:
107 index += 1
109 if query.has_key('AnchorOffset'):
110 index += int(query['AnchorOffset'][0])
112 #foward count
113 if count >= 0:
114 files = files[index:index + count]
115 #backwards count
116 else:
117 if index + count < 0:
118 count = -index
119 files = files[index + count:index]
120 index += count
122 else: # No AnchorItem
124 if count >= 0:
125 files = files[:count]
126 else:
127 index = count % len(files)
128 files = files[count:]
130 return files, totalFiles, index
132 def get_files(self, handler, query, filterFunction=None):
134 def build_recursive_list(path, recurse=True):
135 files = []
136 try:
137 for file in os.listdir(path):
138 if file.startswith('.'):
139 continue
140 file = os.path.join(path, file)
141 if recurse and os.path.isdir(file):
142 files.extend(build_recursive_list(file))
143 else:
144 if not filterFunction or filterFunction(file, file_type):
145 files.append(file)
146 except:
147 pass
148 return files
150 subcname = query['Container'][0]
151 cname = subcname.split('/')[0]
152 path = self.get_local_path(handler, query)
154 file_type = query.get('Filter', [''])[0]
156 recurse = query.get('Recurse',['No'])[0] == 'Yes'
157 files = build_recursive_list(path, recurse)
159 totalFiles = len(files)
161 def dir_sort(x, y):
162 xdir = os.path.isdir(os.path.join(path, x))
163 ydir = os.path.isdir(os.path.join(path, y))
165 if xdir == ydir:
166 return name_sort(x, y)
167 else:
168 return ydir - xdir
170 def name_sort(x, y):
171 return cmp(x, y)
173 if query.get('SortOrder',['Normal'])[0] == 'Random':
174 seed = query.get('RandomSeed', ['1'])[0]
175 self.random_lock.acquire()
176 random.seed(seed)
177 random.shuffle(files)
178 self.random_lock.release()
179 else:
180 files.sort(dir_sort)
182 # Trim the list
183 return self.item_count(handler, query, cname, files)