Merge branch 'beta-wgw'
[pyTivo.git] / plugin.py
blobbbca96ccaf1ad769a356f4259a2af1a5ecbabb83
1 import os, shutil, re, 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 def GetPlugin(name):
12 module_name = '.'.join(['plugins', name, name])
13 module = __import__(module_name, globals(), locals(), name)
14 plugin = getattr(module, module.CLASS_NAME)()
15 return plugin
17 class Plugin(object):
19 random_lock = threading.Lock()
21 CONTENT_TYPE = ''
23 def __new__(cls, *args, **kwds):
24 it = cls.__dict__.get('__it__')
25 if it is not None:
26 return it
27 cls.__it__ = it = object.__new__(cls)
28 it.init(*args, **kwds)
29 return it
31 def init(self):
32 pass
34 def send_file(self, handler, container, name):
35 o = urlparse("http://fake.host" + handler.path)
36 path = unquote(o[2])
37 handler.send_response(200)
38 handler.end_headers()
39 f = file(container['path'] + path[len(name)+1:], 'rb')
40 shutil.copyfileobj(f, handler.wfile)
42 def get_local_base_path(self, handler, query):
44 subcname = query['Container'][0]
45 container = handler.server.containers[subcname.split('/')[0]]
47 return container['path']
49 def get_local_path(self, handler, query):
51 subcname = query['Container'][0]
52 container = handler.server.containers[subcname.split('/')[0]]
54 path = container['path']
55 for folder in subcname.split('/')[1:]:
56 if folder == '..':
57 return False
58 path = os.path.join(path, folder)
59 return path
61 def item_count(self, handler, query, cname, files):
62 """Return only the desired portion of the list, as specified by
63 ItemCount, AnchorItem and AnchorOffset. 'files' is either a
64 list of strings, OR a list of objects with a 'name' attribute.
65 """
66 totalFiles = len(files)
67 index = 0
69 if query.has_key('ItemCount'):
70 count = int(query['ItemCount'][0])
72 if query.has_key('AnchorItem'):
73 bs = '/TiVoConnect?Command=QueryContainer&Container='
74 local_base_path = self.get_local_base_path(handler, query)
76 anchor = query['AnchorItem'][0]
77 if anchor.startswith(bs):
78 anchor = anchor.replace(bs, '/')
79 anchor = unquote(anchor)
80 anchor = anchor.replace(os.path.sep + cname, local_base_path)
81 anchor = os.path.normpath(anchor)
83 if type(files[0]) == str:
84 filenames = files
85 else:
86 filenames = [x.name for x in files]
87 try:
88 index = filenames.index(anchor)
89 except ValueError:
90 print 'Anchor not found:', anchor # just use index = 0
92 if count > 0:
93 index += 1
95 if query.has_key('AnchorOffset'):
96 index += int(query['AnchorOffset'][0])
98 #foward count
99 if count > 0:
100 files = files[index:index + count]
101 #backwards count
102 elif count < 0:
103 if index + count < 0:
104 count = -index
105 files = files[index + count:index]
106 index += count
108 else: # No AnchorItem
110 if count >= 0:
111 files = files[:count]
112 else:
113 index = count % len(files)
114 files = files[count:]
116 return files, totalFiles, index
118 def get_files(self, handler, query, filterFunction=None):
120 def build_recursive_list(path, recurse=True):
121 files = []
122 for file in os.listdir(path):
123 file = os.path.join(path, file)
124 if recurse and os.path.isdir(file):
125 files.extend(build_recursive_list(file))
126 else:
127 if not filterFunction or filterFunction(file, file_type):
128 files.append(file)
129 return files
131 subcname = query['Container'][0]
132 cname = subcname.split('/')[0]
133 path = self.get_local_path(handler, query)
135 file_type = query.get('Filter', [''])[0]
137 recurse = query.get('Recurse',['No'])[0] == 'Yes'
138 files = build_recursive_list(path, recurse)
140 totalFiles = len(files)
142 def dir_sort(x, y):
143 xdir = os.path.isdir(os.path.join(path, x))
144 ydir = os.path.isdir(os.path.join(path, y))
146 if xdir == ydir:
147 return name_sort(x, y)
148 else:
149 return ydir - xdir
151 def name_sort(x, y):
152 numbername = re.compile(r'(\d*)(.*)')
153 m = numbername.match(x)
154 xNumber = m.group(1)
155 xStr = m.group(2)
156 m = numbername.match(y)
157 yNumber = m.group(1)
158 yStr = m.group(2)
160 if xNumber and yNumber:
161 xNumber, yNumber = int(xNumber), int(yNumber)
162 if xNumber == yNumber:
163 return cmp(xStr, yStr)
164 else:
165 return cmp(xNumber, yNumber)
166 elif xNumber:
167 return -1
168 elif yNumber:
169 return 1
170 else:
171 return cmp(xStr, yStr)
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)