|
a/src/mediaserver/cdplugins/uprcl/uprclutils.py |
|
b/src/mediaserver/cdplugins/uprcl/uprclutils.py |
|
... |
|
... |
14 |
# You should have received a copy of the GNU General Public License
|
14 |
# You should have received a copy of the GNU General Public License
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16 |
from __future__ import print_function
|
16 |
from __future__ import print_function
|
17 |
|
17 |
|
18 |
import sys
|
18 |
import sys
|
19 |
import urllib
|
19 |
PY3 = sys.version > '3'
|
|
|
20 |
if PY3:
|
|
|
21 |
from urllib.parse import quote as urlquote
|
|
|
22 |
import functools
|
|
|
23 |
else:
|
|
|
24 |
from urllib import quote as urlquote
|
|
|
25 |
|
20 |
import os
|
26 |
import os
|
21 |
import glob
|
27 |
import glob
|
22 |
import subprocess
|
28 |
import subprocess
|
23 |
import mutagen
|
29 |
import mutagen
|
24 |
try:
|
30 |
import io
|
25 |
from cStringIO import StringIO
|
|
|
26 |
except ImportError:
|
|
|
27 |
from StringIO import StringIO
|
|
|
28 |
|
31 |
|
29 |
from upmplgutils import uplog
|
32 |
from upmplgutils import uplog
|
30 |
|
33 |
|
31 |
# This must be consistent with what contentdirectory.cxx does
|
34 |
# This must be consistent with what contentdirectory.cxx does
|
32 |
g_myprefix = '0$uprcl$'
|
35 |
g_myprefix = '0$uprcl$'
|
|
... |
|
... |
102 |
else:
|
105 |
else:
|
103 |
li['tp']= 'it'
|
106 |
li['tp']= 'it'
|
104 |
# TBD
|
107 |
# TBD
|
105 |
li['upnp:class'] = 'object.item.audioItem.musicTrack'
|
108 |
li['upnp:class'] = 'object.item.audioItem.musicTrack'
|
106 |
|
109 |
|
107 |
for oname,dname in upnp2rclfields.iteritems():
|
110 |
for oname,dname in upnp2rclfields.items():
|
108 |
val = getattr(doc, dname)
|
111 |
val = getattr(doc, dname)
|
109 |
if val:
|
112 |
if val:
|
110 |
li[oname] = val
|
113 |
li[oname] = val
|
111 |
|
114 |
|
112 |
if 'upnp:artist' not in li and doc.albumartist:
|
115 |
if 'upnp:artist' not in li and doc.albumartist:
|
|
... |
|
... |
133 |
# parent process to match urls to plugins)
|
136 |
# parent process to match urls to plugins)
|
134 |
path = doc.getbinurl()
|
137 |
path = doc.getbinurl()
|
135 |
path = path[7:]
|
138 |
path = path[7:]
|
136 |
if 'tt' not in li:
|
139 |
if 'tt' not in li:
|
137 |
li['tt'] = os.path.basename(path.decode('UTF-8', errors = 'replace'))
|
140 |
li['tt'] = os.path.basename(path.decode('UTF-8', errors = 'replace'))
|
138 |
path = os.path.join(pathprefix, path)
|
141 |
path = os.path.join(pathprefix.encode('ascii'), path)
|
139 |
li['uri'] = "http://%s%s" % (httphp, urllib.quote(path))
|
142 |
li['uri'] = "http://%s%s" % (httphp, urlquote(path))
|
140 |
#uplog("rcldoctoentry: uri: %s" % li['uri'])
|
143 |
#uplog("rcldoctoentry: uri: %s" % li['uri'])
|
141 |
|
144 |
|
142 |
# The album art uri is precooked with httphp and prefix
|
145 |
# The album art uri is precooked with httphp and prefix
|
143 |
if doc.albumarturi:
|
146 |
if doc.albumarturi:
|
144 |
li['upnp:albumArtURI'] = doc.albumarturi
|
147 |
li['upnp:albumArtURI'] = doc.albumarturi
|
|
... |
|
... |
170 |
else:
|
173 |
else:
|
171 |
return os.path.dirname(path)
|
174 |
return os.path.dirname(path)
|
172 |
|
175 |
|
173 |
def embdimgurl(doc, httphp, pathprefix):
|
176 |
def embdimgurl(doc, httphp, pathprefix):
|
174 |
if doc.embdimg == 'jpg':
|
177 |
if doc.embdimg == 'jpg':
|
175 |
ext = '.jpg'
|
178 |
ext = b'.jpg'
|
176 |
elif doc.embdimg == '.png':
|
179 |
elif doc.embdimg == '.png':
|
177 |
ext = 'png'
|
180 |
ext = b'png'
|
178 |
else:
|
181 |
else:
|
179 |
return None
|
182 |
return None
|
180 |
path = doc.getbinurl()
|
183 |
path = doc.getbinurl()
|
181 |
path = path[7:]
|
184 |
path = path[7:]
|
182 |
path = urllib.quote(os.path.join(pathprefix, path+ext))
|
185 |
path = urlquote(os.path.join(pathprefix.encode('ascii'), path+ext))
|
183 |
path += "?embed=1"
|
186 |
path += "?embed=1"
|
184 |
return "http://%s%s" % (httphp, path)
|
187 |
return "http://%s%s" % (httphp, path)
|
185 |
|
188 |
|
186 |
def printable(s):
|
189 |
def printable(s):
|
187 |
return s.decode('utf-8', errors='replace') if s else ""
|
190 |
return s.decode('utf-8', errors='replace') if s else ""
|
188 |
|
191 |
|
189 |
def _httpurl(path, httphp, pathprefix):
|
192 |
def _httpurl(path, httphp, pathprefix):
|
190 |
return "http://%s%s" % (httphp, urllib.quote(path))
|
193 |
return "http://%s%s" % (httphp, urlquote(path))
|
191 |
|
194 |
|
192 |
# Find cover art for doc.
|
195 |
# Find cover art for doc.
|
193 |
#
|
196 |
#
|
194 |
# We return a special uri if the file has embedded image data, else an
|
197 |
# We return a special uri if the file has embedded image data, else an
|
195 |
# uri for for the directory cover art (if any).
|
198 |
# uri for for the directory cover art (if any).
|
196 |
# We are usually called repeatedly for the same directory, so we cache
|
199 |
# We are usually called repeatedly for the same directory, so we cache
|
197 |
# one result.
|
200 |
# one result.
|
198 |
_foldercache = {}
|
201 |
_foldercache = {}
|
199 |
_artexts = ('.jpg', '.png')
|
202 |
_artexts = (b'.jpg', b'.png')
|
200 |
_artnames = ('folder', 'cover')
|
203 |
_artnames = ('folder', 'cover')
|
201 |
def docarturi(doc, httphp, pathprefix):
|
204 |
def docarturi(doc, httphp, pathprefix):
|
202 |
global _foldercache, _artnames
|
205 |
global _foldercache, _artnames
|
203 |
|
206 |
|
204 |
if doc.embdimg:
|
207 |
if doc.embdimg:
|
|
... |
|
... |
209 |
|
212 |
|
210 |
# Check for an image specific to the track file
|
213 |
# Check for an image specific to the track file
|
211 |
path,ext = os.path.splitext(docpath(doc))
|
214 |
path,ext = os.path.splitext(docpath(doc))
|
212 |
for ext in _artexts:
|
215 |
for ext in _artexts:
|
213 |
if os.path.exists(path + ext):
|
216 |
if os.path.exists(path + ext):
|
214 |
return _httpurl(os.path.join(pathprefix, path+ext), httphp,
|
217 |
return _httpurl(os.path.join(pathprefix.encode('ascii'),
|
215 |
pathprefix)
|
218 |
path+ext), httphp, pathprefix)
|
216 |
|
219 |
|
217 |
# If doc is a directory, this returns itself, else the father dir.
|
220 |
# If doc is a directory, this returns itself, else the father dir.
|
218 |
folder = docfolder(doc)
|
221 |
folder = docfolder(doc)
|
219 |
|
222 |
|
220 |
if folder not in _foldercache:
|
223 |
if folder not in _foldercache:
|
|
... |
|
... |
232 |
for ext in _artexts:
|
235 |
for ext in _artexts:
|
233 |
if flowersimple == base + ext:
|
236 |
if flowersimple == base + ext:
|
234 |
artnm = fsimple
|
237 |
artnm = fsimple
|
235 |
if artnm:
|
238 |
if artnm:
|
236 |
_foldercache[folder] = _httpurl(
|
239 |
_foldercache[folder] = _httpurl(
|
237 |
urllib.quote(os.path.join(pathprefix, folder, artnm)),
|
240 |
urlquote(os.path.join(pathprefix, folder, artnm)),
|
238 |
httphp, pathprefix)
|
241 |
httphp, pathprefix)
|
239 |
break
|
242 |
break
|
240 |
except:
|
243 |
except:
|
241 |
pass
|
244 |
pass
|
242 |
|
245 |
|
|
... |
|
... |
255 |
al = _keyvalornull(e1, 'upnp:album')
|
258 |
al = _keyvalornull(e1, 'upnp:album')
|
256 |
dr = os.path.dirname(_keyvalornull(e1, 'uri'))
|
259 |
dr = os.path.dirname(_keyvalornull(e1, 'uri'))
|
257 |
tn = _keyvalornull(e1, 'upnp:originalTrackNumber')
|
260 |
tn = _keyvalornull(e1, 'upnp:originalTrackNumber')
|
258 |
uplog("%s tp %s alb %s dir %s tno %s" % (nm, tp,al,dr,tn))
|
261 |
uplog("%s tp %s alb %s dir %s tno %s" % (nm, tp,al,dr,tn))
|
259 |
|
262 |
|
260 |
def cmpentries(e1, e2):
|
263 |
def _cmpentries_func(e1, e2):
|
261 |
#uplog("cmpentries");_logentry("e1", e1);_logentry("e2", e2)
|
264 |
#uplog("cmpentries");_logentry("e1", e1);_logentry("e2", e2)
|
262 |
tp1 = e1['tp']
|
265 |
tp1 = e1['tp']
|
263 |
tp2 = e2['tp']
|
266 |
tp2 = e2['tp']
|
264 |
isct1 = tp1 == 'ct'
|
267 |
isct1 = tp1 == 'ct'
|
265 |
isct2 = tp2 == 'ct'
|
268 |
isct2 = tp2 == 'ct'
|
|
... |
|
... |
296 |
return 1
|
299 |
return 1
|
297 |
|
300 |
|
298 |
k = 'upnp:originalTrackNumber'
|
301 |
k = 'upnp:originalTrackNumber'
|
299 |
a1 = e1[k] if k in e1 else "0"
|
302 |
a1 = e1[k] if k in e1 else "0"
|
300 |
a2 = e2[k] if k in e2 else "0"
|
303 |
a2 = e2[k] if k in e2 else "0"
|
|
|
304 |
try:
|
301 |
return int(a1) - int(a2)
|
305 |
return int(a1) - int(a2)
|
|
|
306 |
except:
|
|
|
307 |
uplog("upnp:originalTrackNumber %s %s"% (a1, a2))
|
|
|
308 |
return 0
|
302 |
|
309 |
|
|
|
310 |
if PY3:
|
|
|
311 |
cmpentries=functools.cmp_to_key(_cmpentries_func)
|
|
|
312 |
else:
|
|
|
313 |
cmpentries=_cmpentries_func
|
303 |
|
314 |
|
304 |
def rcldirentry(id, pid, title, arturi=None, artist=None, upnpclass=None,
|
315 |
def rcldirentry(id, pid, title, arturi=None, artist=None, upnpclass=None,
|
305 |
searchable='1', date=None):
|
316 |
searchable='1', date=None):
|
306 |
""" Create container entry in format expected by parent """
|
317 |
""" Create container entry in format expected by parent """
|
307 |
#uplog("rcldirentry: id %s pid %s tt %s dte %s clss %s artist %s arturi %s" %
|
318 |
#uplog("rcldirentry: id %s pid %s tt %s dte %s clss %s artist %s arturi %s" %
|
|
... |
|
... |
412 |
raise err
|
423 |
raise err
|
413 |
|
424 |
|
414 |
f = None
|
425 |
f = None
|
415 |
size = 0
|
426 |
size = 0
|
416 |
if 'audio/mp3' in mutf.mime:
|
427 |
if 'audio/mp3' in mutf.mime:
|
417 |
for tagname in mutf.iterkeys():
|
428 |
for tagname in mutf.keys():
|
418 |
if tagname.startswith('APIC:'):
|
429 |
if tagname.startswith('APIC:'):
|
419 |
#self.em.rclog("mp3 img: %s" % mutf[tagname].mime)
|
430 |
#self.em.rclog("mp3 img: %s" % mutf[tagname].mime)
|
420 |
mtype = mutf[tagname].mime
|
431 |
mtype = mutf[tagname].mime
|
421 |
s = mutf[tagname].data
|
432 |
s = mutf[tagname].data
|
422 |
size = len(s)
|
433 |
size = len(s)
|
423 |
f = StringIO(s)
|
434 |
f = io.BytesIO(s)
|
424 |
elif 'audio/x-flac' in mutf.mime:
|
435 |
elif 'audio/x-flac' in mutf.mime:
|
425 |
if mutf.pictures:
|
436 |
if mutf.pictures:
|
426 |
mtype = mutf.pictures[0].mime
|
437 |
mtype = mutf.pictures[0].mime
|
427 |
size = len(mutf.pictures[0].data)
|
438 |
size = len(mutf.pictures[0].data)
|
428 |
f = StringIO(mutf.pictures[0].data)
|
439 |
f = io.BytesIO(mutf.pictures[0].data)
|
429 |
elif 'audio/mp4' in mutf.mime:
|
440 |
elif 'audio/mp4' in mutf.mime:
|
430 |
if 'covr' in mutf.iterkeys():
|
441 |
if 'covr' in mutf.keys():
|
431 |
format = mutf['covr'][0].imageformat
|
442 |
format = mutf['covr'][0].imageformat
|
432 |
if format == mutagen.mp4.AtomDataType.JPEG:
|
443 |
if format == mutagen.mp4.AtomDataType.JPEG:
|
433 |
mtype = 'image/jpeg'
|
444 |
mtype = 'image/jpeg'
|
434 |
else:
|
445 |
else:
|
435 |
mtype = 'image/png'
|
446 |
mtype = 'image/png'
|
436 |
size = len(mutf['covr'][0])
|
447 |
size = len(mutf['covr'][0])
|
437 |
f = StringIO(mutf['covr'][0])
|
448 |
f = io.BytesIO(mutf['covr'][0])
|
438 |
|
449 |
|
439 |
if f is None:
|
450 |
if f is None:
|
440 |
raise Exception("can't open embedded image")
|
451 |
raise Exception("can't open embedded image")
|
441 |
else:
|
452 |
else:
|
442 |
return mtype, size, f
|
453 |
return mtype, size, f
|