fix crash upon invalid mountpoint
[enigma2.git] / lib / python / Components / Scanner.py
1 from Plugins.Plugin import PluginDescriptor
2 from Components.PluginComponent import plugins
3
4 from os import path as os_path, walk as os_walk
5 from string import lower
6 from mimetypes import guess_type
7
8 def getExtension(file):
9         p = file.rfind('.')
10         if p == -1:
11                 ext = ""
12         else:   
13                 ext = file[p+1:]
14
15         return lower(ext)
16
17 def getType(file):
18         (type, _) = guess_type(file)
19         if type is None:
20                 # Detect some mimetypes unknown to dm7025
21                 # TODO: do mimetypes.add_type once should be better
22                 ext = getExtension(file)
23                 if ext == "ipk":
24                         return "application/x-debian-package"
25                 elif ext == "ogg":
26                         return "application/ogg"
27                 elif ext == "flac":
28                         return "audio/x-flac"
29                 elif ext == "dmpkg":
30                         return "application/x-dream-package"
31                 elif ext == "nfi":
32                         return "application/x-dream-image"
33                 elif ext == "ts":
34                         return "video/MP2T"
35                 elif ext == "iso":
36                         return "video/x-dvd-iso"
37                 elif file[-12:].lower() == "video_ts.ifo":
38                         return "video/x-dvd"
39                 elif ext == "dat" and file[-11:-6].lower() == "avseq":
40                         return "video/x-vcd"
41         return type
42
43 class Scanner:
44         def __init__(self, name, mimetypes= [], paths_to_scan = [], description = "", openfnc = None):
45                 self.mimetypes = mimetypes
46                 self.name = name
47                 self.paths_to_scan = paths_to_scan
48                 self.description = description
49                 self.openfnc = openfnc
50
51         def checkFile(self, file):
52                 return True
53
54         def handleFile(self, res, file):
55                 if (self.mimetypes is None or file.mimetype in self.mimetypes) and self.checkFile(file):
56                         res.setdefault(self, []).append(file)
57
58         def __repr__(self):
59                 return "<Scanner " + self.name + ">"
60
61         def open(self, list, *args, **kwargs):
62                 if self.openfnc is not None:
63                         self.openfnc(list, *args, **kwargs)
64
65 class ScanPath:
66         def __init__(self, path, with_subdirs = False):
67                 self.path = path
68                 self.with_subdirs = with_subdirs
69
70         def __repr__(self):
71                 return self.path + "(" + str(self.with_subdirs) + ")"
72
73         # we will use this in a set(), so we need to implement __hash__ and __cmp__
74         def __hash__(self):
75                 return self.path.__hash__() ^ self.with_subdirs.__hash__()
76
77         def __cmp__(self, other):
78                 if self.path < other.path:
79                         return -1
80                 elif self.path > other.path:
81                         return +1
82                 else:
83                         return self.with_subdirs.__cmp__(other.with_subdirs)
84
85 class ScanFile:
86         def __init__(self, path, mimetype = None, size = None, autodetect = True):
87                 self.path = path
88                 if mimetype is None and autodetect:
89                         self.mimetype = getType(path)
90                 else:
91                         self.mimetype = mimetype
92                 self.size = size
93
94         def __repr__(self):
95                 return "<ScanFile " + self.path + " (" + str(self.mimetype) + ", " + str(self.size) + " MB)>"
96
97 def execute(option):
98         print "execute", option
99         if option is None:
100                 return
101
102         (_, scanner, files, session) = option
103         scanner.open(files, session)
104
105 def scanDevice(mountpoint):
106         scanner = [ ]
107
108         for p in plugins.getPlugins(PluginDescriptor.WHERE_FILESCAN):
109                 l = p()
110                 if not isinstance(l, list):
111                         l = [l]
112                 scanner += l
113
114         print "scanner:", scanner
115
116         res = { }
117
118         # merge all to-be-scanned paths, with priority to 
119         # with_subdirs.
120
121         paths_to_scan = set()
122
123         # first merge them all...
124         for s in scanner:
125                 paths_to_scan.update(set(s.paths_to_scan))
126
127         # ...then remove with_subdir=False when same path exists
128         # with with_subdirs=True
129         for p in set(paths_to_scan):
130                 if p.with_subdirs == True and ScanPath(path=p.path) in paths_to_scan:
131                         paths_to_scan.remove(ScanPath(path=p.path))
132
133         # convert to list
134         paths_to_scan = list(paths_to_scan)
135         
136         from Components.Harddisk import harddiskmanager 
137         blockdev = mountpoint.rsplit('/',1)[-1]
138         error, blacklisted, removable, is_cdrom, partitions = harddiskmanager.getBlockDevInfo(blockdev)
139
140         # now scan the paths
141         for p in paths_to_scan:
142                 path = os_path.join(mountpoint, p.path)
143
144                 for root, dirs, files in os_walk(path):
145                         for f in files:
146                                 path = os_path.join(root, f)
147                                 if is_cdrom and path.endswith(".wav") and path[-13:-6] == ("/track-"):
148                                         sfile = ScanFile(path,"audio/x-cda")
149                                 else:
150                                         sfile = ScanFile(path)
151                                 for s in scanner:
152                                         s.handleFile(res, sfile)
153
154                         # if we really don't want to scan subdirs, stop here.
155                         if not p.with_subdirs:
156                                 del dirs[:]
157
158         # res is a dict with scanner -> [ScanFiles]
159         return res
160
161 def openList(session, files):
162         if not isinstance(files, list):
163                 files = [ files ]
164
165         scanner = [ ]
166
167         for p in plugins.getPlugins(PluginDescriptor.WHERE_FILESCAN):
168                 l = p()
169                 if not isinstance(l, list):
170                         l = [l]
171                 scanner += l
172
173         print "scanner:", scanner
174
175         res = { }
176
177         for file in files:
178                 for s in scanner:
179                         s.handleFile(res, file)
180
181         choices = [ (r.description, r, res[r], session) for r in res ]
182         Len = len(choices)
183         if Len > 1:
184                 from Screens.ChoiceBox import ChoiceBox
185
186                 session.openWithCallback(
187                         execute,
188                         ChoiceBox,
189                         title = "The following viewers were found...",
190                         list = choices
191                 )
192                 return True
193         elif Len:
194                 execute(choices[0])
195                 return True
196
197         return False
198
199 def openFile(session, mimetype, file):
200         return openList(session, [ScanFile(file, mimetype)])