Using FS mtime to reload non recursive cache.
[pyTivo.git] / plugin.py
blob17aa71dd41039ea8f5505ec7657bf5d84c6045a8
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 try:
16 module_name = '.'.join(['plugins', name, name])
17 module = __import__(module_name, globals(), locals(), name)
18 plugin = getattr(module, module.CLASS_NAME)()
19 return plugin
20 except ImportError:
21 print 'Error no', name, 'plugin exists. Check the type ' \
22 'setting for your share.'
23 return Error
25 class Plugin(object):
27 random_lock = threading.Lock()
29 CONTENT_TYPE = ''
31 def __new__(cls, *args, **kwds):
32 it = cls.__dict__.get('__it__')
33 if it is not None:
34 return it
35 cls.__it__ = it = object.__new__(cls)
36 it.init(*args, **kwds)
37 return it
39 def init(self):
40 pass
42 def send_file(self, handler, container, name):
43 o = urlparse("http://fake.host" + handler.path)
44 path = unquote(o[2])
45 handler.send_response(200)
46 handler.end_headers()
47 f = file(container['path'] + path[len(name) + 1:], 'rb')
48 shutil.copyfileobj(f, handler.wfile)
50 def get_local_base_path(self, handler, query):
52 subcname = query['Container'][0]
53 container = handler.server.containers[subcname.split('/')[0]]
55 return os.path.normpath(container['path'])
57 def get_local_path(self, handler, query):
59 subcname = query['Container'][0]
60 container = handler.server.containers[subcname.split('/')[0]]
62 path = os.path.normpath(container['path'])
63 for folder in subcname.split('/')[1:]:
64 if folder == '..':
65 return False
66 path = os.path.join(path, folder)
67 return path
69 def item_count(self, handler, query, cname, files, last_start=0):
70 """Return only the desired portion of the list, as specified by
71 ItemCount, AnchorItem and AnchorOffset. 'files' is either a
72 list of strings, OR a list of objects with a 'name' attribute.
73 """
74 totalFiles = len(files)
75 index = 0
77 if totalFiles and query.has_key('ItemCount'):
78 count = int(query['ItemCount'][0])
80 if query.has_key('AnchorItem'):
81 bs = '/TiVoConnect?Command=QueryContainer&Container='
82 local_base_path = self.get_local_base_path(handler, query)
84 anchor = query['AnchorItem'][0]
85 if anchor.startswith(bs):
86 anchor = anchor.replace(bs, '/', 1)
87 anchor = unquote(anchor)
88 anchor = anchor.replace(os.path.sep + cname, local_base_path, 1)
89 if not '://' in anchor:
90 anchor = os.path.normpath(anchor)
92 if type(files[0]) == str:
93 filenames = files
94 else:
95 filenames = [x.name for x in files]
96 try:
97 index = filenames.index(anchor, last_start)
98 except ValueError:
99 if last_start:
100 try:
101 index = filenames.index(anchor, 0, last_start)
102 except ValueError:
103 print 'Anchor not found:', anchor
104 else:
105 print 'Anchor not found:', anchor # just use index = 0
107 if count > 0:
108 index += 1
110 if query.has_key('AnchorOffset'):
111 index += int(query['AnchorOffset'][0])
113 #foward count
114 if count >= 0:
115 files = files[index:index + count]
116 #backwards count
117 else:
118 if index + count < 0:
119 count = -index
120 files = files[index + count:index]
121 index += count
123 else: # No AnchorItem
125 if count >= 0:
126 files = files[:count]
127 else:
128 index = count % len(files)
129 files = files[count:]
131 return files, totalFiles, index
133 def get_files(self, handler, query, filterFunction=None):
135 def build_recursive_list(path, recurse=True):
136 files = []
137 try:
138 for file in os.listdir(path):
139 if file.startswith('.'):
140 continue
141 file = os.path.join(path, file)
142 if recurse and os.path.isdir(file):
143 files.extend(build_recursive_list(file))
144 else:
145 if not filterFunction or filterFunction(file, file_type):
146 files.append(file)
147 except:
148 pass
149 return files
151 subcname = query['Container'][0]
152 cname = subcname.split('/')[0]
153 path = self.get_local_path(handler, query)
155 file_type = query.get('Filter', [''])[0]
157 recurse = query.get('Recurse',['No'])[0] == 'Yes'
158 files = build_recursive_list(path, recurse)
160 totalFiles = len(files)
162 def dir_sort(x, y):
163 xdir = os.path.isdir(os.path.join(path, x))
164 ydir = os.path.isdir(os.path.join(path, y))
166 if xdir == ydir:
167 return name_sort(x, y)
168 else:
169 return ydir - xdir
171 def name_sort(x, y):
172 return cmp(x, y)
174 if query.get('SortOrder',['Normal'])[0] == 'Random':
175 seed = query.get('RandomSeed', ['1'])[0]
176 self.random_lock.acquire()
177 random.seed(seed)
178 random.shuffle(files)
179 self.random_lock.release()
180 else:
181 files.sort(dir_sort)
183 # Trim the list
184 return self.item_count(handler, query, cname, files)