Commit d43ad0be authored by Jean-Francois Dockes's avatar Jean-Francois Dockes
Browse files

uprcl: small fixes, comments, misc cover art improvements

parent 82da0f6b
......@@ -34,5 +34,6 @@ class MinimConfig(object):
quotes = self.quotes,
escape = self.escape,
whitespace = self.whitespace)
uplog("Minim config exclude patterns: %s" % lpats)
return lpats
spats = conftree.stringsToString(lpats)
uplog("skippedNames from Minim excludePattern: %s" % spats)
return spats
......@@ -127,6 +127,9 @@ class Folders(object):
for diridx in self._playlists:
pldocidx = self._dirvec[diridx]["."][1]
pldoc = self._rcldocs[pldocidx]
arturi = uprclutils.docarturi(pldoc, self._httphp,self._pprefix)
if arturi:
pldoc.albumarturi = arturi
plpath = uprclutils.docpath(pldoc)
try:
m3u = uprclutils.M3u(plpath)
......@@ -152,8 +155,8 @@ class Folders(object):
elt = os.path.split(url)[1]
self._dirvec[diridx][elt] = (-1, docidx)
else:
uplog("No track found: playlist %s entry %s" %
(plpath,url))
#uplog("No track found: playlist %s entry %s" %
# (plpath,url))
pass
del self._playlists
......@@ -189,8 +192,8 @@ class Folders(object):
b = os.path.basename(url1)
url1 = os.path.join(a, doc.contentgroup, b)
# Split path, then walk the vector, possibly creating
# directory entries as needed
# Split path. The caller will walk the list (possibly creating
# directory entries as needed, or doing something else).
path = url1.split('/')[1:]
return fathidx, path
......@@ -236,7 +239,8 @@ class Folders(object):
for docidx in range(len(self._rcldocs)):
doc = self._rcldocs[docidx]
# No need to include non-audio types in the visible tree.
# Only include selected mtypes: tracks, playlists,
# directories etc.
if doc.mtype not in audiomtypes:
continue
......@@ -362,8 +366,18 @@ class Folders(object):
return [rcldirentry(pid + 'folders', pid, '[folders]'),]
# Look all non-directory docs inside directory, and return the cover
# art we find.
# Look all non-directory docs inside directory, and return the
# cover art we find. The doc albumarturi field has been set during
# the initial walk by call to uprclutils.docarturi()
#
# TBD In the case where this is a contentgroup directory, we'd
# need to go look into the file system for a group.xxx
# image. Actually, the best approach would probably be to create
# virtual doc records for such directories, and set their
# albumarturi during the tree setup. As it is things work if the
# tracks rely on the group pic (instead of having an embedded pic
# or track pic) Also: playlists: need to look at the physical dir
# for a e.g. playlistname.jpg.
def _arturifordir(self, diridx):
for nm,ids in self._dirvec[diridx].items():
if ids[1] >= 0:
......@@ -399,7 +413,7 @@ class Folders(object):
if thisdocidx >= 0:
doc = self._rcldocs[thisdocidx]
else:
uplog("No doc for %s" % pid)
# uplog("No doc for %s" % pid)
doc = None
if thisdiridx >= 0:
......@@ -429,6 +443,7 @@ class Folders(object):
else:
return sorted(entries, cmp=cmpentries)
# Return path for objid, which has to be a container.This is good old
# pwd... It is called from the search module for generating a 'dir:'
# recoll filtering directive.
......
......@@ -15,13 +15,14 @@
from __future__ import print_function
import sys
import conftree
import copy
import locale
import os
import shutil
import conftree
import subprocess
import sys
import time
import copy
from upmplgutils import uplog
import uprclinit
......@@ -42,19 +43,23 @@ def _maybeinitconfdir(confdir, topdirs):
if not os.path.exists(dst) and os.path.exists(src):
shutil.copyfile(src, dst)
exclpats = []
exclpats = ""
if uprclinit.g_minimconfig:
exclpats = uprclinit.g_minimconfig.getexcludepatterns()
path = os.path.join(confdir, "recoll.conf")
if not os.path.exists(path):
f = open(path, "w")
f.write("topdirs=%s\n" % topdirs)
f.write("idxabsmlen=0\n")
f.write("loglevel=2\n")
f.write("noaspell=1\n")
f.write("nomd5types = rclaudio rclimg\n")
f.write("testmodifusemtime=1\n")
f = open(path, "wb")
f.write(b"topdirs = %s\n"% topdirs.encode(locale.getpreferredencoding()))
f.write(b"idxabsmlen = 0\n")
f.write(b"loglevel = 2\n")
f.write(b"noaspell = 1\n")
f.write(b"nomd5types = rclaudio rclimg\n")
f.write(b"testmodifusemtime = 1\n")
if exclpats:
f.write(b"skippedNames+ = " + exclpats.encode("utf-8") + b"\n")
else:
f.write(b"skippedNames+ = \n")
f.close()
......
......@@ -32,7 +32,8 @@ from uprclhttp import runbottle
import minimconfig
from upmplgutils import uplog
from uprclutils import findmyip, stringToStrings
from uprclutils import findmyip
from conftree import stringToStrings
# Once initialization (not on imports)
try:
......
......@@ -21,7 +21,8 @@ import re
from recoll import recoll
from upmplgutils import uplog
from uprclutils import stringToStrings, rcldoctoentry, cmpentries, upnp2rclfields
from uprclutils import rcldoctoentry, cmpentries, upnp2rclfields
from conftree import stringToStrings
def _getchar(s, i):
if i < len(s):
......
#
# Copyright (C) 2017 J.F.Dockes
# Copyright (C) 2017-2019 J.F.Dockes
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -158,6 +158,26 @@ def rcldoctoentry(id, pid, httphp, pathprefix, doc):
return li
def rcldirentry(id, pid, title, arturi=None, artist=None, upnpclass=None,
searchable='1', date=None):
""" Create container entry in format expected by parent """
#uplog("rcldirentry: id %s pid %s tt %s dte %s clss %s artist %s arturi %s" %
# (id,pid,title,date,upnpclass,artist,arturi))
ret = {'id':id, 'pid':pid, 'tt':title, 'tp':'ct', 'searchable':searchable}
if arturi:
ret['upnp:albumArtURI'] = arturi
if artist:
ret['upnp:artist'] = artist
if date:
ret['dc:date'] = date
if upnpclass:
ret['upnp:class'] = upnpclass
else:
ret['upnp:class'] = 'object.container'
return ret
# Bogus entry for the top directory while the index/db is updating
def waitentry(id, pid, httphp):
li = {}
......@@ -200,42 +220,77 @@ def printable(s):
return s.decode('utf-8', errors='replace') if s else ""
else:
return s
# Transform a string so that it can be used as a file name, using
# minimserver rules: just remove any of: " * / : < > ? \ |
def tag2fn(s):
if type(s) == type(u""):
s = s.encode(locale.getpreferredencoding())
return s.replace(b'"', b'').replace(b'*', b'').\
replace(b'/', b'').replace(b':', b'').replace(b'<', b'').\
replace(b'>', b'').replace(b'?', b'').replace(b'\\', b'').\
replace(b'|', b'')
##########################
# Find cover art for doc.
#
# We return a special uri if the file has embedded image data, else an
# uri for for the directory cover art (if any).
# We are usually called repeatedly for the same directory, so we cache
# one result.
#
# The doc can come from recoll (track or directory), or be virtual (playlist)
_foldercache = {}
_artexts = (b'.jpg', b'.png')
_artnames = (b'cover', b'folder')
_artfilenames = []
for base in _artnames:
# All standard cover art file names:
_artexts = (b'.jpg', b'.png')
def _artnamegen(base):
for ext in _artexts:
_artfilenames.append(base + ext)
yield base + ext
_folderartbases = (b'cover', b'folder')
_folderartnames = []
for base in _folderartbases:
for path in _artnamegen(base):
_folderartnames.append(path)
def docarturi(doc, httphp, pathprefix):
global _foldercache, _artnames
global _foldercache
bpp = pathprefix.encode('utf-8')
objpath = docpath(doc)
# Check for an image specific to the track file
base,ext = os.path.splitext(objpath)
for artpath in _artnamegen(base):
if os.path.exists(artpath):
return _httpurl(httphp, os.path.join(bpp, artpath))
# Else try to use an embedded img
if doc.embdimg:
arturi = embdimgurl(doc, httphp, pathprefix)
if arturi:
#uplog("docarturi: embedded: %s"%printable(arturi))
return arturi
# Check for an image specific to the track file
path,ext = os.path.splitext(docpath(doc))
for ext in _artexts:
if os.path.exists(path + ext):
return _httpurl(httphp, os.path.join(bpp, path+ext))
# won't work for the virtual group directory itself: it has no doc
if doc.contentgroup:
base = os.path.join(os.path.dirname(objpath), tag2fn(doc.contentgroup))
for artpath in _artnamegen(base):
#uplog("docarturi: testing %s" % artpath)
if os.path.exists(artpath):
return _httpurl(httphp, os.path.join(bpp, artpath))
# TBD Here minimserver would look for the group then album disc
# then album art
# If doc is a directory, this returns itself, else the father dir.
folder = docfolder(doc)
# Look for an appropriate image in the file folder. Generating the
# charcase combinations would be complicated so we list the folder
# and look for a case-insensitive match
if folder not in _foldercache:
_foldercache = {}
_foldercache[folder] = None
......@@ -248,7 +303,7 @@ def docarturi(doc, httphp, pathprefix):
except:
#traceback.print_exc()
continue
if flowersimple in _artfilenames:
if flowersimple in _folderartnames:
artnm = fsimple
path = os.path.join(bpp, folder, artnm)
_foldercache[folder] = _httpurl(httphp, path)
......@@ -266,6 +321,7 @@ def docarturi(doc, httphp, pathprefix):
pass
return arturi
def _keyvalornull(a, k):
return a[k] if k in a else "NULL"
def _logentry(nm, e1):
......@@ -275,6 +331,7 @@ def _logentry(nm, e1):
tn = _keyvalornull(e1, 'upnp:originalTrackNumber')
uplog("%s tp %s alb %s dir %s tno %s" % (nm, tp,al,dr,tn))
def _cmpentries_func(e1, e2):
#uplog("cmpentries");_logentry("e1", e1);_logentry("e2", e2)
tp1 = e1['tp']
......@@ -331,91 +388,6 @@ if PY3:
else:
cmpentries=_cmpentries_func
def rcldirentry(id, pid, title, arturi=None, artist=None, upnpclass=None,
searchable='1', date=None):
""" Create container entry in format expected by parent """
#uplog("rcldirentry: id %s pid %s tt %s dte %s clss %s artist %s arturi %s" %
# (id,pid,title,date,upnpclass,artist,arturi))
ret = {'id':id, 'pid':pid, 'tt':title, 'tp':'ct', 'searchable':searchable}
if arturi:
ret['upnp:albumArtURI'] = arturi
if artist:
ret['upnp:artist'] = artist
if date:
ret['dc:date'] = date
if upnpclass:
ret['upnp:class'] = upnpclass
else:
ret['upnp:class'] = 'object.container'
return ret
# Parse string into (possibly multiword) tokens
# 'a b "one phrase" c' -> [a, b, 'one phrase', c]
def stringToStrings(str):
# States. Note that ESCAPE can only occur inside INQUOTE
SPACE, TOKEN, INQUOTE, ESCAPE = range(4)
tokens = []
curtok = ""
state = SPACE;
for c in str:
if c == '"':
if state == SPACE:
state = INQUOTE
elif state == TOKEN:
curtok += '"'
elif state == INQUOTE:
if curtok:
tokens.append(curtok);
curtok = ""
state = SPACE
elif state == ESCAPE:
curtok += '"'
state = INQUOTE
continue;
elif c == '\\':
if state == SPACE or state == TOKEN:
curtok += '\\'
state = TOKEN
elif state == INQUOTE:
state = ESCAPE
elif state == ESCAPE:
curtok += '\\'
state = INQUOTE
continue
elif c == ' ' or c == '\t' or c == '\n' or c == '\r':
if state == SPACE or state == TOKEN:
if curtok:
tokens.append(curtok)
curtok = ""
state = SPACE
elif state == INQUOTE or state == ESCAPE:
curtok += c
continue;
else:
if state == ESCAPE:
state = INQUOTE
elif state == SPACE:
state = TOKEN
elif state == TOKEN or state == INQUOTE:
pass
curtok += c
if state == SPACE:
pass
elif state == TOKEN:
if curtok:
tokens.append(curtok)
elif state == INQUOTE or state == ESCAPE:
raise Exception("Bad string: <" + str + ">")
return tokens
# Find first non loopback ip. This is surprisingly
# difficult. Executing "ip addr" actually seems to be the simplest
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment