Better handling of ItemCount/AnchorItem
[pyTivo.git] / plugin.py
blob441f91249a09bb698becd3be5a1e06d24d225263
1 import os, shutil, re, random, threading
2 from urllib import unquote, unquote_plus
3 from urlparse import urlparse
5 def GetPlugin(name):
6 module_name = '.'.join(['plugins', name, name])
7 module = __import__(module_name, globals(), locals(), name)
8 plugin = getattr(module, module.CLASS_NAME)()
9 return plugin
11 class Plugin(object):
13 random_lock = threading.Lock()
15 CONTENT_TYPE = ''
17 def __new__(cls, *args, **kwds):
18 it = cls.__dict__.get('__it__')
19 if it is not None:
20 return it
21 cls.__it__ = it = object.__new__(cls)
22 it.init(*args, **kwds)
23 return it
25 def init(self):
26 pass
28 def send_file(self, handler, container, name):
29 o = urlparse("http://fake.host" + handler.path)
30 path = unquote_plus(o[2])
31 handler.send_response(200)
32 handler.end_headers()
33 f = file(container['path'] + path[len(name)+1:], 'rb')
34 shutil.copyfileobj(f, handler.wfile)
36 def get_local_base_path(self, handler, query):
38 subcname = query['Container'][0]
39 container = handler.server.containers[subcname.split('/')[0]]
41 return container['path']
43 def get_local_path(self, handler, query):
45 subcname = query['Container'][0]
46 container = handler.server.containers[subcname.split('/')[0]]
48 path = container['path']
49 for folder in subcname.split('/')[1:]:
50 if folder == '..':
51 return False
52 path = os.path.join(path, folder)
53 return path
55 def item_count(self, handler, query, cname, files):
56 """Return only the desired portion of the list, as specified by
57 ItemCount, AnchorItem and AnchorOffset
58 """
59 totalFiles = len(files)
60 index = 0
62 if query.has_key('ItemCount'):
63 count = int(query['ItemCount'][0])
65 if query.has_key('AnchorItem'):
66 bs = '/TiVoConnect?Command=QueryContainer&Container='
67 local_base_path = self.get_local_base_path(handler, query)
69 anchor = query['AnchorItem'][0]
70 if anchor.startswith(bs):
71 anchor = anchor.replace(bs, '/')
72 anchor = unquote(anchor)
73 anchor = anchor.replace(os.path.sep + cname, local_base_path)
74 anchor = os.path.normpath(anchor)
76 try:
77 index = files.index(anchor)
78 except ValueError:
79 print 'Anchor not found:', anchor # just use index = 0
81 if count > 0:
82 index += 1
84 if query.has_key('AnchorOffset'):
85 index += int(query['AnchorOffset'][0])
87 #foward count
88 if count > 0:
89 files = files[index:index + count]
90 #backwards count
91 elif count < 0:
92 if index + count < 0:
93 count = -index
94 files = files[index + count:index]
95 index += count
97 else: # No AnchorItem
99 if count >= 0:
100 files = files[:count]
101 else:
102 index = count % len(files)
103 files = files[count:]
105 return files, totalFiles, index
107 def get_files(self, handler, query, filterFunction=None):
108 subcname = query['Container'][0]
109 cname = subcname.split('/')[0]
110 path = self.get_local_path(handler, query)
112 files = [ os.path.join(path, file) for file in os.listdir(path)]
113 if query.get('Recurse',['No'])[0] == 'Yes':
114 for file in files:
115 if os.path.isdir(file):
116 for new_file in os.listdir(file):
117 files.append( os.path.join(file, new_file) )
119 file_type = query.get('Filter', [''])[0]
120 if filterFunction:
121 files = [file for file in files if filterFunction(file, file_type)]
123 totalFiles = len(files)
125 def dir_sort(x, y):
126 xdir = os.path.isdir(os.path.join(path, x))
127 ydir = os.path.isdir(os.path.join(path, y))
129 if xdir == ydir:
130 return name_sort(x, y)
131 else:
132 return ydir - xdir
134 def name_sort(x, y):
135 numbername = re.compile(r'(\d*)(.*)')
136 m = numbername.match(x)
137 xNumber = m.group(1)
138 xStr = m.group(2)
139 m = numbername.match(y)
140 yNumber = m.group(1)
141 yStr = m.group(2)
143 if xNumber and yNumber:
144 xNumber, yNumber = int(xNumber), int(yNumber)
145 if xNumber == yNumber:
146 return cmp(xStr, yStr)
147 else:
148 return cmp(xNumber, yNumber)
149 elif xNumber:
150 return -1
151 elif yNumber:
152 return 1
153 else:
154 return cmp(xStr, yStr)
156 if query.get('SortOrder',['Normal'])[0] == 'Random':
157 seed = query.get('RandomSeed', ['1'])[0]
158 self.random_lock.acquire()
159 random.seed(seed)
160 random.shuffle(files)
161 self.random_lock.release()
162 else:
163 files.sort(dir_sort)
165 # Trim the list
166 return self.item_count(handler, query, cname, files)