Merge lp:~linkinpark342/exaile/fullurl into lp:exaile/0.3.3

Proposed by Abhishek Mukherjee
Status: Merged
Merged at revision: not available
Proposed branch: lp:~linkinpark342/exaile/fullurl
Merge into: lp:exaile/0.3.3
Diff against target: None lines
To merge this branch: bzr merge lp:~linkinpark342/exaile/fullurl
Reviewer Review Type Date Requested Status
Adam Olsen Approve
Review via email: mp+5683@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Abhishek Mukherjee (linkinpark342) wrote :

Forces exaile to only keep full absolute URI's internally.

Because of the unicode issue, tracks store a "file:///path" in unicode with no escaped characters, rather than a properly urlencoded one. This is so that we can look up the unicode os path again later. The player has to deal with any issues that come out of that when passing to gstreamer. All paths also become absolute rather than relative since absolute uri's are by definition absolute.

This branch will cause all users to have to delete their database and, more likely, nuke their ~/.local/share/exaile folder. This was discussed as acceptable on IRC since we are still in Alpha

Revision history for this message
Abhishek Mukherjee (linkinpark342) wrote :

Set the status to work in progress. I found a bug with unicode filenames. I'm going to hunt it down after my exams end.

Revision history for this message
Abhishek Mukherjee (linkinpark342) wrote :

Bug fixed, just a ~8 character change.

