Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package upmpdcli for openSUSE:Factory checked in at 2022-08-13 22:37:13 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/upmpdcli (Old) and /work/SRC/openSUSE:Factory/.upmpdcli.new.1521 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "upmpdcli" Sat Aug 13 22:37:13 2022 rev:10 rq:994889 version:1.5.19 Changes: -------- --- /work/SRC/openSUSE:Factory/upmpdcli/upmpdcli.changes 2022-06-02 21:54:16.492373587 +0200 +++ /work/SRC/openSUSE:Factory/.upmpdcli.new.1521/upmpdcli.changes 2022-08-13 22:37:14.446731814 +0200 @@ -0,0 +1,12 @@ +------------------------------------------------------------------- +Sat Aug 13 10:59:31 UTC 2022 - Michael Pujos <pujos.mich...@gmail.com> + +- update to 1.5.19: + * Fix uprcl web control interface (asking for update did not work) + * Fix upradios stations list order +- update to 1.5.18: + * Create an upmpdcli group during installation and change the group + to upmpdcli when started as root (in addition to the setuid()) + * uprcl: try to show more information when the initialisation fails + +------------------------------------------------------------------- Old: ---- upmpdcli-1.5.17.tar.gz upmpdcli-1.5.17.tar.gz.asc New: ---- upmpdcli-1.5.19.tar.gz upmpdcli-1.5.19.tar.gz.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ upmpdcli.spec ++++++ --- /var/tmp/diff_new_pack.c2XXIG/_old 2022-08-13 22:37:14.978733160 +0200 +++ /var/tmp/diff_new_pack.c2XXIG/_new 2022-08-13 22:37:14.982733170 +0200 @@ -17,7 +17,7 @@ Name: upmpdcli -Version: 1.5.17 +Version: 1.5.19 Release: 0 Summary: UPnP Media Renderer front-end to MPD, the Music Player Daemon License: GPL-2.0-or-later ++++++ upmpdcli-1.5.17.tar.gz -> upmpdcli-1.5.19.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/Makefile.in new/upmpdcli-1.5.19/Makefile.in --- old/upmpdcli-1.5.17/Makefile.in 2022-05-21 16:11:59.000000000 +0200 +++ new/upmpdcli-1.5.19/Makefile.in 2022-06-28 09:15:54.000000000 +0200 @@ -1749,8 +1749,8 @@ maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -@MAKECONFGUI_FALSE@clean-local: @MAKECONFGUI_FALSE@install-exec-local: +@MAKECONFGUI_FALSE@clean-local: clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-local mostlyclean-am diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/cfgui/picoxml.h new/upmpdcli-1.5.19/cfgui/picoxml.h --- old/upmpdcli-1.5.17/cfgui/picoxml.h 2021-12-14 11:41:23.000000000 +0100 +++ new/upmpdcli-1.5.19/cfgui/picoxml.h 2022-06-16 09:00:07.000000000 +0200 @@ -71,7 +71,7 @@ PicoXMLParser(const std::string& input) : m_in(input), m_pos(0) {} - virtual ~PicoXMLParser() {} + virtual ~PicoXMLParser() = default; PicoXMLParser(const PicoXMLParser&) = delete; PicoXMLParser& operator=(const PicoXMLParser&) = delete; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/configure new/upmpdcli-1.5.19/configure --- old/upmpdcli-1.5.17/configure 2022-05-21 16:11:59.000000000 +0200 +++ new/upmpdcli-1.5.19/configure 2022-06-28 09:15:54.000000000 +0200 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for upmpdcli 1.5.17. +# Generated by GNU Autoconf 2.69 for upmpdcli 1.5.19. # # Report bugs to <j...@lesbonscomptes.com>. # @@ -580,8 +580,8 @@ # Identity of this package. PACKAGE_NAME='upmpdcli' PACKAGE_TARNAME='upmpdcli' -PACKAGE_VERSION='1.5.17' -PACKAGE_STRING='upmpdcli 1.5.17' +PACKAGE_VERSION='1.5.19' +PACKAGE_STRING='upmpdcli 1.5.19' PACKAGE_BUGREPORT='j...@lesbonscomptes.com' PACKAGE_URL='http://www.lesbonscomptes.com/upmpdcli' @@ -1331,7 +1331,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures upmpdcli 1.5.17 to adapt to many kinds of systems. +\`configure' configures upmpdcli 1.5.19 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1398,7 +1398,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of upmpdcli 1.5.17:";; + short | recursive ) echo "Configuration of upmpdcli 1.5.19:";; esac cat <<\_ACEOF @@ -1523,7 +1523,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -upmpdcli configure 1.5.17 +upmpdcli configure 1.5.19 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1826,7 +1826,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by upmpdcli $as_me 1.5.17, which was +It was created by upmpdcli $as_me 1.5.19, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2696,7 +2696,7 @@ # Define the identity of the package. PACKAGE='upmpdcli' - VERSION='1.5.17' + VERSION='1.5.19' # Some tools Automake needs. @@ -6136,7 +6136,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by upmpdcli $as_me 1.5.17, which was +This file was extended by upmpdcli $as_me 1.5.19, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6203,7 +6203,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -upmpdcli config.status 1.5.17 +upmpdcli config.status 1.5.19 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/configure.ac new/upmpdcli-1.5.19/configure.ac --- old/upmpdcli-1.5.17/configure.ac 2022-05-21 16:10:56.000000000 +0200 +++ new/upmpdcli-1.5.19/configure.ac 2022-06-28 09:15:41.000000000 +0200 @@ -1,4 +1,4 @@ -AC_INIT([upmpdcli], [1.5.17], [j...@lesbonscomptes.com], +AC_INIT([upmpdcli], [1.5.19], [j...@lesbonscomptes.com], [upmpdcli], [http://www.lesbonscomptes.com/upmpdcli]) AC_PREREQ([2.53]) AC_CONFIG_MACRO_DIRS([m4]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/src/main.cxx new/upmpdcli-1.5.19/src/main.cxx --- old/upmpdcli-1.5.17/src/main.cxx 2021-04-28 15:05:37.000000000 +0200 +++ new/upmpdcli-1.5.19/src/main.cxx 2022-06-13 10:17:53.000000000 +0200 @@ -473,7 +473,7 @@ } if (geteuid() == 0) { if (runas == 0) { - LOGFAT("upmpdcli won't run as root and user " << upmpdcliuser << + LOGFAT("upmpdcli won't run as root and user " << upmpdcliuser << " does not exist " << endl); return 1; } @@ -556,20 +556,23 @@ } if (!opts.cachefn.empty()) { if (chown(opts.cachefn.c_str(), runas, -1) != 0) { - LOGERR("chown("<< opts.cachefn << ") : errno : " << - errno << endl); + LOGERR("chown("<< opts.cachefn << ") : errno : " << errno << endl); } } if (!g_configfilename.empty()) { - ensureconfreadable(g_configfilename.c_str(), upmpdcliuser.c_str(), - runas, runasg); + ensureconfreadable(g_configfilename.c_str(), upmpdcliuser.c_str(), runas, runasg); } + if (initgroups(upmpdcliuser.c_str(), runasg) < 0) { LOGERR("initgroup failed. Errno: " << errno << endl); } + + if (setgid(runasg) < 0) { + LOGSYSERR("main", "setgid", runasg); + LOGERR("Current gid: " << getegid() << "\n"); + } if (setuid(runas) < 0) { - LOGFAT("Can't set my uid to " << runas << " current: " << geteuid() - << endl); + LOGFAT("Can't set my uid to " << runas << " current: " << geteuid() << "\n"); return 1; } #if 0 @@ -601,8 +604,7 @@ if (!sc2mpdpath.empty()) { // Check if sc2mpd is actually there if (access(sc2mpdpath.c_str(), X_OK|R_OK) != 0) { - LOGERR("Specified path for sc2mpd: " << sc2mpdpath << - " is not executable" << endl); + LOGERR("Specified path for sc2mpd: " << sc2mpdpath << " is not executable\n"); sc2mpdpath.clear(); } } @@ -612,15 +614,13 @@ // command are executable. We'll assume that mpd is ok if (access(senderpath.c_str(), X_OK|R_OK) != 0) { LOGERR("The specified path for the sender starter script: [" - << senderpath << - "] is not executable, disabling the sender mode.\n"); + << senderpath << "] is not executable, disabling the sender mode.\n"); senderpath.clear(); } else { string path; if (!ExecCmd::which("mpd2sc", path)) { LOGERR("Sender starter was specified and found but the mpd2sc " - "command is not found (or executable). Disabling " - "the sender mode.\n"); + "command is not found (or executable). Disabling the sender mode.\n"); senderpath.clear(); } } @@ -698,8 +698,7 @@ } mylib = LibUPnP::getLibUPnP(); if (!mylib || !mylib->ok()) { - LOGFAT("Lib init failed: " << - mylib->errAsString("main", mylib->getInitError()) << endl); + LOGFAT("Lib init failed: " << mylib->errAsString("main", mylib->getInitError()) << endl); return 1; } hwaddr = mylib->hwaddr(); @@ -733,14 +732,12 @@ int fd; if ((fd = open(opts.screceiverstatefile.c_str(), O_CREAT|O_RDWR, 0644)) < 0) { - LOGERR("creat(" << opts.screceiverstatefile << ") : errno : " - << errno << endl); + LOGERR("creat(" << opts.screceiverstatefile << ") : errno : " << errno << endl); } else { close(fd); if (geteuid() == 0 && chown(opts.screceiverstatefile.c_str(), runas, -1) != 0) { - LOGERR("chown(" << opts.screceiverstatefile << ") : errno : " - << errno << endl); + LOGERR("chown(" << opts.screceiverstatefile << ") : errno : " << errno << endl); } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/src/mediaserver/cdplugins/pycommon/upradioconf.py new/upmpdcli-1.5.19/src/mediaserver/cdplugins/pycommon/upradioconf.py --- old/upmpdcli-1.5.17/src/mediaserver/cdplugins/pycommon/upradioconf.py 2021-04-13 16:15:54.000000000 +0200 +++ new/upmpdcli-1.5.19/src/mediaserver/cdplugins/pycommon/upradioconf.py 2022-06-28 09:09:53.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright (C) 2021 J.F.Dockes +# Copyright (C) 2021-2022 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 # the Free Software Foundation; either version 2 of the License, or @@ -16,6 +16,7 @@ import os import subprocess +import threading from upmplgutils import uplog, direntry import conftree @@ -30,29 +31,61 @@ self._readRadios(upconfig) #uplog("Radios: %s" % self._radios) + def _fetchStream(self, index, title, uri, artUri, mime): + uplog(f"upradios: Fetching {title}") + streamUri="" + try: + streamUri = subprocess.check_output([self.fetchstream, uri]) + streamUri = streamUri.decode('utf-8').strip("\r\n") + except Exception as ex: + uplog("fetchStream.py failed for %s: %s" % (title, ex)) + if streamUri: + self._radios[index] = (title, streamUri, uri, artUri, mime) + def _readRadiosFromConf(self, conf): '''Read radio definitions from a config file (either main file or radiolist)''' keys = conf.getSubKeys_unsorted() + threads = [] + cntt = 0 for k in keys: if k.startswith("radio"): title = k[6:] uri = conf.get("url", k) artUri = conf.get("artUrl", k) mime = conf.get("mime", k) - if mime: - uplog("GOT MIME %s" % mime) - streamUri = None - try: - streamUri = subprocess.check_output([self.fetchstream, uri]) - streamUri = streamUri.decode('utf-8').strip("\r\n") - except Exception as ex: - uplog("fetchStream.py failed for %s: %s" % (title, ex)) - if streamUri: - self._radios.append((title, streamUri, uri, artUri, mime)) + self._radios.append(None) + idx = len(self._radios) - 1 + t = threading.Thread(target=self._fetchStream, args=(idx, title, uri, artUri, mime)) + t.start() + threads.append(t) + cntt += 1 + # Note that it's ok to join a thread multiple times, so we keep things simple + if cntt >= self._maxthreads: + uplog(f"upradios: Waiting for threads") + for t in threads: + t.join() + uplog(f"upradios: Waiting done") + cntt = 0 + uplog(f"upradios: Waiting for threads") + for t in threads: + t.join() + uplog(f"upradios: Waiting done") + # Get rid of None entries in the list, keeping the order + tlist = [] + for rd in self._radios: + if rd: + tlist.append(rd) + self._radios = tlist + uplog(f"upradios: Init done") def _readRadios(self, upconfig): '''Read radio definitions from main config file, then possible radiolist''' self._radios = [] + self._maxthreads = upconfig.get("upradiosmaxthreads") + if self._maxthreads: + self._maxthreads = int(self._maxthreads) + else: + self._maxthreads = 5 self._readRadiosFromConf(upconfig) radiolist = upconfig.get("radiolist") if radiolist: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprcl-app.py new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprcl-app.py --- old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprcl-app.py 2021-03-20 14:30:41.000000000 +0100 +++ new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprcl-app.py 2022-06-13 09:44:52.000000000 +0200 @@ -35,11 +35,10 @@ # Initialize communication with our parent process: pipe and method # call dispatch -# Some of the modules we use write garbage to stdout, which messes the -# communication with our parent. Why can't people understand that this -# is verboten ? Get off my lawn ! So we dup stdout and close it, then -# pass the right file to cmdtalk. (hoping that none of the imports -# above print anything, else we'll have to move this code up) +# Some of the modules we use write garbage to stdout, which messes the communication with our +# parent. Why can't people understand that this is verboten ? So we dup stdout and close it, then +# pass the right file to cmdtalk. (hoping that none of the imports above print anything, else we'll +# have to move this code up) _outfile = os.fdopen(os.dup(1), "w") os.close(1) fd = os.open("/dev/null", os.O_WRONLY) @@ -121,14 +120,20 @@ raise Exception("uprcl-app: browse: can't browse meta for now") else: try: - if not uprclinit.ready(): + if not uprclinit.initdone(): entries = [waitentry(objid + 'notready', objid, uprclinit.getHttphp()),] - elif not idpath: - entries = _rootentries() else: - if len(rootmap) == 0: - _rootentries() - entries = _browsedispatch(objid, bflg, offset, count) + initstatus, initmessage = uprclinit.initstatus() + if not initstatus: + entries = [waitentry(objid + 'notready', objid, uprclinit.getHttphp(), + initmessage),] + else: + if not idpath: + entries = _rootentries() + else: + if len(rootmap) == 0: + _rootentries() + entries = _browsedispatch(objid, bflg, offset, count) finally: uprclinit.g_dblock.release_read() @@ -140,8 +145,7 @@ entries = entries[2] #msgproc.log("%s" % entries) encoded = json.dumps(entries) - return {"entries" : encoded, "nocache" : nocache, - "offset" : str(resoffs), "total" : str(total)} + return {"entries" : encoded, "nocache" : nocache, "offset" : str(resoffs), "total" : str(total)} @dispatcher.record('search') @@ -155,12 +159,18 @@ nocache = "1" try: - if not uprclinit.ready(): + if not uprclinit.initdone(): entries = [waitentry(objid + 'notready', objid, uprclinit.getHttphp()),] else: - entries = uprclsearch.search( - uprclinit.getTree('folders'), uprclinit.getRclConfdir(), objid, - upnps, uprclinit.getObjPrefix(), uprclinit.getHttphp(), uprclinit.getPathPrefix()) + initstatus, initmessage = uprclinit.initstatus() + if not initstatus: + entries = [waitentry(objid + 'notready', objid, uprclinit.getHttphp(), + initmessage),] + else: + entries = uprclsearch.search( + uprclinit.getTree('folders'), uprclinit.getRclConfdir(), objid, + upnps, uprclinit.getObjPrefix(), uprclinit.getHttphp(), + uprclinit.getPathPrefix()) finally: uprclinit.g_dblock.release_read() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprclfolders.py new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprclfolders.py --- old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprclfolders.py 2021-04-06 10:39:06.000000000 +0200 +++ new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprclfolders.py 2022-06-13 12:54:49.000000000 +0200 @@ -345,11 +345,16 @@ uplog("_rcl2folders took %.2f Seconds" % (end - start)) - # Fetch all the docs by querying Recoll with [mime:*], which is - # guaranteed to match every doc without overflowing the query size - # (because the number of mime types is limited). Something like - # title:* would overflow. This creates the main doc array, which is - # then used by all modules. + # Fetch all the docs by querying Recoll with [mime:*], which is guaranteed to match every doc + # without overflowing the query size (because the number of mime types is limited). Something + # like title:* would overflow. This creates the main doc array, which is then used by all + # modules. + # + # Depending on the recoll version, we use a Python list of Recoll Docs or the more compact but + # immutable QResultStore + # + # When using the resultstore, the records are not modifyable and the aliastags processing is + # performed at indexing time by rclaudio. Cf. minimtagfixer.py def _fetchalldocs(self, confdir): #uplog("_fetchalldocs: has_resultstore: %s" % _has_resultstore) start = timer() @@ -387,8 +392,7 @@ time.sleep(0) end = timer() - uplog("Retrieved %d docs in %.2f Seconds" % - (len(self._rcldocs), end - start)) + uplog("Retrieved %d docs in %.2f Seconds" % (len(self._rcldocs), end - start)) ############## diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprclhttp.py new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprclhttp.py --- old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprclhttp.py 2021-06-02 09:46:35.000000000 +0200 +++ new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprclhttp.py 2022-06-28 07:42:00.000000000 +0200 @@ -52,9 +52,9 @@ reloadsecs='1' if what == 'Update Index': - uprclinit.start_update() + uprclinit.start_index_update() elif what == 'Reset Index': - uprclinit.start_update(rebuild=True) + uprclinit.start_index_update(rebuild=True) elif what == 'Refresh Status': reloadsecs = '' elif not what: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprclindex.py new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprclindex.py --- old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprclindex.py 2021-03-18 17:56:29.000000000 +0100 +++ new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprclindex.py 2022-06-12 17:03:36.000000000 +0200 @@ -28,7 +28,10 @@ import uprclinit -def _maybeinitconfdir(confdir, topdirs): +# We recreate the Recoll configuration files at each startup. This is fast and avoids having to test +# for changed parameters. No directly user-entered data lives in there, it all comes from +# upmpdcli.conf, uprclconfrecolluser or the Minim configuration. +def _initconfdir(confdir, topdirs): if not os.path.isdir(confdir): if os.path.exists(confdir): raise Exception("Exists and not directory: %s" % confdir) @@ -83,16 +86,14 @@ _idxproc = None _lastidxstatus = None - def runindexer(confdir, topdirs, rebuild=False): global _idxproc, _lastidxstatus if _idxproc is not None: raise Exception("uprclrunindexer: already running") - _maybeinitconfdir(confdir, topdirs) + _initconfdir(confdir, topdirs) - cf = conftree.ConfSimple(os.path.join(confdir, "recoll.conf"), - readonly = False) + cf = conftree.ConfSimple(os.path.join(confdir, "recoll.conf"), readonly = False) td = cf.get("topdirs", '') if td != topdirs: cf.set("topdirs", topdirs) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprclinit.py new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprclinit.py --- old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprclinit.py 2021-12-09 18:39:20.000000000 +0100 +++ new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprclinit.py 2022-06-14 04:39:19.000000000 +0200 @@ -35,17 +35,30 @@ from upmplgutils import uplog, findmyip, getcachedir from conftree import stringToStrings +### Configuration stuff +_g_rclconfdir = "" _g_pathprefix = "" _g_httphp = "" -g_dblock = ReadWriteLock() -_g_rclconfdir = "" _g_friendlyname = "UpMpd-mediaserver" -_g_trees = {} -_g_trees_order = ['folders', 'playlists', 'tags', 'untagged'] -g_minimconfig = None # Prefix for object Ids. This must be consistent with what # contentdirectory.cxx does _g_myprefix = '0$uprcl$' +g_minimconfig = None + +### Index update status +# Running state: ""/"Updating"/"Rebuilding" +g_initrunning = "" +# Completion status: ok/notok +g_initstatus = False +# Possible error message if not ok +g_initmessage = "" + +### Data created during initialisation +_g_trees = {} +_g_trees_order = ['folders', 'playlists', 'tags', 'untagged'] + +g_dblock = ReadWriteLock() + def getObjPrefix(): return _g_myprefix @@ -70,19 +83,33 @@ def _reset_index(): _update_index(True) + +def initdone(): + g_dblock.acquire_read() + if g_initrunning: + return False + else: + return True + +def initstatus(): + return (g_initstatus, g_initmessage) + +def updaterunning(): + return g_initrunning + -# Create or update Recoll index, then read and process the data. This -# runs in the separate uprcl_init_worker thread, and signals -# startup/completion by setting/unsetting the g_initrunning flag +# Create or update Recoll index, then read and process the data. This runs in a separate thread, and +# signals startup/completion by setting/unsetting the g_initrunning flag. +# +# While this is running, or after a failure any access to the root container from a Control Point +# will display either an "Initializing" or error message. def _update_index(rebuild=False): uplog("Creating/updating index in %s for %s" % (_g_rclconfdir, g_rcltopdirs)) - # We take the writer lock, making sure that no browse/search - # thread are active, then set the busy flag and release the - # lock. This allows future browse operations to signal the - # condition to the user instead of blocking (if we kept the write - # lock). - global g_initrunning, _g_trees + # We take the writer lock, making sure that no browse/search thread are active, then set the + # busy flag and release the lock. This allows future browse operations to signal the condition + # to the user instead of blocking (if we kept the write lock). + global g_initrunning, _g_trees, g_initstatus, g_initmessage g_dblock.acquire_write() g_initrunning = "Rebuilding" if rebuild else "Updating" g_dblock.release_write() @@ -107,24 +134,31 @@ newtrees['playlists'] = playlists newtrees['tags'] = tagged _g_trees = newtrees + g_initstatus = True + uplog("Init done") + except Exception as ex: + g_initstatus = False + g_initmessage = str(ex) + uplog(f"Initialisation failed with: {g_initmessage}") finally: g_dblock.acquire_write() - g_initrunning = False + g_initrunning = "" g_dblock.release_write() -# Initialisation runs in a thread because of the possibly long index -# initialization, during which the main thread can answer -# "initializing..." to the clients. -def _uprcl_init_worker(): +# This is called from uprcl-app when starting up, before doing anything else. We read configuration +# data, then start two threads: the permanent HTTP server and the index update thread. +def uprcl_init(): + + global _g_pathprefix, g_initstatus, g_initmessage + ####### # Acquire configuration data. - global _g_pathprefix - # pathprefix would typically be something like "/uprcl". It's used - # for dispatching URLs to the right plugin for processing. We - # strip it whenever we need a real file path + # We get the path prefix from an environment variable set by our parent upmpdcli. It would + # typically be something like "/uprcl". It's used for dispatching URLs to the right plugin for + # processing. We strip it whenever we need a real file path. if "UPMPD_PATHPREFIX" not in os.environ: raise Exception("No UPMPD_PATHPREFIX in environment") _g_pathprefix = os.environ["UPMPD_PATHPREFIX"] @@ -159,13 +193,26 @@ g_rcltopdirs = g_minimconfig.getcontentdirs() if g_rcltopdirs: g_rcltopdirs = conftree.stringsToString(g_rcltopdirs) - if not g_rcltopdirs: - raise Exception("uprclmediadirs not in config") + # At this point g_rcltopdirs is a single string (possibly with quoted parts). Compute a list and + # check the elements + pthlist = conftree.stringToStrings(g_rcltopdirs) + goodpthlist = [] + for dir in pthlist: + if not os.path.isdir(dir): + uplog(f"uprcl: [{dir}] is not accessible") + else: + goodpthlist.append(dir) + if not goodpthlist: + g_initstatus = False + g_initmessage = "No accessible media directories in configuration" + return + + g_rcltopdirs = conftree.stringsToString(goodpthlist) + pthstr = g_upconfig.get("uprclpaths") if pthstr is None: uplog("uprclpaths not in config, using topdirs: [%s]" % g_rcltopdirs) - pthlist = stringToStrings(g_rcltopdirs) pthstr = "" for p in pthlist: pthstr += p + ":" + p + "," @@ -179,41 +226,25 @@ host,port = _g_httphp.split(':') - # Start the bottle app. Its' both the control/config interface and - # the file streamer + start_index_update() + + # Start the bottle app. It's both the control/config interface and the file streamer httpthread = threading.Thread(target=runbottle, - kwargs = {'host':host , - 'port':int(port), - 'pthstr':pthstr, - 'pathprefix':_g_pathprefix}) + kwargs = {'host':host , + 'port':int(port), + 'pthstr':pthstr, + 'pathprefix':_g_pathprefix}) httpthread.daemon = True httpthread.start() - _update_index() - - uplog("Init done") + uplog("Init started") -def uprcl_init(): - global g_initrunning - g_initrunning = True - initthread = threading.Thread(target=_uprcl_init_worker) - initthread.daemon = True - initthread.start() - -def ready(): - g_dblock.acquire_read() - if g_initrunning: - return False - else: - return True - -def updaterunning(): - return g_initrunning - -def start_update(rebuild=False): +# This is called from the Bottle Web UI interface for requesting an index update or rebuild +def start_index_update(rebuild=False): try: - if not ready(): + # initdone() acquires the reader lock + if not initdone(): return targ = _reset_index if rebuild else _update_index idxthread = threading.Thread(target=targ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprclplaylists.py new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprclplaylists.py --- old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprclplaylists.py 2021-04-11 14:12:02.000000000 +0200 +++ new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprclplaylists.py 2022-06-13 07:58:24.000000000 +0200 @@ -36,7 +36,10 @@ self._idprefix = '0$uprcl$playlists' self._httphp = httphp self._pprefix = pathprefix - self._radios = upradioconf.UpmpdcliRadios(uprclinit.g_upconfig) + if not conftree.valToBool(uprclinit.g_upconfig.get("uprclnoradioconf")): + self._radios = upradioconf.UpmpdcliRadios(uprclinit.g_upconfig) + else: + self._radios = [] self.recoll2playlists(rcldocs) # Create the untagged entries static vector by filtering the global diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprcltagscreate.py new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprcltagscreate.py --- old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprcltagscreate.py 2021-03-18 17:45:14.000000000 +0100 +++ new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprcltagscreate.py 2022-06-12 19:04:56.000000000 +0200 @@ -186,9 +186,9 @@ uplog("prepareTags: g_indextags: %s g_tagdisplaytag %s" % (g_indextags, g_tagdisplaytag)) - # Compute an array of (table name, recoll field) - # translations. Most often they are identical. This also - # determines what fields we create tables for. + # Compute an array of (table name, recoll field) translations for the tags we need to process, + # as determined by the indexTags property. Most often they are identical. This also determines + # what fields we create tables for. tabtorclfield = [] for nm in g_indextags: tb = _alltagtotable[nm] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprclutils.py new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprclutils.py --- old/upmpdcli-1.5.17/src/mediaserver/cdplugins/uprcl/uprclutils.py 2021-03-20 14:28:43.000000000 +0100 +++ new/upmpdcli-1.5.19/src/mediaserver/cdplugins/uprcl/uprclutils.py 2022-06-13 07:19:40.000000000 +0200 @@ -174,13 +174,13 @@ # Bogus entry for the top directory while the index/db is updating -def waitentry(id, pid, httphp): +def waitentry(id, pid, httphp, msg="Initializing..."): li = {} li['tp'] = 'it' li['id'] = id li['pid'] = pid li['upnp:class'] = 'object.item.audioItem.musicTrack' - li['tt'] = "Initializing..." + li['tt'] = msg li['uri'] = "http://%s%s" % (httphp, "/waiting") li['res.mime'] = "audio/mpeg" return li