Revision history for this message
Adam Olsen (arolsen) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'xl/collection.py'
2--- xl/collection.py 2009-03-18 21:59:28 +0000
3+++ xl/collection.py 2009-04-13 21:34:43 +0000
4@@ -28,6 +28,7 @@
5
6 import os, time, os.path, shutil, logging
7 import gobject
8+import urllib
9
10 logger = logging.getLogger(__name__)
11
12@@ -504,23 +505,24 @@
13 ccheck = {} # compilations dict
14
15 count = 0
16- for folder in os.walk(self.location):
17- basepath = folder[0]
18- for filename in folder[2]:
19+ for basepath, dirnames, filenames in os.walk(self.location):
20+ for filename in filenames:
21 if self.collection:
22 if self.collection._scan_stopped:
23 self.scanning = False
24 return False
25 count += 1
26- fullpath = os.path.join(basepath, filename)
27+ path = os.path.abspath(os.path.join(basepath, filename))
28+ fullpath = "file://" + path
29
30 try:
31 trmtime = db.get_track_attr(fullpath, "modified")
32- mtime = os.path.getmtime(fullpath)
33+ except:
34+ pass
35+ else:
36+ mtime = os.path.getmtime(path)
37 if mtime == trmtime:
38 continue
39- except:
40- pass
41
42 tr = db.get_track_by_loc(fullpath)
43 if tr:
44@@ -553,6 +555,7 @@
45 if not os.path.exists(f):
46 removals.append(db.get_track_by_loc(f))
47 for tr in removals:
48+ logging.info(u"Removing " + unicode(tr))
49 db.remove(tr)
50
51 self.scanning = False
52@@ -625,8 +628,9 @@
53 tr = self.collection.get_track_by_loc(loc)
54 if tr:
55 self.collection.remove(tr)
56+ path = common.local_file_from_url(tr.get_loc_for_io())
57 try:
58- os.unlink(tr.get_loc_for_io())
59+ os.unlink(path)
60 except OSError: # file not found?
61 pass
62 except:
63
64=== modified file 'xl/common.py'
65--- xl/common.py 2008-12-28 17:58:55 +0000
66+++ xl/common.py 2009-04-13 20:21:28 +0000
67@@ -14,7 +14,7 @@
68 # along with this program; if not, write to the Free Software
69 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
70
71-import locale, os, time, threading, urllib, re, random, string
72+import locale, os, time, threading, urllib, re, random, string, urlparse
73 import traceback
74 import logging
75
76@@ -155,17 +155,12 @@
77 text = text.replace(old, new)
78 return text
79
80-def to_url(path):
81- """
82- Converts filesystem path to URL. Returns the input unchanged if it's not
83- an FS path (i.e. a URL or something invalid).
84- """
85- if re.search(r'^[\w]+://', path):
86- return path
87- try:
88- return 'file://' + urllib.pathname2url(path)
89- except IOError:
90- return path
91+def local_file_from_url(url):
92+ """
93+ Returns a local file path based on a url
94+ """
95+ split = urlparse.urlsplit(url)
96+ return urlparse.urlunsplit(('', '') + split[2:])
97
98 class idict(dict):
99 """
100
101=== modified file 'xl/cover.py'
102--- xl/cover.py 2009-03-24 23:16:10 +0000
103+++ xl/cover.py 2009-04-13 21:42:40 +0000
104@@ -456,7 +456,7 @@
105 def find_covers(self, track, limit=-1):
106 covers = []
107 try:
108- search_dir = os.path.dirname(track.get_loc())
109+ search_dir = os.path.dirname(track.local_file_name())
110 except AttributeError:
111 raise NoCoverFoundException()
112
113
114=== modified file 'xl/metadata/__init__.py'
115--- xl/metadata/__init__.py 2009-03-15 04:05:49 +0000
116+++ xl/metadata/__init__.py 2009-04-13 18:38:41 +0000
117@@ -16,6 +16,7 @@
118
119 # do this so formats can inherit from stuff in _base
120 from _base import *
121+import urlparse
122
123 import asf, flac, mod, mp3, mp4, mpc, ogg, sid, speex, tta, wav, wv
124
125
126=== modified file 'xl/metadata/_base.py'
127--- xl/metadata/_base.py 2009-01-13 20:22:25 +0000
128+++ xl/metadata/_base.py 2009-04-18 06:01:50 +0000
129@@ -14,6 +14,9 @@
130
131 import os
132 from xl import common
133+import urlparse
134+import urllib
135+import urllib2
136
137 import logging
138 logger = logging.getLogger(__name__)
139@@ -42,14 +45,22 @@
140 """
141 Loads the tags from the file.
142 """
143- if self.MutagenType:
144- try:
145- self.mutagen = self.MutagenType(self.loc)
146- except:
147- logger.error("Couldn't read tags from possibly corrupt " \
148- "file %s" % self.loc)
149- #common.log_exception(logger)
150- raise NotReadable
151+ try:
152+ self.url = urllib2.urlopen(self.loc)
153+ except urllib2.URLError, urllib2.HTTPError:
154+ logger.error("Couldn't open url to file %s" % self.loc)
155+ raise NotReadable
156+ loc = urlparse.urlsplit(self.loc)
157+ if loc[0] == "file":
158+ if self.MutagenType:
159+ file_loc = common.local_file_from_url(self.loc)
160+ try:
161+ self.mutagen = self.MutagenType(file_loc)
162+ except:
163+ logger.error("Couldn't read tags from possibly corrupt " \
164+ "file %s" % file_loc)
165+ #common.log_exception(logger)
166+ raise NotReadable
167
168 def save(self):
169 """
170@@ -62,7 +73,8 @@
171 if self.MutagenType:
172 return self.mutagen
173 else:
174- return {'title':os.path.split(self.loc)[-1]}
175+ path = common.local_file_from_url(self.loc)
176+ return {'title':os.path.split(path)[-1]}
177
178 def _get_tag(self, raw, tag):
179 try:
180
181=== modified file 'xl/player.py'
182--- xl/player.py 2009-04-14 18:39:32 +0000
183+++ xl/player.py 2009-04-18 06:01:50 +0000
184@@ -22,7 +22,7 @@
185
186 from xl import common, event, playlist, settings
187 import random, time, os, logging, urllib
188-from urlparse import urlparse
189+import urlparse
190
191
192 try:
193@@ -446,10 +446,13 @@
194
195 def _get_track_uri(self, track):
196 uri = track.get_loc_for_io()
197- parsed = urlparse(uri)
198- if parsed[0] == "":
199- uri = "file://" + urllib.pathname2url(uri)
200- uri = uri.encode(common.get_default_encoding())
201+ split = urlparse.urlsplit(uri)
202+ assert split[0] != "", _("Exaile now uses absolute URI's, please "
203+ "delete/rename your %s directory") \
204+ % xdg.data_home
205+ path = common.local_file_from_url(uri)
206+ path = urllib.pathname2url(path)
207+ uri = urlparse.urlunsplit(split[0:2] + (path, '', ''))
208 return uri
209
210 def __notify_source(self, *args):
211@@ -478,7 +481,7 @@
212
213 # make sure the file exists if this is supposed to be a local track
214 if track.is_local():
215- if not os.path.exists(track.get_loc()):
216+ if not track.exists():
217 logger.error(_("File does not exist: %s") %
218 track.get_loc())
219 return False
220@@ -490,7 +493,7 @@
221 self.reset_playtime_stamp()
222
223 self.playbin.set_property("uri", uri)
224- if uri.startswith("cdda://"):
225+ if urlparse.urlsplit(uri)[0] == "cdda":
226 self.notify_id = self.playbin.connect('notify::source',
227 self.__notify_source)
228
229
230=== modified file 'xl/playlist.py'
231--- xl/playlist.py 2009-04-14 17:34:36 +0000
232+++ xl/playlist.py 2009-04-14 23:33:28 +0000
233@@ -40,7 +40,7 @@
234 except ImportError:
235 import elementtree as ETree
236
237-from urlparse import urlparse
238+import urlparse
239 import logging
240 logger = logging.getLogger(__name__)
241
242@@ -209,7 +209,7 @@
243 for track in playlist:
244 handle.write("<entry>\n")
245 handle.write(" <title>%s</title>\n" % track['title'])
246- handle.write(" <ref href=\"%s\" />\n" % urllib.quote(track.get_loc()))
247+ handle.write(" <ref href=\"%s\" />\n" % track.get_loc())
248 handle.write("</entry>\n")
249
250 handle.write("</asx>")
251@@ -222,7 +222,7 @@
252 pl = Playlist(name=name)
253 for t in tracks:
254 tr = track.Track()
255- loc = urllib.unquote(t.find("ref").get("href"))
256+ loc = t.find("ref").get("href")
257 tr.set_loc(loc)
258 tr['title'] = t.find("title").text.strip()
259 tr.read_tags()
260@@ -254,11 +254,8 @@
261 if track[tag] == u"":
262 continue
263 handle.write(" <%s>%s</%s>\n" % (xs, track[tag],xs) )
264- url = urllib.quote(track.get_loc())
265- if urlparse(track.get_loc())[0] == "":
266- handle.write(" <location>file://%s</location>\n" % url)
267- else:
268- handle.write(" <location>%s</location>\n" % url)
269+ url = track.get_loc()
270+ handle.write(" <location>%s</location>\n" % url)
271 handle.write(" </track>\n")
272
273 handle.write(" </trackList>\n")
274@@ -274,7 +271,7 @@
275 pl = Playlist(name=name)
276 for t in tracks:
277 tr = track.Track()
278- loc = urllib.unquote(t.find("%slocation"%ns).text.strip())
279+ loc = t.find("%slocation"%ns).text.strip()
280 tr.set_loc(loc)
281 for xs, tag in XSPF_MAPPING.iteritems():
282 try:
283@@ -441,7 +438,7 @@
284 track: the track to add [Track]
285 location: the index to insert at [int]
286 """
287- if os.path.exists(track.get_loc_for_io()) or not ignore_missing_files:
288+ if track.exists() or not ignore_missing_files:
289 self.add_tracks([track], location)
290
291 def add_tracks(self, tracks, location=None, add_duplicates=True):
292
293=== modified file 'xl/track.py'
294--- xl/track.py 2009-03-24 23:16:10 +0000
295+++ xl/track.py 2009-04-18 06:01:50 +0000
296@@ -20,6 +20,9 @@
297 import xl.metadata as metadata
298 from xl.common import lstrip_special
299 import logging, traceback
300+import urlparse
301+import urllib
302+import urllib2
303 logger = logging.getLogger(__name__)
304
305 settings = settings.SettingsManager.settings
306@@ -61,8 +64,10 @@
307
308 loc: the location [string]
309 """
310- if loc.startswith("file://"):
311- loc = loc[7:]
312+ split = urlparse.urlsplit(loc)
313+ if split[0] == "":
314+ loc = os.path.abspath(loc)
315+ loc = urlparse.urlunsplit(('file', split[1], loc, '', ''))
316 self['loc'] = loc
317
318 def get_loc(self):
319@@ -77,6 +82,22 @@
320 except:
321 return self['loc']
322
323+ def exists(self):
324+ if self.is_local():
325+ return os.path.exists(self.local_file_name())
326+ else:
327+ try:
328+ urllib2.urlopen(self.get_loc_for_io())
329+ except urllib2.URLError, urllib2.HTTPError:
330+ return False
331+ else:
332+ return True
333+
334+ def local_file_name(self):
335+ if not self.is_local():
336+ return None
337+ return common.local_file_from_url(self.get_loc_for_io())
338+
339 def get_loc_for_io(self):
340 """
341 Gets the location in its original form. should always be correct.
342@@ -207,9 +228,11 @@
343
344
345 # fill out file specific items
346- mtime = os.path.getmtime(self.get_loc_for_io())
347+ split = urlparse.urlsplit(self.get_loc_for_io())
348+ path = self.local_file_name()
349+ mtime = os.path.getmtime(path)
350 self['modified'] = mtime
351- self['basedir'] = os.path.dirname(self.get_loc_for_io())
352+ self['basedir'] = os.path.dirname(path)
353 self._dirty = True
354 return f
355 except:
356@@ -217,7 +240,7 @@
357 return False
358
359 def is_local(self):
360- return urlparse(self.get_loc())[0] == ""
361+ return urlparse.urlsplit(self.get_loc()).scheme == "file"
362
363 def get_track(self):
364 """