# -*- coding: utf8 -*-
# mod/aba.py

import cfg
import inc.infos as infos
import inc.mpc as mpc
import mod.commun as commun

from inc.inputkiz import input_seq

# variables du modules
# question : est-ce nécessaire de les garder en mémoire,
#            ou bien on les définit à chaque opération ?
all_lvr = []    # all livres = tous les livres de la bibliothèque
sel_lvr = []    # la sélection des livres
sel_n2i = {}    # retrouver l'index d'un champ "n" dans sel_lvr[]

ouverture_biblio=False

def aba_infos_go():
    """ infos visuelles simples aba """
    infos.m_g("vous écoutez, " + lv_titre() + ", de " + lv_auteur())


def aba_fav1():
    """ ajoute le livre actuel aux favoris et téléchargements
        trois fois de suite : retélécharge, après confirmation, le livre
    """
    fav_add()


def aba_fav2():
    """ supprime le livre actuel des favoris et téléchargements,
        trois fois de suite : ajoute aux bannis
    """
    del_num4c = lv_num4c()
    msg = "supprimer " + zero_off(del_num4c) + " des favoris"
    infos.pdrNew(msg)
    suppression = favoris_bannis(del_num4c, "del_fav")
    if suppression == False:
        # le livre n'est pas dans les favoris,
        # on supprime le dossier
        dossier_du_livre = bbl_folder() + "/" + del_num4c
        if commun.dir_exists(dossier_du_livre):
            cmd = "rm -rf " + dossier_du_livre
            mpc.sub_proc(cmd)
        else:
            # le dossier n'existe pas,
            # on ajoute ce livre aux bannis
            favoris_bannis(del_num4c, "add_bannis")


def aba_fav3():
    """ menu favoris """
    pass


def alogger(s):
    """ fichier log pour débugger """
    pass


def bbl_changer(cle_biblio):
    """ changement de bibliothèque grâce au menu vocal """
    bibo = bbl_actu()
    if bibo == cle_biblio:
        msg = "Vous êtes déjà dans la bibliothèque " + bbl_nom()
        infos.pdrNew(msg)
    else:
        if nb_livres_total() > 0:
            memo_pos_livre_aba()
            save_num4c_actuel(lv_num4c())
        infos.ini_set("aba_bbl_actuelle", cle_biblio)
        reprendre('pas_lancement des audiolivres')

def bbl_audio_cite_classiques():
    bbl_changer("audio_cite_classiques")

def bbl_audio_cite_contemporains():
    bbl_changer("audio_cite_contemporains")

def bbl_cle_usb_anarchie():
    """ lire les livres de la clé usb """
    bbl_changer("cle_usb_anarchie")

def bbl_librivoxde():
    bbl_changer("librivoxde")

def bbl_librivoxen():
    bbl_changer("librivoxen")

def bbl_librivoxfr():
    bbl_changer("librivoxfr")

def bbl_litterature():
    bbl_changer("litteaudio")

def bbl_perso_zip():
    """ lire les livres en ligne perso """
    # procéder pour cette bibliothèque comme pour litterature audio, librivox ...
    # en créant le tableau all_lvr[] adéquat
    bbl_changer("perso_zip")

def bbl_loyalbooksfr():
    bbl_changer("loyalbooksfr")

def bib_prec():
    """ aller à la bibliothèque précédente
    """
    # "loyalbooksfr"
    # "divers_src"
    # "litteaudio"
    # "librivoxfr"
    # "librivoxen"
    # "librivoxde"
    # cfg.bbl_PERSO_ZIP
    # cfg.bbl_CLE_USB_ORDONNEE
    # cfg.bbl_CLE_USB_ANARCHIE
    # "audio_cite_contemporains"
    # "audio_cite_classiques"
    ba = bbl_actu()
    nba = ""
    if ba == "loyalbooksfr":
        nba = "divers_src"
    if ba == "divers_src":
        nba = "litteaudio"
    elif ba == "litteaudio":
        nba = "librivoxfr"
    elif ba == "librivoxfr":
        nba = "librivoxen"
    elif ba == "librivoxen":
        nba = "librivoxde"
    elif ba == "librivoxde":
        nba = cfg.bbl_PERSO_ZIP
    elif ba == cfg.bbl_PERSO_ZIP:
        nba = cfg.bbl_CLE_USB_ORDONNEE
    elif ba == cfg.bbl_CLE_USB_ORDONNEE:
        nba = cfg.bbl_CLE_USB_ANARCHIE
    elif ba == cfg.bbl_CLE_USB_ANARCHIE:
        nba = "audio_cite_contemporains"
    elif ba == "audio_cite_contemporains":
        nba = "audio_cite_classiques"
    elif ba == "audio_cite_classiques":
        nba = "loyalbooksfr"

    if nba == "":
        nba = "audio_cite_classiques"
    bbl_changer(nba)

def bib_suiv():
    """ aller à la bibliothèque suivante
    """
    # "audio_cite_classiques"
    # "audio_cite_contemporains"
    # cfg.bbl_CLE_USB_ANARCHIE
    # cfg.bbl_CLE_USB_ORDONNEE
    # cfg.bbl_PERSO_ZIP
    # "librivoxde"
    # "librivoxen"
    # "librivoxfr"
    # "litteaudio"
    # "divers_src"
    # "loyalbooksfr"
    ba = bbl_actu()
    nba = ""
    if ba == "audio_cite_classiques":
        nba = "audio_cite_contemporains"
    elif ba == "audio_cite_contemporains":
        nba = cfg.bbl_CLE_USB_ANARCHIE
    elif ba == cfg.bbl_CLE_USB_ANARCHIE:
        nba = cfg.bbl_CLE_USB_ORDONNEE
    elif ba == cfg.bbl_CLE_USB_ORDONNEE:
        nba = cfg.bbl_PERSO_ZIP
    elif ba == cfg.bbl_PERSO_ZIP:
        nba = "librivoxde"
    elif ba == "librivoxde":
        nba = "librivoxen"
    elif ba == "librivoxen":
        nba = "librivoxfr"
    elif ba == "librivoxfr":
        nba = "litteaudio"
    elif ba == "litteaudio":
        nba = "divers_src"
    elif ba == "divers_src":
        nba = "loyalbooksfr"
    elif ba == "loyalbooksfr":
        nba = "audio_cite_classiques"

    if nba == "":
        nba = "audio_cite_classiques"
    bbl_changer(nba)


# gestion des favoris et des bannis
def fav_add():
    """ ajouter le livre aux favoris de la bibliothèque en cours """
    favoris_bannis(lv_num4c(), "add_fav")


def fav_del():
    """ supprime le livre des favoris de la bibliothèque en cours """
    favoris_bannis(lv_num4c(), "del_fav")


def ban_add():
    """ ajouter le livre aux bannis de la bibliothèque en cours """
    favoris_bannis(lv_num4c(), "add_ban")


def ban_del():
    """ supprime le livre des bannis de la bibliothèque en cours """
    favoris_bannis(lv_num4c(), "del_ban")


def fav_menu():
    """ lance le menu vocal fav_ban """
    explic = "utiliser les touches 1 à {} pour gérer vos favoris, " + \
        " zéro pour sortir de ce menu, " + \
        "Entrée pour valider la commande entendue"
    commun.menu_vocal("aba_fav_ban", "favoris et indésirables", explic)


def favoris_bannis(num4c, operation):
    """ gère les favoris et les bannis
        qui sont en fait deux listes de mémoire
    """
    SEP = ","
    def add_ban_fav(num4c, ban_fav):
        """ ajoute num4c aux favoris ou bannis
            renvoie True si ajout, False sinon """
        v_ini = "aba_" + ban_fav + "_" + bbl_actu()
        str_fav_actu = infos.ini_get(v_ini, "")
        fav_actu = str_fav_actu.strip().split(SEP)
        if str_fav_actu == "":
            infos.ini_set(v_ini, num4c)  # pour éviter erreur aba_favoris_litteaudio=,0150
        elif not num4c in fav_actu:
            fav_actu.append(num4c)
            fav_actu.sort()     # https://docs.python.org/3/howto/sorting.html
            if len(fav_actu) == 0:
                infos.ini_set(v_ini, num4c)
            else:
                infos.ini_set(v_ini, SEP.join(fav_actu))
        else:
            msg = "livre déjà présent dans les " + ban_fav
            infos.pdrNew(msg)
            return False
        # 3 lignes communes au if et 1er elif
        msg = "livre " + zero_off(num4c) + " ajouté aux " + ban_fav
        infos.pdrNew(msg)
        return True


    def del_ban_fav(num4c, ban_fav):
        """ supprime num4c des favoris ou bannis
            renvoie True si suppression, False sinon """
        v_ini = "aba_" + ban_fav + "_" + bbl_actu()
        str_ban_actu = infos.ini_get(v_ini, "")
        ban_actu = str_ban_actu.strip().split(",")
        if str_ban_actu == "":
            pass    # on ne peut pas enlever qch de rien
                    # éviter ce type d'erreur, aba_favoris_litteaudio=,0150
        elif num4c in ban_actu:
            ban_actu.remove(num4c)
            if len(ban_actu) == 0:
                infos.ini_set(v_ini, '')
            else:
                infos.ini_set(v_ini, SEP.join(ban_actu))
            msg = "livre " + zero_off(num4c) + " supprimé des " + ban_fav
            infos.pdrNew(msg)
            return True
        else:
            msg = "livre déjà absent des " + ban_fav
            infos.pdrNew(msg)
            return False


    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    if num4c is None:
        return False

    if operation == "add_fav":
        return add_ban_fav(num4c, "favoris")
    elif operation == "del_fav":
        return del_ban_fav(num4c, "favoris")
    elif operation == "add_ban":
        return add_ban_fav(num4c, "bannis")
    elif operation == "del_ban":
        return del_ban_fav(num4c, "bannis")


def aba_nfo1():
    """ message sonore : vous écoutez "Des hommes sans femmes", de "Haruki Murakami"
    """
    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    bbll = bbl_langue()
    if mpc.is_playing():
        if bbll == cfg.LG_FRANCAIS:
            masque = "vous écoutez le livre {}, rubrique {}, {}, de {}"
        elif bbll == cfg.LG_ANGLAIS:
            masque = "you are listening to the book {}, domain {}, {}, the author is {}"
        elif bbll == cfg.LG_ALLEMAND:
            masque = "Sie hören das Buch {}, im Besitz {}, {}, der Autor ist {}"
    else:
        if bbll == cfg.LG_FRANCAIS:
            masque = "vous écoutez LE livre {}, rubrique {}, {}, de {}"
        elif bbll == cfg.LG_ANGLAIS:
            masque = "the current bOOk is {}, in the area {}, {}, the author is {}"
        elif bbll == cfg.LG_ALLEMAND:
            masque = "das aktuelle BUCh ist Nummer {}, im Besitz {}, {}, der Autor ist {}"

    annonce = masque.format(zero_off(lv_num4c()), lv_genre(), lv_titre(), lv_auteur())
    infos.pdrNew(annonce)


def aba_nfo2():
    """ message sonore :
        vous êtes à 2 minutes 43
        TODO vous êtes à 2 minutes 43, sur la piste 3
    """
    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    duree_conv = duree_conviviale(str(lv_duree()))
    bbll = bbl_langue()
    if duree_conv == "":
        duree2 = ""
    else:
        if bbll == cfg.LG_FRANCAIS:
            duree2 = ", la durée totale est de " + duree_conv
        elif bbll == cfg.LG_ANGLAIS:
            duree2 = ", total duration is " + duree_conv
        elif bbll == cfg.LG_ALLEMAND:
            duree2 = ", im Ganzen dauert es " + duree_conv
    p = mpc.status_progression()[0]
    q = p.split(":")

    # numéro de piste
    # playlist_position
    # playlist_longueur
    piste = str(mpc.playlist_position())
    piste_total = str(mpc.playlist_longueur())

    mpc.ferme_ton_clapet()
    if len(q) == 2:
        if bbll == cfg.LG_FRANCAIS:
            msg = "vous êtes à " + q[0] + " minutes " + q[1] + duree2
        elif bbll == cfg.LG_ANGLAIS:
            msg = "you are at " + q[0] + " minutes " + q[1] + duree2
        elif bbll == cfg.LG_ALLEMAND:
            msg = "Sie sind um " + q[0] + " Minuten " + q[1] + duree2
        infos.pdrNew(msg)

    if bbll == cfg.LG_FRANCAIS:
        msg = "chapitre " + piste + " sur " + piste_total
    elif bbll == cfg.LG_ANGLAIS:
        msg = "chapter " + piste + " from " + piste_total
    elif bbll == cfg.LG_ALLEMAND:
        msg = "Kapitel " + piste + " von " + piste_total
    infos.pdrNew(msg)


def aba_nfo3():
    """ annonce le nombre de livres (pour le filtre actuel), le lecteur
    """
    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    nb_livres = str(nb_livres_total())
    if lv_poids().strip() == "":
        # espace à donner si livre téléchargé,
        # ou bien redonner la longueur en temps
        espace_mo = ""
    else:
        espace_mo = "il occupe un espace de " + commun.entier(lv_poids()) + " méga octets"
    presents_bannis = "" # TODO
    msg = "bibliothèque " + bbl_nom() + ", " + \
        "vous ACTION un des " + nb_livres + " livres" + presents_bannis + " , " + \
        "ce livre est lu par " + lv_lecteur() + ", " + espace_mo
    if mpc.is_playing():
        msg = msg.replace("ACTION", "écoutez")
    else:
        msg = msg.replace("ACTION", "êtes sur")
    infos.pdrNew(msg)


def bbl_pos_livres():
    """ renvoie le fichier (nom complet) qui mémorise
        la position et progression des livres entamés d'une bibliothèque
    """
    return "/m/memo/aba/pos_lvr_{}.txt".format(bbl_actu())


def bbl_folder(cle_biblio = None):
    """ renvoie le dossier où sont téléchargés les audiolivres
        de la bibliothèque online actuelle :
        /m/music/aba/audio_cite_classiques
        /m/music/aba/litteaudio
    """
    if cle_biblio is None:
        cle_biblio = bbl_actu()
    mpd_dossier = cfg.M_MUSIC + "/aba/" + cle_biblio
    return mpd_dossier


def bbl_sub_folder():
    """ renvoie le dossier relatif où sont téléchargés les audiolivres
        de la bibliothèque online actuelle :
        aba/audio_cite_classiques
        aba/litteaudio
    """
    sub_dossier = "aba/" + bbl_actu()
    return sub_dossier


def bbl_langue():
    """ renvoie la langue de la bibliothèque en cours
    """
    return cfg.BIB_DISPOS[bbl_actu()]["langue"]


def bbl_nom(cle = ''):
    """ renvoie le nom de la bibliothèque en cours
    """
    if cle == '':
        return cfg.BIB_DISPOS[bbl_actu()]["nom"]
    else:
        return cfg.BIB_DISPOS[cle]["nom"]


def bbl_mpc_update():
    """ màj mpd audiobooks biblio actuelle """
    cmd_update = "mpc -q update " + "aba/" + bbl_actu()
    retour_update = mpc.sub_proc(cmd_update)


def bbl_actu():
    """ renvoie la clé de la bibliothèque online actuelle """
    la_biblio = infos.ini_get("aba_bbl_actuelle", cfg.DEFAUT_BIBLIO)
    alogger("la biblio = " + la_biblio)
    alogger("cfg.BIB_DISPOS.keys() = ")
    alogger(str(cfg.BIB_DISPOS.keys()))
    if la_biblio in cfg.BIB_DISPOS.keys():
        return la_biblio
    else:
        # la clé peut être inexistante, si développeur
        # a modifié ou supprimé la clé mémorisée
        cles = list(cfg.BIB_DISPOS.keys())
        return cles[0]


def build_selektion(list_champs_n = None):
    """ construit les tableaux globaux
        sel_lvr[] et sel_n2i{} depuis all_lvr[],

        sel_lvr[] est une liste d'objets livre
        C'est la sélection des livres en cours,
        c'est une partie du tableau complet all_lvr[]
        sel_n2i{} fait pointer un champ "n" vers l'index de sel_lvr[]

        list_champs_n : tableau de champs "n" d'un objet livre,
                        anciennement appelé num4c
    """
    global sel_lvr
    global sel_n2i

    # raz tableaux globaux ...
    sel_lvr = []
    sel_n2i = {}
    nb_mp3 = 0
    nb_zip = 0
    nb_undef = 0

    if list_champs_n is None:
        # parcours des all_lvr[]
        for un_livre in all_lvr:
            l_type_url = un_livre["type_url"]
            if l_type_url == "mp3":
                nb_mp3 += 1
            elif l_type_url == "zip":
                nb_zip += 1
            else:
                nb_undef += 1
        sel_lvr = list(all_lvr)  # duplication
        indice_ze = 0
        for un_livre in sel_lvr:
            sel_n2i[ un_livre["n"] ] = indice_ze
            indice_ze += 1
        infos.log("{} livres, dont mp3/zip/undef {}/{}/{}".format(nb_livres_total(), nb_mp3, nb_zip, nb_undef))

    else:
        # on construit sel_lvr[] et sel_n2i[]
        # depuis all_lvr ET list_champs_n
        # ex : pour les livres présents seuls
        for un_livre in all_lvr:
            if un_livre["n"] in list_champs_n:
                sel_lvr.append(un_livre)
        indice_ze = 0
        for un_livre in sel_lvr:
            sel_n2i[ un_livre["n"] ] = indice_ze
            indice_ze += 1
        infos.m_g("{} livres".format(nb_livres_total()))


def charger_all_lvr(biblio = None):
    """ crée le tableau all_lvr[]
        d'une bibliothèque depuis le fichier catalog.txt correspondant
        (ex : /opt/jk2019/tout/ini/aba/audiocite_classiques/catalog.txt)

        all_lvr[] est un tableau d'objets livres

        TODO : devra vérifier des champs essentiels :

        n=  OBLIGATOIRE
        y=  facultatif
        d=  facultatif
        p=  facultatif
        l=  facultatif
        a=  OBLIGATOIRE
        t=  OBLIGATOIRE
        u=  OBLIGATOIRE, liste d'urls de .mp3 ou .zip,
            ou d'une url commençant par http et
            ne se finissant pas par zip ou mp3
        f=  facultatif
        x=  facultatif

        renvoie True si succès, False sinon
    """
    if biblio is None:
        biblio = bbl_actu()
    global all_lvr
    all_lvr = []
    num_ligne = -1

    cata_type = cfg.BIB_DISPOS[biblio]["cata_type"]
    if cata_type == "dynamique":
        infos.m_g('catalogue dynamic')
        return False

    catalog = cfg.BIB_DISPOS[biblio]["catalogue"] + "/catalog.txt"
    if commun.file_exists(catalog) == False:
        infos.g_m("Le fichier {} n'existe pas".format(catalog))
        return False

    t_auteurs = {}
    t_lecteurs = {}
    t_genres = {}
    t_url = {}
    t_duree = {}
    t_poids = {}
    t_titre = {}

    with open(catalog, "r") as f:
        for ligne in f:
            if ligne.startswith("#"):
                continue
            elements = ligne.split("|")
            num_ligne += 1
            url_media = elements[7].split("=", 1)[1].strip()
            # on suppose ici que tous les éléments d'une liste d'urls
            # sont de la même nature que
            if url_media.endswith(".mp3"):
                type_url = "mp3"
            elif url_media.endswith(".zip"):
                type_url = "zip"
            elif url_media.startswith("CE_TOME"):
                type_url = "CE_TOME_la.com"
            elif url_media.startswith("PARTIES"):
                type_url = "PARTIES_la.com"
            elif url_media == "":
                type_url = "url_vide"
                print("url_vide : " + \
                    elements[0].split("=", 1)[1].strip() + ", " + \
                    elements[8].split("=", 1)[1].strip())
            else: # ex : 1347, https://www.audiocite.net/download.php?id=4491 (qui est un mp3 dans ce cas)
                type_url = "undef"

            num4c = elements[0].split("=", 1)[1].strip()
            duree = elements[2].split("=", 1)[1].strip()

            lecteur = elements[4].split("=", 1)[1].strip()
            auteur = elements[5].split("=", 1)[1].strip()
            genre = elements[1].split("=", 1)[1].strip()
            url_media1 = url_media.replace(" ", "%20")      # ex : 0186
            arr_url_media = url_media1.split("+")   # nouveauté avec litteratureaudio.com
            titre = elements[6].split("=", 1)[1].strip()
            poids = elements[3].split("=", 1)[1].strip()
            poids = poids.replace(",", ".")
            # tableau asso d'un livre
            # TODO : charger_element() permettra de charger chaque champ,
            #        quel que soit l'ordre d'apparition sur la ligne
            #        nydplatuf sera du passé
            #        on pourra même ajouter d'autres champs,
            #        et ne même pas faire figurer les champs optionnels :
            #        x = "ECOUTER_EXTRAIT"
            #        b = "publie_le"

            t_auteurs[auteur] = 0
            t_genres[genre] = 0
            t_lecteurs[lecteur] = 0
            t_url[url_media[0]] = 0
            t_duree[duree] = 0
            t_poids[poids] = 0
            t_titre[titre] = 0

            if len(elements) > 9:
                if elements[9].strip() == "":
                    extrait = ""
                else:
                    xx = elements[9].split("=", 1)
                    extrait = xx[1].strip()
            else:
                extrait = ""

            un_livre = {
                "n": num4c    , # numéro à 4 chiffres
                "y": genre    , # type
                "d": duree    , # durée
                "p": poids    , # poids (string : "5.78", "12", ...)
                "l": lecteur  , # lecteur
                "a": auteur   ,  # auteur
                "t": titre,  # titre
                "u": arr_url_media, # url mp3 ou zip
                "f": elements[8].split("=", 1)[1].strip(),  # url fiche
                "x": extrait,  # extrait
                "type_url": type_url,  # type url : mp3, zip
            }
            all_lvr.append(un_livre)

    # tables annexes, à numéroter, puis réindexer catalog.txt
    # décommenter les lignes ci-dessous
    # pour vérifier un catalogue :
    # - auteurs en doublons du genre "Victor Hugo" et "Victor hugo" ou "Victor  Hugo"
    # - media_url mal formé : ne commence pas par http, par exemple
    # ~ creer_nouvelles_tables(
        # ~ biblio,
        # ~ all_lvr,
        # ~ list(t_auteurs.keys()),
        # ~ list(t_genres.keys()),
        # ~ list(t_lecteurs.keys()),
        # ~ list(t_url.keys()),
        # ~ list(t_duree.keys()),
        # ~ list(t_poids.keys()),
        # ~ list(t_titre.keys()),
        # ~ )
    return True


def choisir_livre():
    """ accès direct à un livre depuis son num4c (de 1 à 4 chiffres demandés) """
    # à l'avenir, le user pourra utiliser les favoris également, plus pratiques
    memo_pos_livre_aba()
    msg = "Entrez un numéro de livre " + \
        " de 1 à " + str(nb_livres_total()) + ", " + \
        " puis appuyez sur Entrée"
    infos.pdrNew(msg)
    try:
        choix_user = input("")
        global sel_n2i
        n_0000 = zero_on(choix_user)
        if n_0000 in sel_n2i.keys():
            save_num4c_actuel(n_0000)
            lire_livre_actuel("annoncer")
        else:
            msg = "Vous avez choisi un nombre hors limites. " + \
                "Abandon de l'opération."
            infos.pdrNew(msg)
    except:
        msg = "Veuillez choisir un nombre de 1 à " + str(nb_livres_total())
        infos.pdrNew(msg)


def devDirSize(start_path):
    """ taille totale du dossier en méga octets """
    # https://stackoverflow.com/questions/1392413/calculating-a-directorys-size-using-python
    import os
    total_size = 0
    for dirpath, dirnames, filenames in os.walk(start_path):
        for f in filenames:
            fp = os.path.join(dirpath, f)
            # skip if it is symbolic link
            if not os.path.islink(fp):
                total_size += os.path.getsize(fp)
    return total_size / 1000000


def dezipper_del(fichier, dossier, prefixeInfo = ""):
    """ va dans le dossier dossier,
        dézippe avec 7z le fichier fichier,
        supprime le fichier fichier
        Renvoie True si tout ok, False si décompression échouée
    """
    # dézipper (cf. doc_04)
    pfx = prefixeInfo

    cmd = "cd " + dossier + "; 7z e -y " + fichier
    infos.g_m(pfx + "dézipper {} 1/4. {}".format(fichier, cmd))
    unzip_please = mpc.sub_proc(cmd)
    # 7z (cf. doc_07)
    if unzip_please.find("Everything is Ok"):
        infos.g_m(pfx + "dézipper {} 2/4. Everything is Ok".format(fichier))
    else:
        infos.g_m(pfx + "dézipper {} 2/4. problème dézippage 7z e".format(fichier))
        return False
    cmd = "cd " + dossier + "; rm " + fichier
    infos.g_m(pfx + "dézipper {} 3/4. {}".format(fichier, cmd))

    remove_zip = mpc.sub_proc(cmd)
    infos.g_m(pfx + "dézipper {} 4/4. {}".format(fichier, remove_zip))

    return True


def dl_files_Now(poids, num4c, list_urls, dossier, fichier, extension):
    """ téléchargements simple ou multiple
        list_urls est une liste à 1 ou plusieurs éléments
        seul le téléchargement a lieu ici, zip ou mp3,
        le dézippage se fait dans dl_media
    """
    if len(list_urls) == 1:
        # un seul fichier à télécharger
        r = dl_fileNow_one(poids, num4c, list_urls[0], dossier, fichier, extension)
        if commun.file_exists("/m/quitter_download"):
            return 1
        else:
            return r
    else:
        # ATTENTION : le bloc ci-dessous est identique
        #             au bloc similaire de dl_files_Now
        #             modifier l'un nécessite de modifier l'autre
        suffixe = 1000
        for une_url in list_urls:
            suffixe += 1
            if suffixe == 1001:
                fichier = num4c
            else:
                fichier = num4c + "_" + str(suffixe)[-3:]
            infos.log("dl files Now, fichier N° {} = {}".format(suffixe, fichier))
            infos.log("dl files Now, url = " + une_url)
            retour_un_fichier = dl_fileNow_one(poids, num4c, une_url, dossier, fichier, extension)
            if commun.file_exists("/m/quitter_download"):
                return False
            if retour_un_fichier == False:
                infos.m_g("ERR download url " + une_url)
                return False

    return True


def dl_fileNow_one(poids, num4c, une_url, dossier, fichier, extension):
    """ télécharge un fichier """
    # Entrée :
    # une_url   : liste d'urls http à télécharger
    # dossier   : dossier destination
    # num4c     : désigne le livre et le radical du fichier à créer
    #
    # Sortie :
    # cette fonction renvoie True si fichier téléchargé, sinon False

    # télécharger et mesurer temps d'exécution (pour estimer vitesse)
    if poids != "":
        taille = poids
        if taille == "0":
            taille = "1"
        dire_poids = " de " + str(int(float(taille))) + " méga octets"
    else:
        dire_poids = ""

    nfo_pfx = "dl fileNow one [" + num4c + "] "
    from time import time
    download_start = time()

    prononcer = ""
    # créer dossier destination
    # et télécharger avec wget
    # entourer l'url de guillemets
    # NOK wget -O 1661.mp3 https://archive.org/download/Lenfant_Maupassant/L'enfant.mp3
    # OK  wget -O 1661.mp3 "https://archive.org/download/Lenfant_Maupassant/L'enfant.mp3"
    masque = "mkdir {0}; wget --timeout=20 --tries=2 -O {0}/{1}.{2} \"{3}\" || echo nok"
    cmd = masque.format(dossier, fichier, extension, une_url)
    infos.g_m(nfo_pfx + cmd)
    retour_cmd = mpc.sub_proc(cmd)
    # killé par commun.quitter()
    infos.g_m("wget killé ou terminé normalement")
    if commun.file_exists("/m/quitter_download"):
        return False
    # TODO : vérifier la présence du fichier après téléchargement
    if retour_cmd == "nok":
        # le téléchargement a échoué
        return False

    # infos vitesse
    if dire_poids != "":
        duree = int(time() - download_start)
        taille = float(str(taille))
        try:
            vitesse = int(taille / duree * 1000)
            infos.g_m(nfo_pfx + "1. durée    : {} secondes, vitesse : {} Ko/s".format(duree, vitesse))
        except ZeroDivisionError:
            infos.g_m("division par zéro")
            infos.g_m("taille = " + str(taille))
            infos.g_m("durée  = " + str(duree))
            infos.g_m(nfo_pfx + "1. durée    : pb de mesure")
    # vérification du résultat
    cmd = "ls -lh " + dossier + "/" + num4c + "." + extension
    infos.g_m(nfo_pfx + "2. vérif    : " + cmd)
    retour_cmd = mpc.sub_proc(cmd)
    infos.g_m(nfo_pfx + "3. résultat : " + retour_cmd)
    cmd_ls = "[ -f {}/{}.{} ] && echo dl_ok".format(dossier, fichier, extension)
    retour_ls = mpc.sub_proc(cmd_ls)
    infos.g_m(nfo_pfx + "4.          : " + retour_ls)

    if retour_ls == "dl_ok":
        return True
    else:
        return False


def dl_media(bib_index, num4c, poids, duree, list_urls, type_url):
    """ se charge de télécharger le mp3, le zip, ou le undef
        de le placer où il faut,
        et de mettre à jour mpd
    """
    # cf. doc_09, pour exemples num4c à tester

    # infos user
    # ~ msg = "dl media [], ze_idx = " + str(ze_idx) + ", num4c = " + num4c
    if len(list_urls) == 1:
        msg1 = "un seul fichier"
    else:
        msg1 = str(len(list_urls)) + " fichiers"
    infos.g_m(msg1)

    # vaut mieux nettoyer complètement ce dossier
    # avant de travailler plus bas
    temp_dir_dlMedia = "/m/temp_dl_media"
    cmd = "rm -rf " + temp_dir_dlMedia
    purger = mpc.sub_proc(cmd)

    # 1. télécharger, et dézipper si zip, vers dossier temporaire
    if type_url == "mp3":
        r = dl_files_Now( poids, num4c, list_urls, temp_dir_dlMedia, num4c, "mp3")
        if commun.file_exists("/m/quitter_download"):
            return 1
        if not r:
            infos.m_g("ERR dl media, dl files Now mp3")
    elif type_url == "zip":
        r = dl_files_Now( poids, num4c, list_urls, temp_dir_dlMedia, num4c, "zip")
        if commun.file_exists("/m/quitter_download"):
            return 1
        if not r:
            infos.m_g("ERR dl media, dl files Now zip")
        # dézipper un ou plusieurs fichiers
        # les noms sont générés de la même façon que dans dl_files_Now()
        # numéroter sur 2, ou mieux sur 3 chiffres
        # pour éviter un problème d'ordre (le fichier
        # 0123_20.mp3 viendra entre le 0123_2.mp3 et le 0123_3.mp3)

        # ATTENTION : le bloc ci-dessous est identique
        #             au bloc similaire de dl_files_Now
        #             modifier l'un nécessite de modifier l'autre
        suffixe = 1000
        for une_url in list_urls:
            suffixe += 1
            if suffixe == 1001:
                fichier = num4c
            else:
                fichier = num4c + "_" + str(suffixe)[-3:]
            r2 = dezipper_del(fichier + ".zip", temp_dir_dlMedia)
            if r2 == False:
                break
    elif type_url == "undef":
        infos.m_g("undef, wget --spider pour connaître type")
        infos.m_g("undef, wget --spider pour connaître type")

        # 1. déterminer le type de fichier avec wget --spider
        cmd = "wget --tries=5 --spider {} | grep Taille".format(list_urls[0])
        infos.g_m("wget SPIDER : wget --tries=5 --spider {} 2>&1 | grep Taille".format(list_urls[0]))
        infos.g_m("wget SPIDER : wget --tries=5 --spider {} 2>&1 | grep Taille".format(list_urls[0]))
        r_spider = mpc.sub_proc(cmd)
        infos.g_m("wget SPIDER : {}".format(r_spider))
        infos.g_m("wget SPIDER : {}".format(r_spider))
        # 2. lancer dl_media en récursif en forçant mp3 ou zip
        if r_spider.find("application/zip") > 0:
            infos.g_m("undef, zip")
            infos.g_m("undef, zip")
            r = dl_media(bib_index, num4c, poids, duree, list_urls, "zip")
        elif r_spider.find("audio/mpeg") > 0:
            infos.g_m("undef, mp3")
            infos.g_m("undef, mp3")
            r = dl_media(bib_index, num4c, poids, duree, list_urls, "mp3")
        else:
            infos.g_m("undef, UNKNOWN")
            infos.g_m("undef, UNKNOWN")
            # signaler un problème de téléchargement :
            # dans fichier .ini, ou mémoire utilisateur
            # wget ne trouve pas le fichier dans le temps imparti
            return False
        return r


    if commun.file_exists("/m/quitter_download"):
        return 1

    # 2. déplacer les mp3 vers dossier num4c
    if type_url in [ "mp3", "zip" ]:
        dir_dest = "/m/music/aba/" + bib_index + "/" + num4c

        if not commun.dir_exists(dir_dest):
            r_creer_dossier = commun.make_dir(dir_dest)

        if commun.dir_exists(dir_dest):
            for audio in [ "mp3", "m4a", "wav" ]:
                cmd = "mv {}/*{} {}".format(temp_dir_dlMedia, audio, dir_dest)
                infos.log("dl media déplacer : " + cmd)
                r_deplacement = mpc.sub_proc(cmd)
        else:
            infos.m_g("ERR création impossible dossier \"" + dir_dest + "\"")

    # TOUDOU_optionnel émettre un bip sonore spécial quand un téléchargement est terminé
    # TOUDOU_optionnel émettre un bip sonore encore spécial quand tous les téléchargements sont terminés
    return r


def DOKU_enregistrer_dans_tableur_html():
    """ enregistre dans un fichier html
        le contenu de all_lvr[]
        et calculent des stats : total Mo, toital durée,
        nb livres, nb auteurs, nb_lecteurs, ...
    """

    def create_auteurs(auteurs, quoi):
        """ renvoie corps de table des auteurs à deux colonnes
            1. auteur
            2. nb de livres
        """
        # table basique, sans tri
        corps_auteurs = ""
        cles = list(auteurs.keys())
        cles.sort()
        n = 0
        for i in cles:
            # i = nom auteur
            # auteurs[i] = nombre de livres
            n += 1
            # ligne = tr_td([ str(n) + "/" + str(len(cles)), i, auteurs[i] ])
            ligne = tr_td([ n, i, auteurs[i] ])
            corps_auteurs = corps_auteurs + "\n" + ligne
        tbl_auteurs_noms = '<a href="#menu" name="' + quoi + '">' + \
        """<h2 style="text-align: center;">""" + quoi + """</h2></a>
    <table class="table_touches">
        <caption>""" + quoi + """ classés par ordre alpha</a></caption>
        """ + \
        entete_table("auteurs") + \
        corps_auteurs + \
        """
    </table>
        """

        # table triée sur nombre de livres décroissants
        # https://docs.python.org/3/howto/sorting.html
        # cf. Operator Module Functions
        from operator import itemgetter, attrgetter
        t = []
        for i in auteurs.keys():
            auteur = i
            nb_livres = auteurs[i]
            t.append( (auteur, int(nb_livres)) )
        s = sorted(t, key=itemgetter(0))                  # tri sur auteur (colonne 0)
        x = sorted(s, key=itemgetter(1), reverse=True)    # tri sur nombre de livres (colonne 1)
        sorted(x, key=itemgetter(0))                      # tri sur auteur (colonne 0)
        # for i in x:
            # print("{}, {}".format(i[0], i[1]))

        corps_auteurs = ""
        n = 0
        for i in x:
            n += 1
            ligne = tr_td([ str(n) + "/" + str(len(x)), i[0], i[1] ])
            # ligne = tr_td([ str(n) + "/" + str(len(cles)), i, auteurs[i] ])
            corps_auteurs = corps_auteurs + "\n" + ligne
        tbl_auteurs_nb_livres = '<a href="#menu" name="' + quoi + '">' + \
        """<h2 style="text-align: center;">""" + quoi + """</h2></a>
    <table class="table_touches">
        <caption>""" + quoi + """ classés par ordre alpha</a></caption>
        """ + \
        entete_table(quoi) + \
        corps_auteurs + \
        """
    </table>
        """

        xxx = "<center><table>" + \
            "<tr>" + \
            "<td>" + tbl_auteurs_noms + "</td>" + \
            "<td>" + tbl_auteurs_nb_livres + "</td>" + \
            "</tr>" + \
            "</table></center>"

        return xxx

    def create_resume():
        """ tableau résumé
        """
        table_resume = ""
        taille_totale_mo = 0
        duree_totale_minutes = 0
        genres__ = {}     # champ "y"
        lecteurs = {}   # champ l
        auteurs_ = {}    # champ a
        n = 0
        max_poids = 1
        min_poids = 1000
        max_duree = 1       # minute
        min_duree = 10000    # 10000 minutes
        nb_cases_poids_vides = 0
        nb_cases_durees_vides = 0
        nb_poids = 0
        nb_duree = 0
        for lvr in all_lvr:     # init tableaux
            genres__[lvr["y"]] = 0
            auteurs_[lvr["a"]] = 0
            lecteurs[lvr["l"]] = 0
        for lvr in all_lvr:
            genres__[lvr["y"]] += 1
            auteurs_[lvr["a"]] += 1
            lecteurs[lvr["l"]] += 1
            if lvr["p"].strip() == "":
                nb_cases_poids_vides += 1
            else:
                nb_poids += 1
                taille_totale_mo += float(lvr["p"])
            if lvr["d"].strip() == "":
                nb_cases_durees_vides += 1
            else:
                nb_duree += 1
                duree_totale_minutes += duree_minutes(lvr["d"])
            n += 1

        # taille
        if nb_poids > 0:
            # taille totale
            # str_taille_totale = \
                # '{:,}'.format(int(taille_totale_mo)).replace(",", " ") + \
                # " Mo"
            str_taille_totale = infos.cvt_taille_mo2go_mo(taille_totale_mo)

            # taille moyenne
            taille_moyenne_mo = taille_totale_mo / nb_poids
            # str_taille_moyenne_mo = \
                # '{:,}'.format(int(taille_moyenne_mo)).replace(",", " ") + \
                # " Mo"
            str_taille_moyenne_mo = infos.cvt_taille_mo2go_mo(taille_moyenne_mo)
        else:
            str_taille_totale = ""
            str_taille_moyenne_mo = "poids NC"

        # durée
        if nb_duree > 0:
            # durée totale
            # str_duree_totale = \
                # '{:,}'.format(int(duree_totale_minutes)).replace(",", " ") + \
                # "<br>" + infos.cvt_duree_minutes2jhm(duree_totale_minutes)
            str_duree_totale = infos.cvt_duree_minutes2jhm(duree_totale_minutes)
            # durée moyenne
            duree_moyenne_minutes = duree_totale_minutes / nb_duree
            str_duree_moyenne_minutes = \
                '{:,}'.format(int(duree_moyenne_minutes)).replace(",", " ") + \
                " mn"
        else:
            str_duree_totale = ""
            str_duree_moyenne_minutes = "durées NC"
        remarque_poids = "Poids max : ..., min : ..."
        remarque_duree = "Durée max : ..., min : ..."

        # table résumé
        table_resume = """
    <a href="#menu" name="resume"><h2 style="text-align: center;">Résumé</h2></a>
    <table class="table_touches">
        <caption>Statistik</a></caption>
        <tr>
            <th>Champ</th>
            <th>Valeur</th>
            <th>Remarque</th>
        </tr>
        """ + \
        tr_td(["Nombre de livres", '{:,}'.format(n).replace(",", " "), ""]) + \
        tr_td(["Taille totale"   , str_taille_totale, str(nb_cases_poids_vides) + " case(s) vide(s)"]) + \
        tr_td(["Taille moyenne"  , str_taille_moyenne_mo, remarque_poids]) + \
        tr_td(["Durée totale"    , str_duree_totale, str(nb_cases_durees_vides) + " case(s) vide(s)"]) + \
        tr_td(["Durée moyenne"   , str_duree_moyenne_minutes, remarque_duree]) + \
        """
    </table>
        """
        return table_resume, auteurs_, genres__, lecteurs

    def create_tous_les_livres():
        """
        """
        global all_lvr
        corps_table = ""
        n = 0
        for lvr in all_lvr:
            ligne_tr = \
                "<tr>" + \
                    td(n) + \
                    td(lvr["n"]) + \
                    td(lvr["y"]) + \
                    td(lvr["d"]) + \
                    td(lvr["p"]) + \
                    td(lvr["l"]) + \
                    td(lvr["a"]) + \
                    td(lvr["t"]) + \
                    td(lvr["x"][:40] + "...") + \
                "</tr>" + "\n"
            corps_table += ligne_tr
            n += 1

        table_all_lvr = """
                <a href="#menu" name="tous_les_livres"><h2 style="text-align: center;">Tous les livres (all_lvr)</h2></a>
                <table class="table_touches">
                    <caption>Documentation http://gangand./pp/projets/xavbox/adm/docs</a></caption>
        """ + entete_table("all_lvr") + corps_table + "</table>"
        return table_all_lvr

    def duree_minutes(d):
        t = d.split(":")
        return int(t[0]) * 60 + int(t[1])

    def entete_table(quoi):
        """
        """
        if quoi == "all_lvr":
            return """
                    <tr>
                        <th>#</th>
                        <th>num4c</th>
                        <th>Genre</th>
                        <th>Durée</th>
                        <th>Poids</th>
                        <th>Lecteur</th>
                        <th>Auteur</th>
                        <th>Titre</th>
                        <th>eXtrait</th>
                    </tr>
            """
        elif quoi == "auteurs":
            return """
                    <tr>
                        <th>#</th>
                        <th>Auteur</th>
                        <th>Nb de livres</th>
                    </tr>
            """
        elif quoi == "lecteurs":
            return """
                    <tr>
                        <th>#</th>
                        <th>Lecteur</th>
                        <th>Nb de livres</th>
                    </tr>
            """
        elif quoi == "genres":
            return """
                    <tr>
                        <th>#</th>
                        <th>Genre</th>
                        <th>Nb de livres</th>
                    </tr>
            """

    def footer_html():
        """
        """
        return """</body>
</html>
        """

    def header_html():
        return """<!DOCTYPE html>
<html>
<head>
    <title>Module audiolivres</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="http://gangand.net/ff/formation/notes/style_notes.css">
    <link rel="stylesheet" href="http://gangand.net/pp/projets/xavbox/xavbox.css">
</head>
<style>
html {
  scroll-behavior: auto;
}
</style>
<body>

"""

    def header_page():
        return "<center><h1>" + bbl_actu() + "</h1><br></center>"

    def menu():
        """ menu en te de page
        """
        return """<a name="menu">
<table class="table_touches">
    <caption>Menu</caption>
    <tr>
        <th><a href="#resume">Résumé</a></th>
        <th><a href="#auteurs">Auteurs</a></th>
        <th><a href="#genres">Genres</a></th>
        <th><a href="#lecteurs">Lecteurs</a></th>
        <th><a href="#tous_les_livres">Tous les livres</a></th>
    </tr>
    </table>
</a>
"""

    def td(s):
        return "          <td>" + str(s) + "</td>"

    def tr_td(arr_td):
        """ renvoie une ligne tr de td
        """
        ligne = ""
        for i in arr_td:
            ligne += td(i) + "\n"
        return "        <tr>" + "\n" + ligne + "</tr>" + "\n"

    if commun.reseau("hostname") == cfg.MACHINE_DEV_1:
        infos.pdrNew("Fichier tableur créé pour la documentation.")
        infos.m_g("*** Lancer dd doku sur bureau => Doku http://gangand.net/pp/projets/xavbox/adm/docs/stats...")
    else:
        return False

    table_tous_les_livres = create_tous_les_livres()
    table_resume, auteurs_, genres__, lecteurs = create_resume()
    table_auteurs  = create_auteurs (auteurs_, "auteurs")
    table_genres   = create_auteurs  (genres__, "genres")
    table_lecteurs = create_auteurs  (lecteurs, "lecteurs")

    corps_html = header_page() + \
        menu() + \
        table_resume + \
        table_auteurs + \
        table_genres + \
        table_lecteurs + \
        table_tous_les_livres
    page_entiere = header_html() + corps_html + footer_html()

    file_write = "/tmp/proj_xbx_adm_dok_stats_" + bbl_actu() + ".html"
    with open(file_write, "w") as f:
        f.write(page_entiere)
    infos.m_g("===== Fichier tableur créé " + file_write)

def duree_conviviale(duree):
    """ renvoie la durée en heures et minutes """
    # AVANT
    # 1h09 --> 1 heure 9 minutes
    #   03 --> 3 minutes
    # 2h   --> 2 heures

    # APRES
    # 01:54:23 --> une heure 54 minutes 23
    def duree_tester(quantite, unite):
        """ chaine à prononcer selon quantité et unité
        """
        bbll = bbl_langue()
        if int(quantite) == 0:
            dire = ""
        elif int(quantite) == 1:
            if bbll == cfg.LG_FRANCAIS:
                article_singulier = "une"
            elif bbll == cfg.LG_ANGLAIS:
                article_singulier = "one"
            elif bbll == cfg.LG_ALLEMAND:
                article_singulier = "eine"
            dire = "{} {} ".format(article_singulier, unite)
        else:
            # int(quantite) permet de transformer 02 en 2
            # pluriel
            if bbll == cfg.LG_FRANCAIS:
                marque_pluriel = "s"
            elif bbll == cfg.LG_ANGLAIS:
                marque_pluriel = "s"
            elif bbll == cfg.LG_ALLEMAND:
                # eine Stunde --> zwei Stunden
                # eine Minute --> zwei Minuten
                marque_pluriel = "n"
            dire = "{} {}{} ".format(int(quantite), unite, marque_pluriel)
        return dire

    if duree == "":
        return ""

    h, m, s = duree.split(":")
    dire_s = ""
    bbll = bbl_langue()
    if bbll == cfg.LG_FRANCAIS:
        dire_h = duree_tester(h, "heure")
        dire_m = duree_tester(m, "minute")
    elif bbll == cfg.LG_ANGLAIS:
        dire_h = duree_tester(h, "hour")
        dire_m = duree_tester(m, "minute")
    elif bbll == cfg.LG_ALLEMAND:
        dire_h = duree_tester(h, "Stunde")
        dire_m = duree_tester(m, "Minute")

    # une heure trois minutes
    return dire_h + dire_m + dire_s


def enqueue_download(bbl_key, lvr, silent = ""):
    """ ajoute le téléchargement à la file d'attente,
        en créant le fichier témoin,
        que le thread Enqueue traitera

        ajouter le livre actuel, de la bibliothèque actuelle
        au thread thread_download
        ex: /m/aba_dl_bg_bib_audio_cite__0152.txt
            pour télécharger le num4c 0152 de la biblio audio_cite
            audio_cite est la clé de BIB_DISPOS dans cfg.py
    """
    # dl_bg = download background (téléchargement en arrière-plan)
    espace_ok, s_espace = commun.espace("suffisant")
    if espace_ok == True:
        masque = "/m/aba_dl_bg_{}__{}.txt"
        file_download = masque.format(bbl_key, lvr["n"])
        with open(file_download, "w") as fd:
            # contenu du fichier témoin sans importance pour l'instant
            # bib_librivoxfr|0152|35|1h18|http://...zip+http://...zip
            contenu = "{}|{}|{}|{}|{}|{}".format(bbl_key, lvr["n"], lvr["type_url"], lvr["p"], lvr["d"], "+".join(lvr["u"]))
            fd.write(contenu)

    if silent == "":
        if espace_ok == True:
            msg = "enqueue " + file_download + " créé pour " + bbl_key
            infos.m_g(msg)
            msg = "espAce libre, " + \
                str(commun.espace("libre")) + ", " + \
                "téléchargement programmé"
            infos.pdrNew(msg)  # "il reste 3200 méga octets sur la clé usb"
        else:
            msg = "espace  libre trop  faible, " + str(commun.espace("libre")) + \
                "téléchargement annulé"
            infos.pdrNew(msg)
    else:
        if espace_ok == True:
            msg = "enqueue " + file_download + ", " + \
                lvr["p"] + ", " + \
                lvr["t"] + ", " + \
                " créé pour " + bbl_key
        else:
            msg = "enqueue annulé, espace trop faible"
        infos.m_g(msg)
    return msg


from threading import Thread
class Download(Thread):
    """ Thread chargé de télécharger en arrière-plan """
    # thread à lancer dès le démarrage du module aba

    def __init__(self):
        Thread.__init__(self)
        # ~ print("Lancement thread Download ...")

    def run(self):
        """Code à exécuter pendant l'exécution du thread."""
        # pause pour laisser temps chargement de sel_lvr[]
        commun.faire_pause(10)

        while True:
            commun.faire_pause(4)
            cmd = "ls -1 /m/aba_dl_bg_*.txt 2> /dev/null"
            str_dir_command = mpc.sub_proc(cmd)

            # on quitte le thread et l'appli
            if commun.file_exists("/m/quitter_download"):
                infos.g_m("fin thread Download")
                cmd = "rm /m/quitter_download"
                mpc.sub_proc(cmd)
                cmd = "rm /m/aba_dl_bg*txt"
                mpc.sub_proc(cmd)
                break

            # si fichiers témoin présents
            # un fichier témoin est du type :
            # /m/aba_dl_bg_audio_cite_classiques__2254.txt
            # et contient une ligne comme ci-dessous :
            # audio_cite_classiques|2254|mp3|15.0|0:15:00|http://www.archive.org/download/LhommeVoil/Lhomme_voil_Marcel_-_schwob.mp3
            if len(str_dir_command) > 10:
                # TOUDOU_optionnel afficher les téléchargements en attente pour le DEV
                list_downloads = str_dir_command.split("\n")
                n_dl_bg = 0
                # afficher tous les .txt présents
                for dl_bg in list_downloads:
                    n_dl_bg += 1
                    masque = "DEBUG dl en attente, {} sur {} {}"
                    msg = masque.format(n_dl_bg, len(list_downloads), dl_bg)
                    infos.log(msg)
                # nettoyer liste_dl pour ne conserver que
                # les lignes qui commencent par "aba_dl_bg_"
                num4c_dl = ""
                for temoin in list_downloads:
                    if temoin.startswith("/m/aba_dl_bg_"):
                        # lire le contenu du fichier
                        with open(temoin, "r") as file_data:
                            data_ligne = file_data.readline().strip()
                        data_livre = data_ligne.split("|")
                        # bib_librivoxfr|0152|mp3|35|1h18|http://...zip+http://...zip
                        bib_index = data_livre[0]      # bib_audiocite
                        num4c_dl = data_livre[1]       # 0152
                        type_url = data_livre[2]
                        poids = data_livre[3]
                        duree = data_livre[4]
                        liste_d_urls = data_livre[5].split("+")
                        # on ne télécharge que le premier de la liste
                        # avec dl_media, on est en synchrone

                        b_espace, s_espace = commun.espace("suffisant")
                        if b_espace == True:
                            mask = "téléch. {} n:{}, {} Mo, {}, type {}"
                            msg = mask.format(bib_index, num4c_dl, poids, duree, type_url)
                            infos.m_g(msg)
                            dl_media(bib_index, num4c_dl, poids, duree, liste_d_urls, type_url)
                            # contrôler encore ici si téléchargement
                            # a bien eu lieu
                            # (pb d'espace, connexion interrompu, ...)
                            # la tâche doit se finir pour que les lignes
                            # ci-dessous soient exécutées
                            msg = "espace  libre, " + \
                                str(commun.espace("libre")) + ", " + \
                                "après téléchargement arrière-plan fini " + \
                                str(temoin)
                            infos.m_g(msg)
                            # on émet un son quand un téléchargement est terminé
                            commun.sound_tache_terminee()
                            # on met à jour mpd pour la biblio actuelle
                            bbl_mpc_update()
                        else:
                            msg = "le livre ne peut pas être " + \
                                "téléchargé faute de place"
                            # car s_espace =
                            # "espace restant inférieur à 2000 méga octets"
                            infos.pdrNew(msg)
                            # infos.m_g("Download, " + message_espace_insuffisant(s_espace)[0])
                        # on supprime le fichier témoin
                        cmd = "rm {}".format(temoin)
                        mpc.sub_proc(cmd)


                        # on ne télécharge que le premier de la liste
                        break


DEV_ne_pas_lancer_thread = False
if DEV_ne_pas_lancer_thread == False:
    # démarrer le thread avec le module aba ?
    thread_queue = Download()
    # Lancement des threads
    thread_queue.start()


def entree_lire():
    """ lit le livre actu si présent,
        le télécharge sinon
        séquence : touche Entrée une fois
    """
    mpc.ferme_ton_clapet()
    if infos.ini_get("mode_consultation", "consulter") == "consulter":
        if mpc.is_playing():
            msg = "Vous êtes déjà en train de lire le livre, " + \
                "utilisez les touches 1, 2, 3 pour naviguer"
            infos.m_g(msg)
        else:
            lire_livre_actuel("lire")
    else:
        infos.m_g("En mode lire / consultation, touche Entrée pressée => RIEN")


def espace_suffisant_aba():
    """ vérifie dès démarrage du module aba
        si on pourra télécharger des livres """
    b_espace, s_espace = commun.espace("suffisant")
    if b_espace == False:
        infos.pdrNew(str(s_espace));
        msg = "L'espace est insuffisant actuellement " + \
            "pour télécharger de nouveaux livres, " + \
            "mais vous pouvez toujours écouter les présents."
        infos.pdrNew(msg)
        # mega_octets_biblio = commun.du(bbl_folder())
        # bbll = bbl_langue()
        # if bbll == cfg.LG_FRANCAIS:
            # voc_dl1 = "les livres téléchargés de cette bibliothèque occupent {} méga octets ".format(mega_octets_biblio)
            # voc_dl2 = "téléchargement impossible par manque de place, " + \
                # "veuillez supprimer des livres, " + \
                # "ou éteindre la machine et insérer " + \
                # "une clé avec de l'espace libre "
        # elif bbll == cfg.LG_ANGLAIS:
            # voc_dl1 = "download of absent books will not happen because space is missing "
            # voc_dl2 = "download of absent books will not happen because space is missing "
        # elif bbll == cfg.LG_ALLEMAND:
            # voc_dl1 = "Herunterladung unmöglich, Raum zu kurz "
            # voc_dl2 = "Herunterladung unmöglich, Raum zu kurz "
        # infos.pdrNew(voc_dl1, voc_dl2)


def supprimer_livres():
    """ supprimes les livres présents de la bibliothèque actuelle
    """
    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    mpc.stop()
    mpc.clear()
    mpc.ferme_ton_clapet()

    space_left_avant = commun.espace("libre")
    bn = bbl_nom()
    nb_prez = nb_livres_prezents()
    space = commun.du(bbl_folder())

    mask = "suppression des {} livres présents dans la bibliothèque {}"
    infos.pdrNew(mask.format(nb_prez, bn))

    dossier = bbl_folder()
    cmd = "rm -rf " + dossier
    mpc.sub_proc(cmd)

    space_left_apres = commun.espace("libre")
    mask = "espace avant, {}, espace après suppression, {}"
    infos.pdrNew(mask.format(space_left_avant, space_left_apres))

    mask = "gain obtenu : {} méga octets"
    infos.pdrNew(mask.format(space))

    cmd = "mkdir " + dossier
    mpc.sub_proc(cmd)

    slct_tout()


def supprimer_livre():
    """ supprime le livre présent actuel
    """
    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    space_left_avant = commun.espace("libre")
    mpc.stop()
    mpc.clear()
    mpc.ferme_ton_clapet()

    # - supprimer de la liste sel_lvr[]
    del_num4c = lv_num4c()
    del_titre = lv_titre()
    infos.g_m("Nb livres AVANT : {}".format(nb_livres_total()))
    ze_idx = ze__indice_actuel()

    # supprimer dossier physique
    dossier_du_livre = bbl_folder() + "/" + del_num4c
    cmd = "rm -rf " + dossier_du_livre
    infos.g_m(cmd)
    mpc.sub_proc(cmd)

    # reconstruire sel_lvr et sel_n2i
    global sel_n2i
    global sel_lvr
    ze2 = []
    sel_n2i = {}
    idx = 0
    nb_append = 0
    for i in sel_lvr:
        if idx != ze_idx:
            ze2.append(i)
            sel_n2i[i["n"]] = nb_append
            nb_append += 1
        idx += 1
    # print("==================== ze_idx = " + str(ze_idx))
    # print("==================== sel_n2i keys = " + str(sel_n2i.keys()))
    # print("==================== sel_n2i values = " + str(sel_n2i.values()))
    sel_lvr = []
    if nb_append == 0:
        # on a supprimé tous les livres
        infos.pdrNew("vous avez supprimé le dernier livre de la sélection")
        commun.faire_pause(3)
        space_left_apres = commun.espace("libre")
        masque = "espace avant, {}, espace après suppression, {}"
        infos.pdrNew(masque.format(space_left_avant, space_left_apres))
        slct_tout()
    else:
        idx = 0
        for i in ze2:
            sel_lvr.append(i)
        infos.g_m("Nb livres APRES : {}".format(nb_livres_total()))
        masque = "suppression du livre {}, de la bibliothèque {}, " + \
            "il vous reste {} livres en sélection"
        infos.pdrNew(masque.format(del_titre, bbl_nom(), nb_livres_total()))

        space_left_apres = commun.espace("libre")
        masque = "espace avant, {}, espace après suppression, {}"
        infos.pdrNew(masque.format(space_left_avant, space_left_apres))

        msg = "Appuyer sur 1, 2 ou 3 pour naviguer dans les livres"
        infos.pdrNew(msg)


def choisir_autre_biblio_svp():
    msg = "Aucun livre disponible, veuillez choisir " + \
        "une autre bibliothèque avec la touche 11"
    infos.pdrNew(msg)


def livre_hasard(quiet = ""):
    """ livre au petit bonheur la chance dans sel_lvr[] """
    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    memo_pos_livre_aba()
    mpc.ferme_ton_clapet()
    import random
    num_rand = random.randint(0, nb_livres_total() - 1)
    num4c_ze_actu = lv_num4c(num_rand)
    save_num4c_actuel(num4c_ze_actu)
    msg34 = "un livre au hasard parmi " + str(nb_livres_total())
    arr_msg = [ "",         # ne rien dire au niveau 0
                "hasard",
                msg34,
                msg34
            ]
    infos.pdrNew(arr_msg)
    if infos.niveau_aide_voc() in (2, 3):
        commun.faire_pause(2, False)
    infos.g_m("lire_livre_actuel")
    lire_livre_actuel("annoncer")


def offline_download():
    """ appareil offline, télécharger tous les livres de la bibliothèques en cours
        en vue de créer un appareil autonome qui aura à disposition
        tous les livres du catalogue sur place
        On télécharge hors clé usb sur ip12, car il y a 22 Go d'espace
        libre sur / le 23 juin 2020.

        touche OFFLINE
    """
    # infos.m_g("

    # télécharger
    commun.tts_niveau_aide(0)
    # infos.m_g("Début du téléchargement de quelques audiolivre hors ligne")
    bbl_audio_cite_classiques()
    offline_sr1("audio_cite_classiques", 15)

    bbl_litterature()
    offline_sr1("litteaudio", 5)
    commun.tts_niveau_aide(3)
    # infos.m_g("Fin du téléchargement de quelques audiolivre hors ligne")


def offline_sr1(fichier_faits, nb_downloads_max):
    """ télécharger nb_downloads_max livres
        de la biblio en cours
    """

    def dir_classer_define(lvr_n):
        """ renvoie sous-dossier de rangement
            pour le lvr["n"] demandé
        """
        # ex : lvr["n"] = 0158 => dir_classer = 0101-0200
        #    : lvr["n"] = 1502 => dir_classer = 1501-1600

        #    : lvr["n"] = 0100 => dir_classer = 0101-0200   ERREUR
        #    : lvr["n"] = 0100 => dir_classer = 0001-0100   OK

        # A B            C    D
        # 0001 à 0100 => 0001-0100

        A = lvr_n[:2]
        B = lvr_n[-2:]

        if B == "00":                       # lvr_n = 0100
            C1 = str(int(A) + 100 - 1)[-2:] # 00
            D1 = A                          # 01
        else:                               # lvr_n = 0099
            C1 = A                          # 00
            D1 = "0" + str(int(A) + 1)[-2:] # 001
            D1 = D1[-2:]                    # 01

        sous_dir = C1 + "01" + "-" + D1 + "00"
        return sous_dir

    def batch():
        """ crée le fichier offline_<BIBLIO>.sh
        """
        output = "/m/offline_" + bbl_actu() + ".sh"
        global all_lvr
        n = 0
        f = open(output, "w")
        ligne = "# bibliothèque " + bbl_actu()
        print(ligne, file=f)
        print("", file=f)

        ligne = "[ -d /home/pi/xavbox_offline ] || echo Dossier /home/pi/xavbox_offline absent"
        print(ligne, file=f)
        ligne = "[ -d /home/pi/xavbox_offline ] || exit"
        print(ligne, file=f)
        ligne = "cd /home/pi/xavbox_offline"
        print(ligne, file=f)

        ligne = "if [ -f livres_faits.txt ]; then "
        print(ligne, file=f)
        ligne = "  echo livres_faits.txt présent"
        print(ligne, file=f)
        ligne = "else"
        print(ligne, file=f)
        ligne = "  echo livres_faits.txt absent"
        print(ligne, file=f)
        ligne = "  exit"
        print(ligne, file=f)
        ligne = "fi"
        print(ligne, file=f)

        for lvr in all_lvr:
            ligne = "# livre " + lvr["n"]
            print(ligne, file=f)

            dir_classer = dir_classer_define(lvr["n"])
            partial_path = bbl_actu() + "/" + dir_classer + "/" + lvr["n"]
            full_path = "/home/pi/xavbox_offline/" + partial_path

            ligne = "cd /home/pi/xavbox_offline"
            print(ligne, file=f)
            print("", file=f)

            ligne = "[ -f " + bbl_actu() + ".stop ] && echo Dossier biblio_stop"
            print(ligne, file=f)
            ligne = "[ -f " + bbl_actu() + ".stop ] && exit"
            print(ligne, file=f)
            print("", file=f)

            ligne = "if grep " + partial_path + " livres_faits.txt ; then "
            print(ligne, file=f)
            ligne = " echo PRESENT " + full_path
            print(ligne, file=f)
            print("else", file=f)

            ligne = "  echo dl " + full_path
            print(ligne, file=f)

            ligne = "  mkdir -p " + full_path
            print(ligne, file=f)

            ligne = "  cd " + full_path
            print(ligne, file=f)

            pfx = 1000
            for u in lvr["u"]:  # plusieurs fichiers pour ce livre
                pfx += 1
                dest = lvr["n"] + "_" + str(pfx)[1:] + "." + lvr["type_url"]

                ligne = "  wget -O " + dest + " \"" + u + "\""
                print(ligne, file=f)

                if lvr["type_url"] == "zip":
                    ligne = "  7z e -y " + dest
                    print(ligne, file=f)

                    ligne = "  rm " + dest
                    print(ligne, file=f)


            print("fi", file=f)
            print("[ $(df . | tail -1 | awk '{print $4}') -lt 5000000 ] && echo 5 Go restants, FIN", file=f)
            print("[ $(df . | tail -1 | awk '{print $4}') -lt 5000000 ] && exit", file=f)
            print("", file=f)

        infos.m_g("Fichier {} crée".format(output))


    def ancienne_fonction():
        """ télécharge un par un les livres non présents
            dans le fichier faits.txt
            PAS AU POINT
        """
        global all_lvr
        n = 0
        taille_totale = 0
        nb_passer = 0
        with open("/home/pi/xavbox_offline/livres_faits.txt", "r") as f:
            ff = f.readlines()
        faits = []
        for i in ff:
            faits.append(i.strip())

        for lvr in all_lvr:
            if bbl_actu() + "/" + lvr["n"] in faits:
                continue

            dir_classer = dir_classer_define(lvr["n"])
            full_path = "/home/pi/xavbox_offline/" + bbl_actu() + "/" + dir_classer + lvr["n"]
            if commun.dir_exists(full_path):
                # infos.m_g("livre fait " + full_path + ", on passe")
                pass
            else:
                commun.make_dir(full_path)
                n += 1
                # wget_spider_taille indique la taille d'un fichier
                # sans besoin de le télécharger
                pfx = 0
                for u in lvr["u"]:  # plusieurs fichiers pour ce livre
                    pfx += 1
                    if lvr["p"] == "":
                        poids = wget_spider_taille(u)
                        # poids = '{:,} octets'.format(poids)
                    else:
                        poids = lvr["p"]
                    dest = lvr["n"] + "_" + str(pfx) + "." + lvr["type_url"]
                    cmd = "cd " + full_path + ";" + "wget -O " + dest + " " + u
                    msg = bbl_actu() + " multi " + str(n) + "." + str(pfx) + " , " + \
                        lvr["n"]        + " , " + \
                        lvr["type_url"] + ", " + \
                        str(poids)           + " Mo, " + \
                        lvr["t"]
                    infos.m_g(msg)
                    msg_wget = "wget -O " + dest + " " + u
                    infos.m_g(msg_wget)
                    r = mpc.sub_proc(cmd)
                    dezipper_del(dest, full_path)

                taille = commun.du(full_path)
                taille_totale += taille
                tf1 = '{:,}'.format(taille).replace(",", " ")
                tf2 = '{:,}'.format(taille_totale).replace(",", " ")
                infos.m_g("--> {} Mo, total {} Mo".format(tf1, tf2))
                infos.m_g("")
                if n > nb_downloads_max:
                    break
        mask = "dl offline_download() {} terminés dans la biblio {}"
        infos.m_g(mask.format(n, "/home/pi/xavbox_offline/" + bbl_actu()))

    batch()


def present_hasard_biblios():
    """ écouter un livre présent au hasard
        en recherchant dans toutes les bibliothèques
        touches "1111"
    """

    def taille_livre_present():
        """ renvoie espace en Mo du livre présent
            attention :
                opération différente selon que c'est anarchie
                ou online
        """

    def phb_chercher(prononcer = True):
        """ rechercher tous les livres présents
            dans les bibliothèques statiques
            un livre présent est symbolisé par la présence d'un sous-dossier
            dans le dossier de la bibliothèque
            doc : gangand.net/pp/projets/xavbox/adm/adm_aba.php#changement
        """
        cles = list(cfg.BIB_DISPOS.keys())
        cles.sort()
        n = 0
        infos.m_g("Bibliothèques, livres présents")
        total_Mo = 0
        nb_total_presents = 0
        nb_biblios_presents = 0
        choix_hasard = []
        nb_livr_prezents_partout = 0

        for cle_biblio in cles:
            n += 1
            bib1 = cfg.BIB_DISPOS[cle_biblio]
            cata_type = bib1["cata_type"]
            catalogue = bib1["catalogue"]
            nom       = bib1["nom"]
            msg = "vide"
            if cata_type == "statique":
            # cata_type = "tous":
            # if cata_type == "tous":
                dir_biblio = cfg.ABA_DIR + "/" + cle_biblio  # /m/music/aba/audio_cite_classiques
                if commun.dir_exists(dir_biblio):
                    nb_dossiers = nb_livres_prezents(cle_biblio)
                    nb_total_presents += nb_dossiers
                    if nb_dossiers > 0:
                        nb_biblios_presents += 1
                        t1 = commun.du(dir_biblio)
                        taille = " (" + str(t1) + " Mo)"
                        total_Mo += t1
                        sous_dossiers = commun.folders_simple(dir_biblio)
                        for full_path in sous_dossiers:
                            if commun.du(full_path) > 0:
                                # on ne vérifie que la taille,
                                # pas la nature des fichiers
                                choix_hasard.append((cle_biblio, full_path))
                                nb_livr_prezents_partout += 1
                    else:
                        taille = ""
                    msg = str(nb_dossiers).rjust(2) + " présents" + taille
            else:
                msg = "dynamique"
            infos.g_m(str(n).rjust(2) + ". " + nom.ljust(20) + " : " + msg)
        if nb_livr_prezents_partout > 0:
            infos.g_m("Taille totale : " + str(total_Mo) + " Mo")
            mask = "{} livres présents dans {} bibliothèques, qui occupent {} méga octets"
            msg = mask.format(nb_total_presents, nb_biblios_presents, total_Mo)
            if prononcer:
                infos.pdrNew([ "", msg ])
            else:
                infos.m_g(msg)
        return nb_livr_prezents_partout, choix_hasard

    def phb_5_au_hasard(nb_livr_prezents_partout, choix_hasard, prononcer = True):
        """ lister chaque biblio + livres présents
        """
        if nb_livr_prezents_partout > 0:
            import random
            max_rand = len(choix_hasard) - 1
            num_rand = random.randint(0, max_rand)
            try:
                cle_biblio, full_path = choix_hasard[ num_rand ]
            except IndexError:
                infos.m_g("index random = " + str(num_rand))
                infos.m_g("longueur choix_hasard = " + str(len(choix_hasard)))
            mask = "biblio {}, dossier {}"
            msg = mask.format(cle_biblio, full_path)
            infos.g_m(msg)
        splitter = full_path.split("/")
        champ_n = splitter[ len(splitter) - 1 ]   # programmer commun.basename(path)
        mask = "Lecture du livre au hasard {}, de la bibliothèque {}"
        msg = mask.format(champ_n, bbl_nom(cle_biblio) )
        if prononcer:
            infos.pdrNew(msg)
        else:
            infos.g_m(msg)
        return cle_biblio, champ_n

    # parcourir toutes les bibliothèques au hasard
    # voir s'il y a des livres présents
    mpc.ferme_ton_clapet()
    msg = "écoute d'un livre présent, choisi au hasard " + \
        "des bibliothèques"
    infos.pdrNew(["", msg])
    mpc.clear()

    Prononcer_True = True
    nb_livr_prezents_partout, choix_hasard = phb_chercher(Prononcer_True)
    if nb_livr_prezents_partout == 0:
        msg = "Aucun livre n'est présent " + \
            "dans les bibliothèques, " + bbl_nom() + ", " + \
            "vous pouvez utiliser les touches 666 ou 6666 " + \
            "pour télécharger des livres au hasard."
        infos.pdrNew(msg)
        return 0

    cle_biblio, champ_n = phb_5_au_hasard(
        nb_livr_prezents_partout, choix_hasard, Prononcer_True)
    # lancer la lecture après mémorisation livre en cours
    if nb_livres_total() > 0:
        memo_pos_livre_aba()
        save_num4c_actuel(lv_num4c())
    infos.ini_set("aba_bbl_actuelle", cle_biblio)
    save_num4c_actuel(champ_n)
    charger_all_lvr()
    build_selektion()
    if infos.niveau_aide_voc() > 1:
        infos.pdrNew("Appuyez sur Entrée pour écouter le livre")
        a = input()
    lire_livre_actuel("lire")
    # touches "1111"


def rand10lvr():
    """ télécharge 10 nouveaux livres au hasard
        en arrière-plan
        pour lecture ultérieure
        touches "666"
    """
    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    NB_LIVRES_DL_HASARD = 10
    if 0 < nb_livres_total() < NB_LIVRES_DL_HASARD:
        nb_downloads_random = nb_livres_total()
    else:
        nb_downloads_random = NB_LIVRES_DL_HASARD

    memo_pos_livre_aba()
    mpc.ferme_ton_clapet()

    # sortir si espace insuffisant
    espace_ok, s_espace = commun.espace("suffisant")
    if not espace_ok:
        infos.pdrNew(message_espace_insuffisant(s_espace))
        # msg = "espace restant insuffisant pour télécharger, " + \
            # "veuillez libérer d'abord de l'espace."
        # infos.pdrNew(msg)
        # msg = "Pour cela, placez-vous sur un livre présent, " + \
            # "puis effacez-le avec 987, " + \
            # "ou bien 11 pour ouvrir le menu vocal, puis la touche 9, " + \
            # "pour supprimer plusieurs livres d'un coup."
        # infos.pdrNew(msg)
        return 0

    space_left = commun.espace("libre")
    msg = "eSpace libre, " + str(space_left) + ", " + \
        "tentative de téléchargement de 10 livres au hasard " + \
        "dans la sélection"
    infos.pdrNew(msg)
    commun.faire_pause(4)
    # memo_lvr_actu = lv_livre_actuel()    # définit le livre actu
    # contrôler le nombre de livres au total d'abord
    nb_livres_finaux = 0
    import random
    # TODO : s'assurer que le nombre au hasard n'a pas encore été choisi
    for i in range(nb_downloads_random):
        num_rand = random.randint(0, nb_livres_total() - 1)
        infos.m_g("***** num_rand =" + str(num_rand) + " *****")
        num4c_ze_actu = lv_num4c(num_rand)
        save_num4c_actuel(num4c_ze_actu)
        if media_present(lv_livre_actuel()) == "absent_usb":
            lvr = lv_livre_actuel()
            enqueue_download(bbl_actu(), lvr, "rand10lvr")
            nb_livres_finaux += 1
        else:
            msg = str(num4c_ze_actu) + \
                " présent, on en choisit un autre"
            infos.g_m(msg)
    infos.pdrNew(str(nb_livres_finaux) + " livres sont en cours " + \
        "de téléchargement")


def rand10lvr_biblios():
    """ télécharger 10 livres au hasard
        de toutes les bibliothèques
        touches "6666"
    """
    # parcourir toutes les bibliothèques au hasard
    # voir s'il y a des livres présents
    msg = "télécharger 10 livres au hasard " + \
        "de toutes les bibliothèques"
    infos.pdrNew(msg + " en cours")
    # prendre 10 fois une bibliothèque au hasard
    # programmer avec enqueue le téléchargement


def rand11lvr_biblios():
    """ télécharger 11 livres au hasard
        de toutes les bibliothèques
        touches "6666"
    """
    # parcourir toutes les bibliothèques au hasard
    # voir s'il y a des livres présents
    mpc.ferme_ton_clapet()
    mpc.clear()

    msg = "Téléchargement de 11 livres au hasard " + \
        "de toutes les bibliothèques"
    infos.pdrNew(msg + " en cours")
    # prendre 11 fois une bibliothèque au hasard
    # programmer avec enqueue le téléchargement

    # sortir si espace insuffisant
    espace_ok, s_espace = commun.espace("suffisant")
    if not espace_ok:
        infos.pdrNew(message_espace_insuffisant(s_espace))
        return 0

    nb_dl_books_partout = 11
    cles = list(cfg.BIB_DISPOS.keys())
    cles.sort()
    infos.m_g("Bibliothèques, {} livres au hasard partout".format(nb_dl_books_partout))

    import random
    nb_livres_programmes = 0
    global all_lvr

    while True:
        if nb_livres_programmes == nb_dl_books_partout:
            break

        num_rand = random.randint(0, len(cles) - 1 )
        cle_biblio = cles[num_rand]
        bib1 = cfg.BIB_DISPOS[cle_biblio]
        cata_type = bib1["cata_type"]
        if cata_type == "statique":
            infos.ini_set("aba_bbl_actuelle", cle_biblio)
            if charger_all_lvr():
                # choisir un livre au hasard dans la bibliothèque activée
                nb_livres_programmes += 1
                num_rand_2 = random.randint(0, len(all_lvr) - 1 )
                lvr_hasard = all_lvr[ num_rand_2 ]
                champ_n_hasard = lvr_hasard[ "n" ]
                titre = lvr_hasard[ "t" ]
                infos.m_g("nb_livres_programmes = " + \
                    str(nb_livres_programmes) + \
                    ", " + champ_n_hasard + \
                    ", " + titre)
                enqueue_download(cle_biblio, lvr_hasard, "silence")


def livre_prec():
    """ aller au livre suivant """
    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    memo_pos_livre_aba()
    num_livre_actuel = ze__indice_actuel() - 1
    if num_livre_actuel == -1:
        num_livre_actuel = nb_livres_total() - 1
    save_num4c_actuel(lv_num4c(num_livre_actuel))
    mpc.ferme_ton_clapet()
    lire_livre_actuel("annoncer")


def livre_suiv():
    """ aller au livre précédent """
    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    memo_pos_livre_aba()
    num_livre_actuel = ze__indice_actuel() + 1
    if num_livre_actuel > nb_livres_total() - 1:
        num_livre_actuel = 0
    save_num4c_actuel(lv_num4c(num_livre_actuel))
    mpc.ferme_ton_clapet()
    lire_livre_actuel("annoncer")


def lire_livre_actuel(action, direkt = ''):
    """ lit le livre de numéro ze_num_actuel(),
        l'annonce, ou le télécharge
        quand on change de bibliothèque, le dernier livre lu
        dans la bibliothèque est retrouvé grâce à la valeur correspondante
        du fichier py_user.ini, exemples :
        num4c_litteaudio=0444|2020-02-25 17:00:10
        num4c_librivoxen=0053|2020-02-25 19:54:42
        num4c_cle_usb_anarchie=0012|2020-02-25 21:48:06
    """

    def final_annoncer(final_seek_read):
        """ renvoie une chaîne du type :
            - "livre présent, ..."
            - "livre à télécharger, ..."
        """
        # annoncer seulement le livre (cf. doc_05)
        infos.log("  --> TIP : Appuyer sur Entrée pour (re)lancer (débuter/terminer) la lecture de ce livre")
        infos.log("  -->       seek_read = " + seek_read)
        # on prononce livre ou audiolivre si média présent sur clé usb,
        # référence s'il est absent
        msg1 = final_resume_livre()

        bbll = bbl_langue()
        if bbll == cfg.LG_FRANCAIS:
            msg_livre_present = "livre présent "
            msg_livre_telech = "livre à télécharger "
            msg_final_appui_sur_entree_download = ", appuyez sur Entrée pour lancer le téléchargement"
            msg_final_appui_sur_entree_lire = ", appuyez sur Entrée pour lancer la lecture"
        elif bbll == cfg.LG_ANGLAIS:
            msg_livre_present = "book present "
            msg_livre_telech = "book to download "
            msg_final_appui_sur_entree_download = ", press enter to start the download"
            msg_final_appui_sur_entree_lire = ", press Enter to start reading"
        elif bbll == cfg.LG_ALLEMAND:
            msg_livre_present = "anwesendes Buch "
            msg_livre_telech = "Buch zu herunterladen "
            msg_final_appui_sur_entree_download = ", drücken Sie Enter zur Herunterladung"
            msg_final_appui_sur_entree_lire = ", drücken Sie Enter zum Lesen"

        # bn = bbl_nom() + ", "
        bn = "" # on raccourcit le message vocal
        if media_present(lv_livre_actuel()) == "présent_usb":
            return [
                msg_livre_present + msg1[0],
                msg_livre_present + msg1[1],
                bn + msg_livre_present + msg1[2] + msg_final_appui_sur_entree_lire,
                bn + msg_livre_present + msg1[3] + msg_final_appui_sur_entree_lire
                ]
        elif bbl_actu() == cfg.bbl_CLE_USB_ANARCHIE:
            msg_livre_present = " livre sur clé "
            return [
                msg_livre_present + msg1[0],
                msg_livre_present + msg1[1],
                bn + msg_livre_present + msg1[2] + msg_final_appui_sur_entree_lire,
                bn + msg_livre_present + msg1[3] + msg_final_appui_sur_entree_lire
                ]
        else:
            return [
                msg_livre_telech + msg1[0],
                msg_livre_telech + msg1[1],
                bn + msg_livre_telech + msg1[2] + msg_final_appui_sur_entree_download,
                bn + msg_livre_telech + msg1[3] + msg_final_appui_sur_entree_download
                ]

    def final_lire(lvr, piste, seek):
        """ lance le livre en cours, ou son téléchargement
            renvoie une chaîne du type :
            - "lecture du livre, ..."
            - "téléchargement programmé ..."
        """
        # infos
        arr_resume_livre = final_resume_livre()
        # "777, 51 minutes, Dix années d'exil, de staël De"
        bbll = bbl_langue()
        if ( (bbl_actu() == cfg.bbl_CLE_USB_ANARCHIE) or
             (media_present(lvr) == "présent_usb") ):
            # clé usb ou media présent : on lit
            infos.m_g("final_lire, média présent pour " + lvr["n"])
            arr_lecture = lancer_lecture(lvr, piste, seek)
            # au lieu de mémoriser le livre quand on quitte le module,
            # on le mémorise dès que la lecture commence
            save_num4c_actuel(lvr["n"])

            if bbll == cfg.LG_FRANCAIS:
                voc_lecture1 = "lecture "
                voc_lecture2 = "lecture du livre "
            elif bbll == cfg.LG_ANGLAIS:
                voc_lecture1 = "book reading "
                voc_lecture2 = "book is being read "
            elif bbll == cfg.LG_ALLEMAND:
                voc_lecture1 = "Buchlesung "
                voc_lecture2 = "wir lesen das Buch "
            arr_prononcer3 = [
                "",
                voc_lecture1 + ", " + arr_resume_livre[1] + ",, " + arr_lecture[1],
                voc_lecture2 + ", " + arr_resume_livre[2] + ",, " + arr_lecture[2],
                voc_lecture2 + ", " + arr_resume_livre[3] + ",, " + arr_lecture[3]
                ]

        else:
            # media absent : on programme le téléchargement
            # est-ce qu'on mémorise le livre même si pas téléchargé ?
            # - oui , pour l'instant
            save_num4c_actuel(lvr["n"])
            b_espace, s_espace = commun.espace("suffisant")
            if b_espace == False:
                arr_prononcer3 = message_espace_insuffisant(s_espace)
            else:
                enqueue_download(bbl_actu(), lvr)
                if bbll == cfg.LG_FRANCAIS:
                    voc_dl1 = "téléchargement programmé "
                    voc_dl2 = "téléchargement en arrière-plan du livre "
                elif bbll == cfg.LG_ANGLAIS:
                    voc_dl1 = "download added to the queue "
                    voc_dl2 = "background download for book "
                elif bbll == cfg.LG_ALLEMAND:
                    voc_dl1 = "Herunterladung programmiert "
                    voc_dl2 = "Herunterladung in Hintergrund programmiert "

                arr_prononcer3 = [
                    voc_dl1,
                    voc_dl1,
                    voc_dl2 + arr_resume_livre[2],
                    voc_dl2 + arr_resume_livre[3]
                    ]

        return arr_prononcer3

    def final_resume_livre():
        """ retourne une chaîne du type :
            - "La Maison à vapeur, de Jules VERNE"
            - "404, 14 heures , La Maison à vapeur, de Jules VERNE, lu par Orangeno"
        """
        # définir variables
        n = zero_off(lv_num4c())
        t = lv_titre()
        a = lv_auteur()
        l = lv_lecteur()
        g = lv_genre()

        t = t.replace("_", " ") #     Raymond_Devos_-_Je_zappe_1992
                                # --> Raymond Devos - Je zappe 1992

        a = a.replace("_", " ")
        lu_par_lecteur = ", Lu par " + l
        bbll = bbl_langue()
        if bbll == cfg.LG_ANGLAIS:
            if l.strip() == "":
                lu_par_lecteur = ""
            else:
                lu_par_lecteur = ", the reader is " + l
        elif bbll == cfg.LG_ALLEMAND:
            if l.strip() == "":
                lu_par_lecteur = ""
            else:
                lu_par_lecteur = ", der Vorleser ist " + l
        else:
            if l.strip() == "":
                lu_par_lecteur = ""
            else:
                lu_par_lecteur = ", lu par " + l

         # on inverse les termes
         # ex : Allais Alphonse -> Alphonse Allais
        # ~ if a.find(" ") > 0:
            # ~ lva = a.split(" ", 2)
            # ~ a = lva[1] + " " + lva[0]
        d = duree_conviviale(lv_duree())
        # 457, 19 minutes, Ventre Saint-Gris, de  Brisay
        # 458, 16 minutes, La Dame de Bayard, de henry Brisay
        # 777, 51 minutes, Dix années d'exil, de staël De
        # 1536, 10 minutes, Comment on se venge, de Richard Lesclide
        # 2015, 28 minutes, Révélation magnétique, de Edgar Poe
        if bbll == cfg.LG_FRANCAIS:
            if a.strip() == "":
                ecrit_par = ""
            else:
                ecrit_par = ", de " + a
            return [
                t + ", de " + a,
                t + ", de " + a,
                n + ", " + g + ", " + d + ", " + t + ecrit_par + lu_par_lecteur,
                n + ", " + g + ", " + d + ", " + t + ecrit_par + lu_par_lecteur
                ]
        elif bbll == cfg.LG_ANGLAIS:
            if a.strip() == "":
                ecrit_par = ""
            else:
                ecrit_par = ", author " + a
            return [
                t + ", written by " + a,
                t + ", written by " + a,
                n + ", " + d + ", " + t + ecrit_par + lu_par_lecteur,
                n + ", " + d + ", " + t + ecrit_par + lu_par_lecteur
                ]
        elif bbll == cfg.LG_ALLEMAND:
            if a.strip() == "":
                ecrit_par = ""
            else:
                ecrit_par = ", der Autor ist " + a
            return [
                t + ", geschrieben von " + a,
                t + ", geschrieben von " + a,
                n + ", " + d + ", " + t + ecrit_par + lu_par_lecteur,
                n + ", " + d + ", " + t + ecrit_par + lu_par_lecteur
                ]

    def horodatage_convivial(mn_sec):
        """ convertit horodatage en langage parlé """
        # ex :  0:05 devient  0 minute   5
        #      14:32 devient 14 minutes 32
        temps = mn_sec.split(":")
        if len(temps) == 2:
            return "{} minutes {}".format(temps[0], temps[1])
        else:
            return ""

    def lancer_lecture(lvr_lecture, piste_lecture, seek_lecture):
        """ lit un fichier mp3 ou un dossier de mp3
            renvoie liste chaînes du type :
            - "piste 3 sur 8"
            - "piste 3 sur 8, à 3 minutes 12"
        """
        if bbl_actu() == cfg.bbl_CLE_USB_ANARCHIE:
            # jouer la clé usb : ajouter à mpd chaque fichier mp3
            num_url = 0
            for url in lvr_lecture["u"]:
                if num_url == 0:
                    infos.m_g("USB {}: mpc add 'file://{}'".format(num_url, url))
                # IMPORTANT, mpd accepte les chemins absolus, comme :
                # mpc add "/m/music/type3/Z/Stefan Zweig/Partie 1"
                mpc.add("file://" + url)
                # LDM = lecture des médias
                # documentation en cours d'écriture
                # cf. http://gangand.net/pp/projets/xavbox/adm.php#aba_lecture
                infos.m_g("LDM_mpc.add(\"file://" + url + "\")")
                num_url += 1
            infos.m_g("USB : ...")
            infos.m_g("USB {} : mpc add {}".format(num_url, url))
        else:
            # ajouter le dossier, de fichier(s) mp3, du livre demandé
            bib_dir = bbl_sub_folder()
            audio_files_folder = bib_dir + "/" + lvr_lecture["n"]
            # TODO : voir si bien placé
            # il vaut mieux faire un update quand on crée un dossier
            # et qu'on y copie ou dézippe des nouveaux mp3
            mpc.update_wait(audio_files_folder)
            # ajouter et jouer fichier à position enregistrée
            mpc.add(audio_files_folder)
            infos.m_g("LDM_mpc.add(\"" + audio_files_folder + "\")")

        # lancer la lecture de la playliste du livre
        mpc.ferme_ton_clapet()
        mpc.play(piste_lecture)
        mpc.seek(seek_lecture)
        infos.m_g("inconnu ? => " + mpc.sub_proc("mpc -f %file% | head -1"))

        # si une seule piste dans playlist, ne pas annoncer le n° de piste
        # ~ if int(mpc.playlist_longueur()) > 1:
        bbll = bbl_langue()
        if bbll == cfg.LG_FRANCAIS:
            piste, sur_total, at_time = "piste ", " sur ", " à "
        elif bbll == cfg.LG_ANGLAIS:
            piste, sur_total, at_time = "track ", " from ", " at "
        elif bbll == cfg.LG_ALLEMAND:
            piste, sur_total, at_time = "Stück ", " von ", " an "
        msg_start = piste + piste_lecture + sur_total + mpc.playlist_longueur()

        if seek_lecture in [ "", "00:00:00"]:
            msg = msg_start
        else:
            msg = msg_start + ", " + at_time + horodatage_convivial(seek_lecture)

        arr_prononcer2 =  [
            msg_start,
            msg_start,
            msg_start,
            msg_start
            ]
        return arr_prononcer2

    def last_pos(pos_lvr):
        """ position dans le morceau
            renvoie un tuple :
            position (numéro de piste), seek (02:38), ...
        """
        # "non lu" signifie "pas de mémorisation trouvée pour ce livre"
        piste1, seek1, seek1_read, horo1 = "1", "00:00:00", "non lu", ""

        mem_file = bbl_pos_livres()
        if commun.file_exists(mem_file):
            with open(mem_file, "r") as f:
                livres_commences = f.readlines()
                for ligne in livres_commences:
                    ligne = ligne.strip()
                    if ligne.find(pos_lvr["n"] + "|") == 0:
                        t = ligne.split("|")
                        piste1, seek1, horo1 = t[1], t[2], t[3]
                        liste_str = [ str(i) for i in range(1, 1000) ]
                        if not piste1 in liste_str:
                            # pour éviter les valeurs du type::
                            # volume: 79%   repeat: off   random: off   single: off   consume: off
                            infos.log("ERR piste_memo BAD : " + piste1)
                            piste1 = "1"
                        seek1_read = seek1 + "/"
                        break
        infos.log("piste1, seek1, seek1_read, horo = {}, {}, {}, {}".format(piste1, seek1, seek1_read, horo1))
        # ex : 00:00:00, non lu
        #          0:10, 0:10/
        #          0:05, 0:05/
        return piste1, seek1, seek1_read

    def visu_nfo_livre(lv1, nfo_seek_read):
        """ ligne infos livre
            renvoie une chaîne du type :
            livre  749 n=0749 mp3   0:14/ 0:1:00   1.2Mo DJ Rebel   Conversation dune pe
        """
        # TOUDOU_optionnel : indiquer par une lettre P = présent, A = absent (ou . = absent)
        #                    si le media est là
        #                    --> petit bip sonore genre pouce levé, pour dire que le media est là
        lv1u = lv1["u"][0]
        tu = lv1["type_url"]
        seek1 = nfo_seek_read.rjust(7)

        lv1n = zero_off(get_num4c_actuel()).rjust(4)
        lv1d = lv1["d"].rjust(5)
        lv1p = lv1["p"].rjust(5) + "Mo"
        lv1l = lv1["l"][:9].ljust(10)
        lv1t = (lv1["t"] + "." * 20)[0:20]
        lv1urls = lv1u[:20] + " ... " + lv1u[-20:]
        msg = "VISU livre " + " ".join((lv1n, tu, seek1, lv1d, lv1p, lv1l, lv1t))
        infos.g_m(msg)
        p0 = "livre " + lv1["n"] + ", " + lv1["t"] + " de " + lv1["a"]
        p1 = "livre " + lv1["n"] + ", " + lv1["t"] + " de " + lv1["a"]
        p2 = "livre " + lv1["n"] + ", " + lv1["t"] + " de " + lv1["a"]
        p3 = "livre " + lv1["n"] + ", " + lv1["t"] + " de " + lv1["a"]
        arr_prononcer1 = [ p0, p1, p2, p3 ]
        return arr_prononcer1

    ####################################################################
    #                        lire_livre_actuel()                       #
    ####################################################################
    # la lecture est lancée si user appuie sur Entrée (entree_lire() ),
    # quand il entend le livre actu (Entrée fixe la variable action à "lire")
    # mpc.ferme_ton_clapet()          # coupe les messages vocaux en cours et à venir
    mpc.clear()                     # stoppe lecture actuelle
    mpc.random_off()                # lire un livre dans l'ordre
    lvr_actu = lv_livre_actuel()    # définit le livre actu

    # cf. doc_01 et fichier lire_livre_actuel.odt sur /3en1
    piste, seek, seek_read = last_pos(lvr_actu)             # positionne dans le morceau, MUET
    visu_nfo_livre(lvr_actu, seek_read)   # infos visuelles livre     , MUET
    global ouverture_biblio
    prendre_parole = True
    if direkt == "direkt":
        prendre_parole = False
        arr_prononcer = final_lire(lvr_actu, piste, seek)
    elif action == "lire":
        if ouverture_biblio == True:
            arr_prononcer = final_annoncer(seek_read)
        else:
            # prendre_parole = False
            arr_prononcer = final_lire(lvr_actu, piste, seek)
            # ne rien prononcer quand on demande la lecture d'un livre
            # pour des infos, il y a les touches /, //, /*
            # le user a déjà entendu les infos du livre
    elif action == "annoncer":
        arr_prononcer = final_annoncer(seek_read)
    else:
        arr_prononcer = "erreur, l'action n'est ni lire, ni annoncer "

    if prendre_parole == True:
        infos.pdrNew(arr_prononcer)
    else:
        infos.m_g("PRENDRE PAROLE FALSE 2")
    # if prendre_parole == True:
        # if direkt == '':
            # infos.pdrNew(arr_prononcer, bbl_langue())

    # dès qu'un livre est lu ou annoncé,
    # la bibliothèque est au-delà du premier livre
    ouverture_biblio = False


def loglevel():
    """ retourne le niveau du loglevel """
    return int(infos.ini_get("loglevel", "0"))


def lv_data(champ, idx_ze = None):
    """ champ du livre
        sel_lvr[ idx_ze ]          ou
        sel_lvr[ ze__indice_actuel() ]
    """
    global sel_lvr
    if len(sel_lvr) == 0:
        # attention : si global sel_lvr est vide,
        # sel_lvr[idx_ze] renvoie un IndexError
        return "sel_lvr est vide"

    if idx_ze is None:
        idx_ze = ze__indice_actuel()
    return sel_lvr[idx_ze][champ]


def lv_livre_actuel():
    """ renvoie le livre actuel """
    global sel_lvr
    # ~ print("ze__indice_actuel()=" + str(ze__indice_actuel()))
    return sel_lvr[ze__indice_actuel()]


def lv_auteur(idx_ze = None):
    """ auteur du livre
        sel_lvr[ idx_ze ]          ou
        sel_lvr[ ze__indice_actuel() ]
        "a" signifie poids
    """
    return lv_data("a", idx_ze)


def lv_duree(idx_ze = None):
    """ durée du livre
        sel_lvr[ idx_ze ]          ou
        sel_lvr[ ze__indice_actuel() ]
        "d" signifie poids
    """
    return lv_data("d", idx_ze)


def lv_fiche(idx_ze = None):
    """ url fiche info html du livre
        sel_lvr[ idx_ze ]          ou
        sel_lvr[ ze__indice_actuel() ]
        "f" signifie fiche
    """
    return lv_data("f", idx_ze)


def lv_genre(idx_ze = None):
    """ genre du livre (roman, conte, ...)
        sel_lvr[ idx_ze ]          ou
        sel_lvr[ ze__indice_actuel() ]
        "y" signifie genre
    """
    return lv_data("y", idx_ze)


def lv_lecteur(idx_ze = None):
    """ lecteur(s) ou lectrice(s) du livre
        sel_lvr[ idx_ze ]          ou
        sel_lvr[ ze__indice_actuel() ]
        "l" signifie lecteur
    """
    return lv_data("l", idx_ze)


def lv_poids(idx_ze = None):
    """ poids du livre
        sel_lvr[ idx_ze ]          ou
        sel_lvr[ ze__indice_actuel() ]
        "p" signifie poids
    """
    return lv_data("p", idx_ze)


def lv_num4c(idx_ze = None):
    """ num4C (numéro à 4 chiffres) du livre
        sel_lvr[ idx_ze ]          ou
        sel_lvr[ ze__indice_actuel() ]
        "n" signifie numl4c
    """
    return lv_data("n", idx_ze)


def lv_titre(idx_ze = None):
    """ titre du livre
        sel_lvr[ idx_ze ]          ou
        sel_lvr[ ze__indice_actuel() ]
        "n" signifie numl4c
    """
    return lv_data("t", idx_ze)


def lv_type_url(idx_ze = None):
    """ type d'url du livre
        sel_lvr[ idx_ze ]          ou
        sel_lvr[ ze__indice_actuel() ]
        sera peut-être abandonné certainement
    """
    return lv_data("type_url", idx_ze)


def lv_url(idx_ze = None):
    """ liste d'urls media du livre (éléments séparés par le signe '+'
        sel_lvr[ idx_ze ]          ou
        sel_lvr[ ze__indice_actuel() ]
        "u" signifie url
    """
    return lv_data("u", idx_ze)


def media_present(lvr):
    """ teste présence mp3 seul, ou d'un dossier zip
        renvoie une des 4 valeurs suivantes :
            "présent_usb",
            "absent_usb",
            "dépôt_local_présent"
            "dépôt_local_absent"
            "taille_était_nulle"

        fonctions utilisées dans deux autres fonctions seulement :
        rand10lvr()
        lire_livre_actuel.final_annoncer()

        TODO : un livre présent sur un dépôt local (/media/fbx/rouge)
        doit-il être considéré comme "présent" ou "dépôt_local"
    """
    bib_dir = bbl_folder()
    # présence sur clé usb
    if commun.dir_exists(bib_dir + "/" + lvr["n"]):
        # # vérifier si fichier .mp3 de taille nulle
        cmd = "du -sc {}/{}".format(bib_dir, lvr["n"]) + \
            "/*mp3 2>/dev/null | tail -1 | awk '{print $1}'"
        taille_mp3 = mpc.sub_proc(cmd)
        if taille_mp3 == "0":
            msg = "Ce livre a rencontré un problème " + \
                "de téléchargement, veuillez réessayer plus tard"
            infos.pdrNew(msg)
            mask_cmd = "rm -rf {}/{}"
            cmd = mask_cmd.format(bib_dir, lvr["n"])
            # # suppression du dossier num4c
            mpc.sub_proc(cmd)
            return "taille_était_nulle"
        return "présent_usb"
    # présence dépôt local (nas freebox)
    elif commun.depot_local_present("aba"):
        if commun.dir_exists("/media/fbx/aba/" + bbl_actu() + "/" + lvr["n"]):
            return "dépôt_local_présent"
        else:
            return "dépôt_local_absent"
    # média absent partout : sur clé usb ET sur dépôt local
    else:
        infos.log("debug Dossier media absent " + bib_dir + "/" + lvr["n"])
        return "absent_usb"


def memo_pos_livre_aba():
    """ mémorise le dernier moment lu du livre actuel (= livre quitté)
        càd position dans playlist et progression
    """

    def memo_pos_livres_maj(fichier, ecrire_ligne):
        """ mémorise le dernier moment de lecture
            d'un livre désigné par son num4c
            ex : memo_pos_livres_maj("/m/memo/aba/memo_pos_livres_litteratureaudio.txt", "0038|1|0:41|2020-02-20 07:43:49")
        """
        # attention : penser au cas où on a lu tout le livre
        le_numero = ecrire_ligne.split("|")[0]

        infos.m_g("DEBUG_MEMO " + fichier + " " + ecrire_ligne)

        if commun.file_exists(fichier):
            infos.g_m("le fichier " + fichier + " existe")
            s_num = le_numero + "|"  # "0152|"
            with open(fichier, "r") as f:
                lignes = f.readlines()

            f2 = open(fichier, "w")
            ligne_trouvee = False
            for ligne in lignes:
                ligne = ligne.strip()
                if ligne.find(s_num) == 0:
                    # mettre à jour ce livre
                    ligne_trouvee = True
                    f2.write(ecrire_ligne + '\n')
                    infos.g_m("cas1 = " + ecrire_ligne)
                    infos.g_m("cas1 : on mémorise en mettant à jour")
                    infos.g_m("memoPosLivre cas1 [MAJ] : >>> " + ecrire_ligne + " <<<")
                elif len(ligne) > 5:
                    f2.write(ligne + '\n')
                    infos.g_m("cas2 = " + ligne)
            if not ligne_trouvee:
                # première mémorisation pour ce livre
                f2.write(ecrire_ligne + '\n')
                infos.g_m("cas3 = " + ecrire_ligne)
                infos.g_m("cas3 : on mémorise ce livre pour la première fois")
                infos.g_m("memoPosLivre cas3 [NEW] : >>> " + ecrire_ligne + " <<<")
        else:
            # 1er livre mémorisé
            f2 = open(fichier, "w")
            f2.write(ecrire_ligne + '\n')
            infos.g_m("cas4 = " + ecrire_ligne)
            infos.g_m("cas4 : on mémorise un premier livre")
            infos.g_m("memoPosLivre cas4 [+FILE] : >>> " + ecrire_ligne + " <<<")

        f2.close()
        infos.g_m("voir fichier " + fichier)

    # mémoriser position et progression du livre quitté
    if nb_livres_total() == 0:
        infos.m_g("on ne mémorise rien, car nb livres total = 0")
        return 0

    s_piste = str(mpc.playlist_position())
    if s_piste.startswith("volume:"):
        infos.g_m("s_piste = " + s_piste)
        infos.g_m("on ne mémorise rien, car s_piste.startswith volume")
    else:
        # on mémorise le num4c, la piste, la progression, l'horodatage
        # 0418|1|0:11|2020-02-26 00:20:04
        s_horo = infos.jour_heure_actu()
        infos.m_g("debug1")
        print("debug nb livres total = {}".format(nb_livres_total()))
        s_num = lv_num4c()  # 0152 (colonne "n=" dans pipe.txt)
        infos.m_g("debug2")
        s_prog = mpc.status_progression()[0]

        memo_ligne = "{}|{}|{}|{}".format(s_num, s_piste, s_prog, s_horo)
        memo_pos_livres_maj(bbl_pos_livres(), memo_ligne)


def memo_stop_module():
    """ quitter module (touche 0) : mémoriser où on est """
    infos.m_g("aba memo_stop_module")
    memo_pos_livre_aba()


def menu_changer_biblio():
    """ changer de bibliothèque par un menu vocal """
    explication = \
    "choisissez une bibliothèque de 1 à {0}, " + \
    "pressez ensuite Entrée pour la lancer, " + \
    "ou bien zéro pour sortir de ce menu, " + \
    "vous êtes actuellement dans la bibliothèque {}".format(bbl_nom())
    commun.menu_vocal("aba_bibliotheques", "bibliothèque", explication)


def menu_suppr_lvr():
    """ menu Entrée suppression livre(s) """
    msg = "Appuyer sur 1 puis Entrée pour supprimer le livre présent"
    infos.pdrNew(msg)
    msg = "ou bien sur 2 pour supprimer tous les livres présents"
    infos.pdrNew(msg)
    choix = input()
    if choix == "1":
        supprimer_livre()   # on ne vérifie pas que le livre est présent
                            # cela permet de forcer la suppression du dossier
                            # s'il existe
    elif choix == "2":
        supprimer_livres()
    else:
        infos.pdrNew("Suppression annulée")


def menu_voc11():
    """ menu spécial aba """
    commun.menu_vocal("aba_mnu11", "audiolivres")


def message_espace_insuffisant(s_espace):
    """ message générique aba d'espace insuffisant
        PAs de prononciation ici,
        renvoi d'un tableau de messages pour pdrNew
    """
    mega_octets_occupes = commun.du(cfg.M_MUSIC + "/aba")
    bbll = bbl_langue()
    if bbll == cfg.LG_FRANCAIS:
        voc_dl2 = "vous ne pouvez pas télécharger " + \
            "de livre audio, " + s_espace + ", " + \
            "essayez de faire de la place " + \
            "en sélectionnant les livres présents avec les touches 111, " + \
            "puis en les effaçant."
        # autre message possible :
        # espace restant insuffisant pour télécharger,
        # veuillez libérer d'abord de l'espace.
        # Pour cela, placez-vous sur un livre présent,
        # puis effacez-le avec 987, ou bien 11 pour
        # ouvrir le menu vocal, puis la touche 9,
        # pour supprimer plusieurs livres d'un coup.
        voc_dl1 = voc_dl2
    elif bbll == cfg.LG_ANGLAIS:
        voc_dl1 = "download not possible because space is missing "
        voc_dl2 = "download not possible because space is missing "
    elif bbll == cfg.LG_ALLEMAND:
        voc_dl1 = "Herunterladung unmöglich, Raum zu kurz "
        voc_dl2 = "Herunterladung unmöglich, Raum zu kurz "
    # pas de téléchargement, ne pas annoncer le résumé du livre
    arr_msg_insuff = [
        voc_dl1,
        voc_dl1,
        voc_dl2,
        voc_dl2
        ]
    return arr_msg_insuff


def nb_livres_favoris():
    """ renvoie le nombre de livres consignés en favoris
        IMPORTANT : un favori peut être présent ou absent
    """
    num4c_trouves = infos.ini_get("aba_favoris_" + bbl_actu(), "")
    if num4c_trouves == "":
        return 0
    else:
        return len(num4c_trouves.split(","))


def nb_livres_prezents(cle_biblio = None):
    """ renvoie le nombre (int) de sous-dossiers
        dans dir_search
        ou, si argument vide,
        dans bbl_folder() --> /m/music/aba/<folder>

        TODO : améliorer en vérifiant si dossiers de taille nulle existent
        TODO IMPORTANT : pas valable pour usb anarchie
    """
    if cle_biblio is None:
        cle_biblio = bbl_actu()

    # cas particulier : usb anarchie
    if cle_biblio == cfg.bbl_CLE_USB_ANARCHIE:
        return nb_livres_total()

    # les autres cas
    dir_search = bbl_folder(cle_biblio)
    if commun.dir_exists(dir_search):
        cmd = "ls -1 {} | wc -l".format(dir_search)
        nb_dossiers = int(mpc.sub_proc(cmd))
        return nb_dossiers
    else:
        return 0


def nb_livres_total():
    """ nombre de livres (lignes) da la sélection,
        càd du tableau global sel_lvr[]
    """
    global sel_lvr
    return len(sel_lvr)


def next_trk():
    """ aller au chapitre suivant """
    # la touche 6 a deux fonctions dans le module aba
    mpc.next_trk()
    infos.m_g("piste {}".format(mpc.playlist_position()))


def prev_trk():
    """ aller au chapitre précédent """
    mpc.prev_trk()
    infos.m_g("piste {}".format(mpc.playlist_position()))


def rechercher():
    """ rechercher un mot ou une partie de mots
        dans la colonne auteur, lecteur ou titre
    """
    def aide_alphabet():
        """ récite les codes de chaque lettre
        """
        # num2lettre = {
        #  "1":"a",  "2":"b",  "3":"c",  "4":"d",  "5":"e",
        #  "6":"f",  "7":"g",  "8":"h",  "9":"i", "10":"j",
        # "11":"k", "12":"l", "13":"m", "14":"n", "15":"o",
        # "16":"p", "17":"q", "18":"r", "19":"s", "20":"t",
        # "21":"u", "22":"v", "23":"w", "24":"x", "25":"y",
        # "26":"z" }
        # nombres = num2lettre.keys()

        # utiliser num2lettre et num2lettre.keys()
        pass


    mpc.ferme_ton_clapet()
    msg = """ Appuyez sur 1, 2 ou 3, puis Entrée pour rechercher
    respectivement dans les auteurs, lecteurs et titres des livres
    """
    infos.pdrNew(msg)
    msg = """ Appuyez sur 4 puis Entrée pour rechercher
    sur les trois colonnes, ou 0 puis Entrée pour annuler
    """
    infos.pdrNew(msg)

    # 1. demander le champ de recherche
    champ = input()

    # 2. demander le mot de recherche
    mpc.ferme_ton_clapet()
    num2lettre = {
         "1":"a",  "2":"b",  "3":"c",  "4":"d",  "5":"e",
         "6":"f",  "7":"g",  "8":"h",  "9":"i", "10":"j",
        "11":"k", "12":"l", "13":"m", "14":"n", "15":"o",
        "16":"p", "17":"q", "18":"r", "19":"s", "20":"t",
        "21":"u", "22":"v", "23":"w", "24":"x", "25":"y",
        "26":"z" }
    nombres = num2lettre.keys()
    mot = ""
    dans_quoi =  {
         "1":"auteurs",
         "2":"lecteurs",
         "3":"titres" }
    msg = """ Pour définir le mot à rechercher,
        tapez 1 puis Entrée pour la lettre, a,
        10 puis Entrée pour la lettre, j,
        20 puis Entrée pour la lettre, t,
        puis Entrée tout court pour lancer la recherche
    """
    infos.pdrNew(msg)

    if champ in dans_quoi.keys():
        while True:
            carac = input()
            mpc.ferme_ton_clapet()
            if carac == "":
                break
            if carac in nombres:
                # transformer 1 en a, 2 en b, 3 en c, ..., 25 en y, 26 en z
                lettre = num2lettre[carac]
                mot = mot + lettre
                infos.pdrNew("lettre " + lettre)
            else:
                infos.pdrNew("mauvais nombre, désolé")
        # recherche sur le mot
        champ_dmd = dans_quoi[champ]
        infos.m_g("recherche du mot '{}' dans les {}".format(mot, champ_dmd))

    # 3. lancer la recherche sur le champ demandé
    dans_champ =  {
         "1":"a",
         "2":"l",
         "3":"t" }
    champ_alt = dans_champ[champ]
    occurences = {}
    # initialiser occurences{}
    if mot != "":
        for lvr in all_lvr:
            if mot.lower() in lvr[champ_alt].lower():
                occurences[lvr[champ_alt]] = 0
    n = 0
    t_recherche = []
    if mot != "":
        for lvr in all_lvr:
            if mot.lower() in lvr[champ_alt].lower():
                n += 1
                t_recherche.append(lvr["n"])
                occurences[lvr[champ_alt]] += 1
    nb_corresp_mot = len(t_recherche)
    if nb_corresp_mot > 0:
        mask = "{} livres trouvés pour le mot {}"
        infos.pdrNew(mask.format(nb_corresp_mot, mot))

        mask = "{} {} différents trouvés"
        infos.m_g(mask.format(len(occurences), champ_dmd))

        num4c_trouves = ",".join(t_recherche)
        slct_get_build(num4c_trouves)
    else:
        infos.pdrNew("Aucun livre trouvé pour le mot demandé")
        num4c_trouves = ""


def reprendre_direct():
    """ reprendre directement le dernier audiolivre
    """
    msg = "reprendre directement le dernier audiolivre"
    infos.g_m(msg)
    reprendre("direkt")


def reprendre(direkt = ''):
    """ lit dernier livre lu en quittant module """
    # ici, on définit simplement le numéro du livre à lire,
    # puis on lance lire_livre_actuel()

    def dev_afficher_numeros_utiles():
        """ affiche quelques nuémros de livres utiles de tests
        """
        msg = "numéros de livres intéressants"
        infos.m_g(msg)
        numeros_audio_cite = " courts : 642, 748, 823, zip : 762, 763, mp3 : 869"
        numeros_litteaudio = " courts :  28, 47, 70, 71, 304, 305, 306"
        numeros_librivox_fr = " 4"
        numeros_divers_src = "4, 5, 6"
        msg = "biblio audiocité   : " + numeros_audio_cite
        msg = "biblio littérature : " + numeros_litteaudio
        msg = "biblio librivox_fr : " + numeros_librivox_fr
        msg = "biblio divers      : " + numeros_divers_src
        infos.m_g(msg)

    def init_reprendre(direkt = ""):
        """ initialisation variables selon biblio actuelle """
        mpc.ferme_ton_clapet()
        if direkt == 'pas_lancement des audiolivres':
            pass
        elif direkt == '':
            msg = "audiolivres"
            infos.g_m("DBG lancement des audiolivres")
            # infos.pdrNew(["", msg, msg, msg])
            # commun.faire_pause(3, True)    # pause de 2s

        infos.ini_set("py_current_module", "aba")
        infos.ini_set("mode_consultation", "consulter")
        bib_folder = bbl_folder()
        # dossier /m/music/aba/perso_zip, /m/music/aba/litteratureaudio, ...
        cmd = "[ -d " + bib_folder + " ] || mkdir -p " + bib_folder
        retour_cmd = mpc.sub_proc(cmd)

    def cnt_numeroter(quoi, liste1, nydplatufx = None):
        """ renvoie la liste fournie, numérotée """
        # cnt = Créer Nouvelles Tables
        infos.m_g("===== " + quoi + " =====")
        n = 1000
        liste2 = []
        dico = {}
        compter = {}
        for nom in liste1:
            n += 1
            num3c = str(n)[-3:]
            num_nom = num3c + ":" + nom   # 001:abbé Ricard
            liste2.append(num_nom)
            dico[nom] = num3c
            compter[nom] = 0
            if n < 1004:
                infos.m_g(num_nom)
        infos.m_g("...")
        infos.m_g(num_nom)   # le dernier élément
        infos.m_g("")   # ligne vide

        if not nydplatufx is None:
            global all_lvr
            for L in all_lvr:
                compter[L[nydplatufx]] += 1   # compter["Daniel Luttringer"] += 1
            return liste2, dico, compter
        else:
            return liste2, dico

    def cnt_pgcd(list_urls):
        """ renvoie la partie commune la plus longue
            pour tous les éléments
        """
        longueur_maxi = 1000
        # longueur la plus courte du tableau
        for i in list_urls:
            # ~ print(len(i))
            if len(i) < longueur_maxi:
                # ~ print(i)
                longueur_maxi = len(i)
        # ~ m_g("longueur_maxi = " + str(longueur_maxi))
        # la chaîne commune n'excédera pas la longueur longueur_maxi
        use_previous_modele = False
        debut_commun_a_tous = ""
        for i in range(longueur_maxi):
            modele = list_urls[0][:i]
            # ~ print("modele1 = " + modele)
            for e in list_urls:
                # ~ print("e[:i]  = " + e[:i])
                # ~ print("modele = " + modele)
                # ~ print()
                if e[:i] != modele:
                    # ce modèle ne marche plus
                    use_previous_modele = True
                    break
            if use_previous_modele == True:
                debut_commun_a_tous = modele_precedent
                break
            else:
                modele_precedent = modele
        return debut_commun_a_tous

    def cnt_raccourcir_tab_url(arr_urls, clef):
        """ remplir tableau html
        """
        len_url = len(arr_urls)
        if len_url == 0:
            # ~ return "&nbsp;"
            return "     <td align=center><small>" + "&nbsp;" + "</small></td>" + '\n'

        # LE_MP3 sont habituellement unique,
        # parfois, il y a plusieurs liens
        if (clef == "LE_MP3") and (len_url > 1):
            return "     <td align=center style='background: lightgreen;'><small>" + str(len_url) + "</small></td>"
        else:
            return "     <td align=center><small>" + str(len_url) + "</small></td>"

        return str(len_url)
        # ~ elif len_url == 1:
            # ~ return arr_urls[0]

        pgcd1 = pgcd(arr_urls)
        arr2 = [ i.replace(pgcd1, "[PC]") for i in arr_urls ]
        return str(len_url)

    def creer_nouvelles_tables(biblio, all_lvr, list_auteurs, list_genres, list_lecteurs, list_url, list_duree, list_poids, list_titre):
        """ créer trois tables auteurs, genres, lecteurs,
            et un nouveau catalogue aminci
            soit au total 4 nouveaux fichiers :
            tbl_catalog.txt, tbl_auteurs.txt, tbl_genres.txt, tbl_lecteurs.txt

            les 3 listes suivantes sont non triées, mais avec des noms d'auteurs uniques :
            list_auteurs, list_genres, list_lecteurs

            gain de place dans la base de données trop faible pour être intéressant
            par contre, permet de repérer/corriger les données rapidement
        """
        chemin = cfg.BIB_DISPOS[biblio]["catalogue"]
        save_list_to_file(chemin + "/tbl_auteurs_bruts.txt", list_auteurs)
        save_list_to_file(chemin + "/tbl_lecteurs_bruts.txt", list_lecteurs)
        save_list_to_file(chemin + "/tbl_genres_bruts.txt", list_genres)

        save_list_to_file(chemin + "/tbl_url_bruts.txt", list_url)
        save_list_to_file(chemin + "/tbl_duree_bruts.txt", list_duree)
        save_list_to_file(chemin + "/tbl_poids_bruts.txt", list_poids)
        save_list_to_file(chemin + "/tbl_titre_bruts.txt", list_titre)

        # numéroter les listes
        list_auteurs_num , dico_auteurs, compte_auteurs  = cnt_numeroter("auteurs" , list_auteurs, "a")
        list_genres_num  , dico_genres, compte_genres   = cnt_numeroter("genres"  , list_genres, "y")
        list_lecteurs_num, dico_lecteurs, compte_lecteurs = cnt_numeroter("lecteurs", list_lecteurs, "l")

        list_url_num  , dico_url   = cnt_numeroter("url"  , list_url)
        list_duree_num, dico_duree = cnt_numeroter("duree", list_duree)
        list_poids_num, dico_poids = cnt_numeroter("poids", list_poids)
        list_titre_num, dico_titre = cnt_numeroter("titre", list_titre)

        # enregistrer les listes numérotées sur fichier
        save_list_to_file(chemin + "/tbl_auteurs.txt" , list_auteurs, compte_auteurs)
        save_list_to_file(chemin + "/tbl_lecteurs.txt", list_lecteurs, compte_lecteurs)
        save_list_to_file(chemin + "/tbl_genres.txt"  , list_genres, compte_genres)

        infos.m_g("Fichiers tbl créés.")

        # recréer catalog.txt --> tbl_catalog.txt
        # avec les colonnes a=, l=, y= (auteur, lecteur, genre)
        # comportant uniquement des numéros

        # 1. les dictionnaires :
        # dico_auteurs  : nomAuteur  --> numAuteur
        # dico_genres   : nomGenre   --> numGenre
        # dico_lecteurs : nomLecteur --> numLecteur

        new_catalogue = []
        for L in all_lvr:
            # nydplatufx
            nom, genre_1, duree, poids, lecteur_1 = L["n"], L["y"], L["d"], L["p"], L["l"]
            auteur_1, titre, media_url1, fiche, extrait = L["a"], L["t"], L["u"], L["f"], L["x"]

            # attention : media_url1 est une variable list
            auteur_2  = dico_auteurs[auteur_1]
            genre_2   = dico_genres[genre_1]
            lecteur_2 = dico_lecteurs[lecteur_1]
            media_url2 = "+".join(media_url1)     # il faudra raccourci avec radical commun (pgcd)

            masque = "n={}|y={}|d={}|p={}|l={}|a={}|t={}|u={}|f={}|x={}"
            # ~ masque = "{}|{}|{}|{}|{}|{}|{}|{}|{}|{}"

            # on utilise les index et les autres tables
            ligne_new = masque.format(nom, genre_2, duree, poids, lecteur_2, auteur_2, titre, media_url2, fiche, extrait)
            # on n'utilise pas d'index
            ligne_new = masque.format(nom, genre_1, duree, poids, lecteur_1, auteur_1, titre, media_url2, fiche, extrait)

            new_catalogue.append(ligne_new)
        save_list_to_file(chemin + "/tbl_CATALOG.txt", new_catalogue)
        # source : catalog.txt
        # ~ n=0001|y=nouvelles        |d=    15|p=   14|l=Plumedencr          |a=morissette Joseph ferdinand        |t=Lucien et Marie-Louise                                      |u=https://archive.org/download/LucienEtMarieLouise/Lucien%20et%20Marie-Louise.mp3                |f=nouvelles/joseph-ferdinand--morissette-lucien-et-marie-louise.html
        # ~ n=0002|y=religions        |d= 01h25|p=   80|l=Léa                 |a=Abbé regnault P.a.                |t=Chemin de croix médité                                    |u=https://archive.org/compress/chemindecroixmedite/formats=VBR%20MP3&file=/chemindecroixmedite.zip                        |f=religions/p.a.-abbe-regnault-chemin-de-croix-medite.html
        # ~ n=0003|y=contes           |d=    02|p= 1,75|l=Sabine              |a=Abbé ricouard                     |t=Contes et légendes de la Seine-Maritime. La bal des fées  |u=https://www.archive.org/download/LeBalDesFees/Le_bal_des_fees.mp3                              |f=contes/abbe-ricouard-contes-et-legendes-de-la-seine-maritime--la-bal-des-fees.html

        # des    : tbl_catalog.txt
        # ~ n=0001|y=001|d=    15|p=   14|l=001|a=001|t=Lucien et Marie-Louise                                      |u=https://archive.org/download/LucienEtMarieLouise/Lucien%20et%20Marie-Louise.mp3                |f=nouvelles/joseph-ferdinand--morissette-lucien-et-marie-louise.html
        # ~ n=0002|y=002|d= 01h25|p=   80|l=002|a=002|t=Chemin de croix médité                                    |u=https://archive.org/compress/chemindecroixmedite/formats=VBR%20MP3&file=/chemindecroixmedite.zip                        |f=religions/p.a.-abbe-regnault-chemin-de-croix-medite.html
        # ~ n=0003|y=003|d=    02|p= 1,75|l=003|a=003|t=Contes et légendes de la Seine-Maritime. La bal des fées  |u=https://www.archive.org/download/LeBalDesFees/Le_bal_des_fees.mp3                              |f=contes/abbe-ricouard-contes-et-legendes-de-la-seine-maritime--la-bal-des-fees.html
        pass

    def save_list_to_file(fichier, list_quoi, dico_compte = None):
        """
        """
        if dico_compte is None:

            list_quoi.sort()
            liste_triee = "\n".join(list_quoi)
            with open(fichier, "w") as f:
                corps = "# fichier créé par aba.py\n" + liste_triee
                f.write(corps)
        else:
            # comptabiliser pour chaque élément
            liste_triee = list(list_quoi)
            with open(fichier, "w") as f:
                for i in list_quoi:
                    d = dico_compte[i]
                    # print(i + ":" + str(d), file=f)
                    f.write(i + ":" + str(d))
            f.close()

    def usb_dossiers_audio(src_dir):
        """ renvoie une liste de dossiers contenant des fichiers audio,
            sauf ceux commençant par /m/music,
            càd avec extensions mp3, m4a, ogg, wav, wma
            utile pour lire des audiolivres copiés de façon anarchique
            sur une clé usb

            output : list_audio_folders[]
        """
        # TODO : vérifier que les fichiers audio ne sont pas de taille nulle
        if cfg.CLE_USB_BROWSE_M_MUSIC == True:
            # on prend toute la clé
            list_folders = commun.folders_recurse(src_dir)
        else:
            # on prend tout sauf /m/music
            list_folders_all = commun.folders_recurse(src_dir)
            list_folders = []
            for d in list_folders_all:
                # supprimer les dossiers commençant par /m/music/
                # car ils sont prévus pour être lus par mpd/jk2019 en natif
                if d.startswith(cfg.M_MUSIC):
                    pass
                else:
                    list_folders.append(d)

        list_audio_folders = []
        for un_dossier in list_folders:
            liste_fichiers = commun.files_simple(un_dossier)
            nb_fichiers = len(liste_fichiers)
            if nb_fichiers > 0:
                # analyser les fichiers du dossier "un_dossier"
                nb_audio = 0
                for i in liste_fichiers:
                    i_low = i.lower()
                    if (
                      (i_low.endswith(".mp3")) or
                      (i_low.endswith(".m4a")) or
                      (i_low.endswith(".ogg")) or
                      (i_low.endswith(".wav")) or
                      (i_low.endswith(".wma"))
                      ):
                        nb_audio += 1
                if nb_audio > 0:
                    if loglevel() > 0:
                        infos.m_g("{:>3s} fichiers, {:>3d} audio, {}".format(str(nb_fichiers), nb_audio, un_dossier))
                    list_audio_folders.append(un_dossier)
        return list_audio_folders

    def usb_charger_livres():
        """ chercher les livres sur la clé usb,
            création de la list all_lvr[]
        """
        msg = "usb_charger_livres() recherchent tous les fichiers audio" + \
            "sur la clé usb, à l'exception de ceux qui sont dans " + \
            "/m/music. Ne prend pas en compte les fichiers .zip"
        infos.g_m(msg)
        msg = "La bibliothgèque 'lire les livres de la clé u s b'" + \
            "permet de lire les fichiers copiés de façon anarchique" + \
            "par une bonne âme."
        infos.g_m(msg)
        msg = "Il y aura peut-être une possibilité de fairte du ménage" + \
            "sur une clé trop remplie."
        infos.g_m(msg)

        # dossiers avec fichiers audio
        m_audio_folders = usb_dossiers_audio("/m")
        if len(m_audio_folders) == 0:
            return False
        masque = "Nb dossiers contenant au moins un fichier audio, et hors du dossier /m/music : {}"
        print(masque.format(len(m_audio_folders)))

        # créer le tableau all_lvr []
        global all_lvr
        all_lvr = []
        # 10 champs = nydplatufx
        # n=0001|y=Romans|d=13:25:00|p=732|l=Victoria|a=MIRBEAU, Octave|t=Le Journal d'une femme de chambre|u=http://www.litteratureaudio.net/mp3/Octave_Mirbeau_-_Le_Journal_d_une_femme_de_chambre.zip|f=mirbeau-octave-le-journal-dune-femme-de-chambre.html|x=http://www.litteratureaudio.net/mp3/Octave_Mirbeau_-_Le_Journal_d_une_femme_de_chambre_Chap01.mp3
        # n=0002|y=Romans|d=5:30:00|p=|l=Jean-Luc Fischer|a=H. P. Lovecraft|t=L'Affaire Charles Dexter Ward|u=http://www.litteratureaudio.org/mp3/H_P_Lovecraft_L_Affaire_CDW.zip|f=lovecraft-howard-phillips-laffaire-charles-dexter-ward.html|x=http://www.litteratureaudio.org/mp3/H_P_Lovecraft_L_Affaire_CDW_Ch_I_1.mp3
        # n=0003|y=Romans|d=28:30:00|p=|l=Cocotte|a=DUMAS, Alexandre|t=Les Trois Mousquetaires|u=http://www.litteratureaudio.org/mp3/Alexandre_Dumas_-_Les_trois_Mousquetaires_chap0-10.zip+http://www.litteratureaudio.org/mp3/Alexandre_Dumas_-_Les_trois_Mousquetaires_chap11-20.zip+http://www.litteratureaudio.org/mp3/Alexandre_Dumas_-_Les_trois_Mousquetaires_chap21-30.zip+http://www.litteratureaudio.org/mp3/Alexandre_Dumas_-_Les_trois_Mousquetaires_chap31-40.zip+http://www.litteratureaudio.org/mp3/Alexandre_Dumas_-_Les_trois_Mousquetaires_chap41-50.zip+http://www.litteratureaudio.org/mp3/Alexandre_Dumas_-_Les_trois_Mousquetaires_chap51-60.zip+http://www.litteratureaudio.org/mp3/Alexandre_Dumas_-_Les_trois_Mousquetaires_chap61-68.zip|f=dumas-alexandre-les-trois-mousquetaires.html|x=http://www.litteratureaudio.org/mp3/Alexandre_Dumas_-_Les_Trois_Mousquetaires_Chap00_Introduction.mp3
        # n=0004|y=Romans|d=6:43:00|p=182|l=Damien Genevois|a=VERNE, Jules|t=Le Tour du monde en 80 jours|u=http://www.litteratureaudio.net/mp3/Jules_Verne_-_Le_Tour_du_monde_en_80_jours.zip|f=jules-verne-le-tour-du-monde-en-80-jours.html|x=
        # n=0005|y=Romans|d=19:0:00|p=|l=Monique Vincens|a=PROUST, Marcel|t=Du côté de chez Swann|u=http://www.litteratureaudio.net/mp3/Marcel_Proust_-_Du_Cote_de_chez_Swann_L1_Combray_V2.zip+http://www.litteratureaudio.net/mp3/Marcel_Proust_-_Un_Amour_de_Swann.zip+http://www.litteratureaudio.net/mp3/Marcel_Proust_-_Du_Cote_de_chez_Swann_L3_Nom_de_pays_-_le_nom.zip|f=proust-marcel-du-cote-de-chez-swann.html|x=http://www.litteratureaudio.net/mp3/Marcel_Proust_-_Du_Cote_de_chez_Swann_L1_Combray_Introduction.mp3
        num = 0
        for audio_folder in m_audio_folders:
            files_audio_path_usb = commun.files_audio_simple(audio_folder)
            num += 1
            num4c = zero_on(num)
            genre = ""
            duree = ""
            poids = ""
            lecteur = ""
            # ci-dessous, ok pour dossier
            # Pierre-Gilles de Gennes/Laser fermeture éclair
            # auteur = mpc.sub_proc('basename "$(dirname "{}")"'.format(audio_folder))
            auteur = ""
            titre = mpc.sub_proc('basename "{}"'.format(audio_folder))
            arr_url_media = files_audio_path_usb
            fiche = ""
            extrait = ""
            un_livre = {
                "n": num4c    , # numéro à 4 chiffres
                "y": genre    , # type
                "d": duree    , # durée
                "p": poids    , # poids (string : "5.78", "12", ...)
                "l": lecteur  , # lecteur
                "a": auteur   , # auteur
                "t": titre    , # titre
                "u": arr_url_media, # url mp3 ou zip
                "f": fiche    , # url fiche
                "x": extrait  , # extrait
                "type_url": "mp3",  # type url : mp3, zip
                # "type_url": "usb_mp3",  # type url : mp3, zip
            }
            all_lvr.append(un_livre)
            infos.m_g("usb, titre " + str(num) + " : " + str(titre))
        infos.m_g("usb, titres trouvés sur la clé (hors /m/music) : " + str(num))
        # creer_html_all_lvr(all_lvr, "/m/cle_usb_all_lvr.html")
        return True

    def perso_zip_charger_livres():
        """ chercher les livres zip sur http://gangand.net/...
            créer la list all_lvr[]
        """
        # procéder pour cette bibliothèque comme pour litterature audio, librivox ...
        # en créant le tableau all_lvr[] adéquat
        cmd = 'wget -q http://gangand.net/aa/audiolivres/perso_zip -O - | grep zip | sort | grep -o -E "href=\\"[^>]*"'
        liste_brute_1 = mpc.sub_proc(cmd)

        cmd_poids = 'wget -q http://gangand.net/aa/audiolivres/perso_zip -O - | grep zip'
        les_poids = mpc.sub_proc(cmd_poids)
        print("*" * 30 + " bbl_perso_zip " + "*" * 30)
        print(liste_brute_1)
        ############################################################
        # href="antoine_blondin_-_rive_gauche_avec_pierre_assouline.zip"
        # href="raymond_devos_-_mon_chien_c_est_quelqu_un.zip"
        # href="raymond_devos_-_un_ange_passe.zip"
        # href="raymond_devos_-_volume_1.zip"
        # href="San_Antonio_-_En_peignant_la_girafe.zip"
        ############################################################

        # TODO : récupérer le poids :
        # http://gangand.net/aa/audiolivres/perso_zip/?C=N;O=D

        print("*" * 30 + " bbl_perso_zip fin " + "*" * 30)

        liste_brute_2 = liste_brute_1

        liste_brute_1 = liste_brute_1.replace('"', '')
        liste_brute_1 = liste_brute_1.replace('href=', '')
        noms_zip = liste_brute_1.split("\n")

        liste_brute_2 = liste_brute_2.replace('href="', "http://gangand.net/aa/audiolivres/perso_zip/")
        liste_brute_2 = liste_brute_2.replace('"', '')
        fichiers_zip = liste_brute_2.split("\n")

        # fichiers zip en ligne
        if len(fichiers_zip) == 0:
            return False
        masque = "Nb fichiers zip trouvés sur http://gangand.net/... : {}"
        print(masque.format(len(fichiers_zip)))

        # créer le tableau all_lvr []
        global all_lvr
        all_lvr = []
        num = 0
        for un_zip in fichiers_zip:
            # un_zip est de la forme :
            # http://gangand.net/aa/audiolivres/perso_zip/antoine_blondin_-_rive_gauche_avec_pierre_assouline.zip
            auteur_titre = noms_zip[num]
            new_num4c = auteur_titre
            # auteur_titre est de la forme :
            # antoine_blondin_-_rive_gauche_avec_pierre_assouline.zip
            auteur_titre = auteur_titre.replace(".zip", "")
            auteur_titre = auteur_titre.replace("_", " ")
            auteur_titre = auteur_titre.replace("%c3%a9", "é")
            auteur_titre = auteur_titre.split(" - ",2)

            auteur = "auteur zip"
            titre = "titre zip"
            if len(auteur_titre) == 1:
                auteur = "auteur inconnu"
                titre = auteur_titre[0]
            elif len(auteur_titre) == 2:
                auteur = auteur_titre[0]
                titre = auteur_titre[1]
            num += 1
            num4c = zero_on(num)
            # TODO : Raymond_Devos_-_Je_zappe_1992
            # au lieu de 0001
            # num4c = new_num4c.replace(".zip", "")
            genre = ""
            duree = ""
            poids = ""
            lecteur = ""
            arr_url_media = [ un_zip,]
            fiche = ""
            extrait = ""
            un_livre = {
                "n": num4c    , # numéro à 4 chiffres
                "y": genre    , # type
                "d": duree    , # durée
                "p": poids    , # poids (string : "5.78", "12", ...)
                "l": lecteur  , # lecteur
                "a": auteur   , # auteur
                "t": titre    , # titre
                "u": arr_url_media, # url mp3 ou zip
                "f": fiche    , # url fiche
                "x": extrait  , # extrait
                "type_url": "zip",  # type url : mp3, zip
                # "type_url": "usb_mp3",  # type url : mp3, zip
            }
            all_lvr.append(un_livre)
        # creer_html_all_lvr(all_lvr, "/m/perso_zip_all_lvr.html")
        return True

    def creer_html_all_lvr(livr_bruts, f_html):
        """ crée une vue du tableau dynamique
            des livres sur clé usb quelconque
            (càd une clé envoyée par un/une amie à France)
        """
        entete = "<tr> \
                <td><b>num4c</b></td> \
                <td><b>genre</b></td> \
                <td><b>duree</b></td> \
                <td><b>poids</b></td> \
                <td><b>lecteur</b></td> \
                <td><b>auteur</b></td> \
                <td><b>titre</b></td> \
                <td><b>url[0]</b></td> \
                <td><b>fiche</b></td> \
                <td><b>extrait</b></td> \
            </tr>"
        corps_html = "<html><head><title>Livres sur clé usb</title></head>" + \
            "<body><center><table border=1 cellpadding=4 cellspacing=2>"
        corps_html += entete
        lignes = ""
        for i in livr_bruts:
            les_champs = \
                (i["n"], i["y"], i["d"], i["p"], i["l"], i["a"], i["t"], i["u"][0], i["f"], i["x"])
            ligne = "<tr><td>" + \
                "</td><td>".join(les_champs) + \
                "</td></tr>" + "\n"
            lignes += ligne
        corps_html += lignes
        corps_html += "</table></center></body></html>"
        with open(f_html, "w") as f:
            f.write(corps_html)
        infos.m_g("Le fichier {} a été créé.".format(f_html))

    def verif_dir_audiolivres():
        """ si /m pas monté, on quitte
            si /m/memo/aba pas créable, on quitte
        """
        if commun.m_is_mounted() == False:
            msg = "Problème, la clé u s b ne semble pas détectée, " + \
                "abandon des audiolivres."
            infos.pdrNew(msg)
            return False

        # vérif "/m/memo/aba" existe (ABA_MEMO_DIR)
        dossier = cfg.ABA_MEMO_DIR
        if not commun.dir_exists(dossier):
            commun.make_dir(dossier)
            if not commun.dir_exists(dossier):
                msg = "Erreur lors de la création du dossier mémoire audiolivres"
                infos.pdrNew(msg)
                return False
        return True


    ########################################
    #            reprendre aba             #
    ########################################
    # mettre False ci-dessous pour ne pas
    # reprendre_directement la lecture ("annoncer" en fait)
    REPRENDRE_ANNONCER = True
    # REPRENDRE_ANNONCER = False

    infos.m_g("B pour dbg_var_all_lvr(), étude variables ESSENTIELLES")

    mpc.ferme_ton_clapet()
    if verif_dir_audiolivres() == False:
        return 0

    init_reprendre(direkt)
    global all_lvr
    chargement_livres = False
    if bbl_actu() == cfg.bbl_CLE_USB_ANARCHIE:
        chargement_livres = usb_charger_livres()
        # DOKU_enregistrer_dans_tableur_html()
    elif bbl_actu() == cfg.bbl_PERSO_ZIP:
        chargement_livres = perso_zip_charger_livres()
        # DOKU_enregistrer_dans_tableur_html()
    else:
        chargement_livres = charger_all_lvr()
        # DOKU_enregistrer_dans_tableur_html()

    global ouverture_biblio
    if (chargement_livres == True) and (direkt == 'direkt'):
        build_selektion()
        ouverture_biblio = True
        lire_livre_actuel("lire", direkt)
    elif chargement_livres == True:
        bn = bbl_nom()
        nb_ref = len(all_lvr)
        nb_prez = nb_livres_prezents()
        nb_favo = nb_livres_favoris()
        space = commun.du(bbl_folder())

        # découper les messages vocaux permet leur création plus rapide
        # et perturbent moins l'utilisateur en réduisant la durée
        # des moments de silence

        # "ouverture de la bibliothèque audiocité classiques"
        masque1 = "bibliothèque {}"
        msg1 = masque1.format(bn)
        masque2 = "ouverture de la bibliothèque {}"
        msg2 = masque2.format(bn)
        infos.pdrNew( [ msg1, msg2] )

        # "2601 références disponibles dont 8 présentes et accessibles immédiatement"
        if bbl_actu() == cfg.bbl_CLE_USB_ANARCHIE:
            masque1 = "{} éléments présents"
            msg1 = masque1.format( nb_ref )
            msg2 = msg1
            infos.pdrNew( [ msg1, msg2] )
        else:
            if nb_prez > 0:
                masque1 = "{} éléments dont {} présents, qui occupent {} méga octets"
                msg1 = masque1.format( nb_ref, nb_prez, space )
                masque2 = "{} références disponibles dont {} présentes et accessibles immédiatement"
                msg2 = masque2.format( nb_ref, nb_prez )
                infos.pdrNew( [ msg1, msg2] )
            else:
                masque1 = "{} éléments dont aucun présent"
                msg1 = masque1.format( nb_ref )
                masque2 = "{} références disponibles dont aucune téléchargée"
                msg2 = masque2.format( nb_ref, nb_prez )
                infos.pdrNew( [ msg1, msg2] )

        # 12 livres sont notés dans les favoris
        if nb_favo > 0:
            if nb_favo == 1:
                msg1 = "un livre en favori"
                msg2 = "un seul livre a été enregistré comme favori"
            else:
                masque1 = "{} livres en favori"
                msg1 = masque1.format( nb_favo )
                masque2 = "{} livres sont notés dans les favoris"
                msg2 = masque2.format( nb_favo )
            infos.pdrNew( [ msg1, msg2] )


        # "les livres présents occupent un espace de 92 méga octets"
        if nb_prez > 0:
            msg1 = ""
            if nb_prez == 1:
                masque2 = "le seul livre présent occupe un espace de {} méga octets"
            elif nb_prez > 1:
                masque2 = "les livres présents occupent un espace de {} méga octets"
            msg2 = masque2.format( space )
            infos.pdrNew( [ msg1, msg2] )

        # vérifier dès maintenant l'espace libre
        # et annoncer si insuffisant
        espace_suffisant_aba()
        # annoncer la bibliothèque ouverte

        build_selektion()
        ouverture_biblio = True
        if REPRENDRE_ANNONCER == True:
            lire_livre_actuel("annoncer")
    else:
        msg = "Pas de catalogue trouvé pour " + bbl_actu() + ", " + \
            "veuillez choisir une autre bibliothèque"
        infos.pdrNew(msg)
        menu_changer_biblio()


def stats_fichiers():
    """ stats tous fichiers """

    def stats_fich1(la_liste):
        """ infos sur medias, auteurs, ... """
        # calculer durée (minutes) et taille (Mo) totales
        taille_totale = 0
        duree_totale = 0
        dic_taille = {}
        dic_duree = {}
        for el in la_liste:
            taille_el = float(el["p"])
            taille_totale += taille_el

            duree = el["d"]
            find_h = duree.find("h")
            if find_h > -1:
                # on a un format 1h ou bien 1h03
                heure = int(duree[0:find_h])
                # ~ print("vérif " + duree)
                if duree[(find_h + 1):] != '':
                    minutes = int(duree[(find_h + 1):])
                else:
                    minutes = 0
                la_duree = minutes + heure * 60
            else:
                la_duree = int(duree)
            duree_totale += la_duree
            dic_duree[str(100000 + la_duree) + el["n"]] = [el["d"], el["t"], el["a"]]
            dic_taille[str(200000 + taille_el) + el["n"]] = [el["p"], el["t"], el["a"]]

        # cf. doc_03
        r = [taille_totale, duree_totale, dic_taille, dic_duree]
        return r

    def stats_sel_lvr():
        """ statistiques sur fichiers url mp3 """
        global sel_lvr
        r = stats_fich1(sel_lvr)
        stats_voir_save("sel_lvr", len(sel_lvr), r)

    def stats_voir_save(titre, len_liste, r):
        """ afficher résultats """
        taille_totale = r[0]
        duree_totale = r[1]
        # dic_taille = r[2]
        # dic_duree = r[3]
        infos.m_g("========== Stats : " + str(len_liste) + " fichiers " + titre + " ==========")
        taille_moyenne = int(taille_totale / len_liste)
        taille_totale = int(taille_totale)
        infos.m_g("taille totale     = " + str(taille_totale) + " Mo")
        infos.m_g("       moyenne    = " + str(taille_moyenne) + " Mo")
        infos.m_g(
            "       moyenne    = " + str(taille_totale) + " / " + str(len_liste) + " = " + str(taille_moyenne) + " Mo")
        infos.m_g("")

        duree_moyenne = int(duree_totale / len_liste)
        infos.m_g("durée totale  (mn)= " + str(duree_totale) + " mn")
        infos.m_g("              (h) = " + str(int(duree_totale / 60)) + " h")
        infos.m_g("              (j) = " + str(int(duree_totale / 60 / 24)) + " j")
        infos.m_g("      moyenne (mn)= " + str(duree_totale) + " / " + str(len_liste) + " = " + str(duree_moyenne) + " mn")
        infos.m_g("")

    stats_sel_lvr()


def trk_first():
    """ aller au premier chapitre """
    if mpc.trk_first():
        aba_infos_go()


def trk_last():
    """ aller au dernier chapitre """
    if mpc.trk_last():
        aba_infos_go()


def save_num4c_actuel(num4c):
    """ sauvegarde dans py_user.ini
        du champ "n" (num4c) du livre actuel + bibliothèque actuelle.
        ex : num4c_cle_usb_anarchie=0012
             <=> le champ "n" du livre actuel de la biblio cle_usb_anarchie est 0012
        ex : num4c_litteaudio=0244
             <=> le champ "n" du livre actuel de la biblio litteaudio est 0244

        valeur lue par la fonction get_num4c_actuel()
    """
    if nb_livres_total() == 0:
        return False

    var1 = "aba_num4c_" + bbl_actu()
    val1 = num4c + "|" + infos.jour_heure_actu()
    infos.ini_set(var1, val1)


def get_num4c_actuel():
    """ renvoie le num4c du livre actuel
        de la bibliothèque actuelle """
    # vérifier que le fichier .ini renvoie un num4c valide
    # càd dans sel_n2i.keys()
    num_horo = infos.ini_get("aba_num4c_" + bbl_actu(), "0001|")
    num4c = num_horo.split("|")[0]
    global sel_n2i
    if num4c in sel_n2i.keys():
        return num4c
    else:
        # on renvoie le champ "n" de la 1ère ligne de sel_lvr (indice 0)
        if nb_livres_total() > 0:
            global sel_lvr
            infos.m_g("ERR num4c, 1er trouvé = {}".format(lv_num4c(0)))
            # infos.m_g("ERR : 3. get_num4c_actuel, dim sel_lvr[] = {}".format(len(sel_lvr)))
            return lv_num4c(0)
        else:
            return "sel_lvr vide"


def slct_auteur():    # OK
    """ lit seulement les livres du même auteur """
    def get_books():
        """ liste les livres favoris dans la biblio actuelle """
        # num4c_trouves = infos.ini_get("aba_favoris_" + bbl_actu(), "")
        # -------------------------------------------------------------
        # 0. rechercher le livre actuel
        # 1. rechercher l'auteur du livre actuel
        # 2. copier build_selektion pour trouver les livres du même auteur
        # -------------------------------------------------------------
        auteur_actuel = lv_auteur()
        infos.pdrNew("Sélection des livres de l'auteur actuel, " + auteur_actuel)

        # rechercher dans all_lvr le même auteur
        t_meme_auteur = []   # Tableau Même Auteur
        n_view = 5
        mask = "{}. auteur {}, livre {}, titre {}"
        for lv in all_lvr:
            if auteur_actuel == lv["a"]:
                t_meme_auteur.append(lv["n"])
                n_view -= 1
                if n_view > 0:
                    infos.m_g(mask.format(n_view, lv["a"], lv["n"], lv["t"]))
        if len(t_meme_auteur) > 0:
            num4c_trouves = ",".join(t_meme_auteur)
        else:
            num4c_trouves = ""

        return slct_get_build(num4c_trouves)

    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    # slct_auteur()
    mpc.ferme_ton_clapet()
    nb_found = get_books()
    if nb_found == 0:
        masque = "Aucun livre du même auteur pour la bibliothèque {}"
        msg = masque.format(bbl_nom())
    elif nb_found == 1:
        masque = "Un seul livre de cet auteur pour la bibliothèque {}"
        msg = masque.format(bbl_nom())
    else:
        masque = "{} livres de cet auteur pour la bibliothèque {}"
        msg = masque.format(nb_found, bbl_nom())
    infos.pdrNew(msg)


def slct_favoris():  # OK
    """ lit seulement les livres favoris """
    def get_books():
        """ liste les livres favoris dans la biblio actuelle """
        # selon le type de bibliothèque,
        # on va chercher les favoris à deux (ou trois ?) endroits différents

        # TODO :
        # 1. clé usb
        # --> les favoris sont stockés dans la clé usb

        # 2. perso en ligne
        # --> les favoris sont stockés sur la machine
        #     dans /home/pi, et pas /opt/jk2019 (car màj peut écraser les favoris)

        # 3. gratuits en ligne
        # --> les favoris sont stockés sur la machine
        #     dans /home/pi, et pas /opt/jk2019 (car màj peut écraser les favoris)
        # selon le type de bibliothèque,
        # on va chercher les favoris à deux (ou trois ?) endroits différents

        # TODO :
        # 1. clé usb
        # --> les favoris sont stockés dans la clé usb

        # 2. perso en ligne
        # --> les favoris sont stockés sur la machine
        #     dans /home/pi, et pas /opt/jk2019 (car màj peut écraser les favoris)

        # 3. gratuits en ligne
        # --> les favoris sont stockés sur la machine
        #     dans /home/pi, et pas /opt/jk2019 (car màj peut écraser les favoris)
        num4c_trouves = infos.ini_get("aba_favoris_" + bbl_actu(), "")
        return slct_get_build(num4c_trouves)

    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    # slct_favoris()
    mpc.ferme_ton_clapet()
    nb_found = get_books()
    if nb_found == 0:
        masque = "Aucun livre dans les favoris pour la bibliothèque {}"
        msg = masque.format(bbl_nom())
    elif nb_found == 1:
        masque = "sélection du seul livre inscrit dans la liste " + \
            "des favoris pour la bibliothèque {}"
        msg = masque.format(bbl_nom())
    else:
        masque = "sélection des {} livres favoris de la bibliothèque {}"
        msg = masque.format(nb_found, bbl_nom())
    infos.pdrNew(msg)


def slct_get_build(str_champs_n_trouves):
    """ lance build_selektion(str_champs_n_trouves)
        si str_champs_n_trouves pas vide, et
        renvoie le nombre de livres trouvés dans str_champs_n_trouves

        Est appelé par
            slct_auteur(),
            slct_favoris(),
            slct_lecteur(),
            slct_presents()
    """
    # bn = bbl_nom()
    if str_champs_n_trouves == "":
        # infos.m_g("Aucun livre favori pour la biblio " + bn)
        nb_trouves = 0
    else:
        lst_champs_n = str_champs_n_trouves.split(",")
        nb_trouves = len(lst_champs_n)

    if nb_trouves > 0:
        # msg = "livres favoris : " + ", ".join(lst_champs_n)
        # infos.m_g(msg)
        build_selektion(lst_champs_n) # ICI, liste générée des livres sélectionnés
    # else:
        # msg = "Aucun livre favori dans la bibliothèque " + bn
        # infos.m_g(msg)
    return nb_trouves


def slct_presents(): # OK
    """ lit seulement les livres téléchargés """

    def get_books():
        """ liste les livres présents dans la biblio actuelle

            les livres présents sont les dossiers
            présents dans chaque dossier biblio
            /m/music/aba/audiocite
            /m/music/aba/litteratureaudio
            /m/music/aba/librivox_fr
            /m/music/aba/librivox_de
            /m/music/aba/vorleser

            renvoie un int : nombre de livres présents,
            soir le nb d'éléments de la liste lst_champs_n
        """
        # un livre est présent si son dossier l'est dans /m/music/aba/audiocite
        # si la biblio actuelle est "bib_audio_cite"
        dossiers_num4c = commun.folders_simple_nom_seul(bbl_folder())
        str_dossiers_num4c = ",".join(dossiers_num4c)
        return slct_get_build(str_dossiers_num4c)

    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    # slct_presents()
    mpc.ferme_ton_clapet()
    if bbl_actu() == cfg.bbl_CLE_USB_ANARCHIE:
        # tous les livres sont présents pour bbl_CLE_USB_ANARCHIE
        slct_tout("silent")
        nb_found = nb_livres_total()
    else:
        nb_found = get_books()

    if nb_found == 0:
        masque = "Aucun livre présent sur la clé pour la bibliothèque {}"
        msg = masque.format(bbl_nom())
    elif nb_found == 1:
        masque = "sélection du seul livre présent sur la clé pour la bibliothèque {}"
        msg = masque.format(bbl_nom())
    else:
        masque = "sélection des {} livres présents sur la clé pour la bibliothèque {}"
        msg = masque.format(nb_found, bbl_nom())
    infos.pdrNew(msg)


def slct_bannis():
    """ lit seulement les livres favoris """
    # TODO à continuer
    pass


def slct_lecteur():
    """ lit seulement les livres du lecteur actuel """
    def get_books():
        """ liste les livres du lecteur actuel dans la biblio actuelle """
        lecteur_actuel = lv_lecteur()
        infos.pdrNew("Sélection des livres du lecteur actuel, " + lecteur_actuel)
        # rechercher dans all_lvr le même auteur
        t_meme_lecteur = []   # Tableau Même Auteur
        n_view = 5
        mask = "{}.lecteur {}, livre {}, titre {}"
        for lv in all_lvr:
            if lecteur_actuel == lv["l"]:
                t_meme_lecteur.append(lv["n"])
                n_view -= 1
                if n_view > 0:
                    infos.m_g(mask.format(n_view, lv["l"], lv["n"], lv["t"]))
        if len(t_meme_lecteur) > 0:
            num4c_trouves = ",".join(t_meme_lecteur)
        else:
            num4c_trouves = ""
        return slct_get_build(num4c_trouves)

    if nb_livres_total() == 0:      # empêche le plantage si on est
        choisir_autre_biblio_svp()  # dans une bibliothèque sans livre (usb par exemple)
        return 0

    # slct_lecteur()
    mpc.ferme_ton_clapet()
    nb_found = get_books()
    if nb_found == 0:
        masque = "Aucun livre du même lecteur pour la bibliothèque {}"
        msg = masque.format(bbl_nom())
    elif nb_found == 1:
        masque = "Un seul livre de ce lecteur pour la bibliothèque {}"
        msg = masque.format(bbl_nom())
    else:
        masque = "{} livres de ce lecteur pour la bibliothèque {}"
        msg = masque.format(nb_found, bbl_nom())
    infos.pdrNew(msg)


def filtrer_bruts(champ, valeur):
    """ renvoie une list de num4c pour le lecteur lecteur
    """
    global all_lvr
    renvoyer = []
    for i in all_lvr:
        if i[champ] == valeur:
            renvoyer.append(i["n"])
    return renvoyer


def slct_genre():
    """ lit seulement les livres du genre actuel """
    # TODO à continuer
    pass


def slct_tout(silent = ""):
    """ réinitialise la sélection """
    if nb_livres_total() == 0:
        choisir_autre_biblio_svp()
        return 0

    build_selektion()
    masque = "sélection des {} livres de la bibliothèque {}"
    if silent == "":
        msg = masque.format(nb_livres_total(), bbl_nom())
        infos.pdrNew(msg)


def wget_spider_taille(url):
    """ connaître la taille d'un fichier à télécharger """
    # cf. doc_02
    # utiliser wget --spider "http://www.litteratureaudio.org/mp3/Emile_Zola_-_La_debacle_P1.zip"
    # ...
    # Taille : 405411018 (387M) [application/zip]
    # Le fichier distant existe
    # ...
    cmd = "wget --spider " + url + " 2> /tmp/spider.txt"
    r = mpc.sub_proc(cmd)
    with open("/tmp/spider.txt", "r") as f:
        lignes = f.readlines()
    for i in lignes:
        if i.startswith("Taille : "):
            x = i.replace("Taille : ", "")
            y = x.split(" ")
            taille = y[0]
            cmd = "rm /tmp/spider.txt"
            r = mpc.sub_proc(cmd)
            return taille
    return 0


def ze_indice(num4c):
    """ indice de sel_lvr[] pour le num4c donné """
    global sel_n2i
    # num4c peut être égal à 'sel_lvr est vide'
    # si la biblio est vide (usb par exemple)
    return sel_n2i[num4c]


def ze__indice_actuel():
    """ indice de sel_lvr[] pour le num4c actuel """
    return ze_indice( get_num4c_actuel() )


def zero_on(s):
    """ formate un num4c sur 4 caractères """
    # inverse de zero_off()
    # attention : s est une string
    padder4 = "000" + str(s)
    return padder4[-4:]


def zero_off(n):
    """ enlève les 0 du début """
    # inverse : zero_on
    if len(str(n)) == 4:
        n = int(n) + 20000    #  0307 + 20000 = 20307
        n = n - 20000         # 20307 - 20000 =   307
        return str(n)         #   307
    else:
        return n


def dbg_var():

    def header():
        return """
<!DOCTYPE html>
<html>
<head>
    <title>biblio """ + bbl_actu() + """[]</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="http://gangand.net/pp/projets/xavbox/xavbox.css">
    <style>
html {
  scroll-behavior: smooth;
}
body{
    margin: 0;
    padding: 0;
    color: lightgrey;
    background-color: black;
    font-size: 1.11em;
    position: relative;
}
h2 {
    margin: 0 auto;
    text-align: center;
}
a { text-decoration: none;
    color: white;
}
a:link, a:visited {
    text-decoration: none;
    color: white;
}
.sticky1 {
    position: sticky;
    top: 0px;
    right: 0px;
    width: 600px;
/*
    background: #222;
    */
    opacity: 0.7;

}
a:hover {
    color: white;
}
    </style>
</head>
<body>
        """

    def menu():
        return """
            <div class=\"sticky1\">
            <table class=\"table_touches\">
                <tr>
                    <td><a href="#top" >Accueil   </a></td>
                    <td><a href="#sel_lvr"    >sel_lvr[...]      </a></td>
                    <td><a href="#sel_n2i"      >sel_n2i{...}        </a></td>
                    <td><a href="#all_lvr" >all_lvr[...]   </a></td>
                </tr>
            </table>
            </div>
        """

    def footer():
        return """</body>
</html>
    """

    global all_lvr
    p1 = dbg_var_gene(all_lvr, "all_lvr")
    global sel_lvr
    p2 = dbg_var_gene(sel_lvr, "sel_lvr")
    global sel_n2i
    p3 = dbg_var_sel_n2i(sel_n2i, "sel_n2i")

    full_page = header() + \
        "<a name='top'><h2>" + "Biblio " + bbl_actu() + "</h2></a>" + "<br>" + \
        menu() + p2 + p3 + p1 + footer()
    fichier = "dbg_var/" + bbl_actu() + ".html"
    with open(fichier, "w") as f:
        f.write(full_page)
    infos.m_g("{} créé".format(fichier))


def dbg_var_gene(tableau, titre, limite = 12):
    """ enregistre les variables all_lvr[], sel_lvr[]
        et sel_n2i{} dans un fichier dbg_var_all_lvr.html
        pour bien comprendre leur interaction
    """
    def td(s):
        return "<td>" + str(s) + "</td>"
    n = 0
    corps = ""
    for i in tableau:
        ligne = \
        "  <tr>" + td(str(n)) + \
            td(i["n"]) + td(i["y"]) + td(i["d"]) + td(i["p"]) + td(i["l"]) + \
            td(i["a"]) + td(i["t"]) + \
            "<td><a title=\"" + str(i["u"]) + "\">URIs</a></td>" + \
            "<td><a title=\"" + str(i["f"]) + "\">fiche info</a></td>" + \
            td(i["x"]) + \
            td(i["type_url"]) + \
        "  </tr>"
        n += 1
        corps = corps + ligne + "\n"
        if n == limite:
            break
    entete = \
        "<tr>" + \
            "<th><a title='indice tableau'>#</a></th>" + \
            "<th><a title='num4c'>n</a></th>" + \
            "<th><a title='type (roman, nouvelle, conte, ...)'>y</a></th>" + \
            "<th><a title='durée'>d</a></th>" + \
            "<th><a title='poids'>p</a></th>" + \
            "<th><a title='lecteur'>l</a></th>" + \
            "<th><a title='auteur'>a</a></th>" + \
            "<th><a title='titre'>t</a></th>" + \
            "<th><a title='URIs média'>u</a></th>" + \
            "<th><a title='fiche information'>f</a></th>" + \
            "<th><a title='extra'>x</a></th>" + \
            "<th><a title='mp3 ou zip'>type_url</a></th>" + \
        "</tr>"
    corps = "<table class=\"table_touches\" style='margin: 15px auto 25px;'>" + "\n" + entete + corps + "\n" + "</table>"
    aname = "<a name='" + titre + "'><h2>" + titre + "[...]</h2></a>" + "\n"
    return aname + corps


def dbg_var_sel_n2i(tableau, titre, limite = 12):
    """ contenu de sel_n2i
    """
    def td(s):
        return "<td>" + str(s) + "</td>"
    n = 0
    corps = ""
    tk = list(tableau.keys())
    tk.sort()
    for i in tk:
        rri = "sel_n2i[" + str(i) + "]"
        ri = sel_n2i[i]
        mask = "le livre de champ 'n' {} se trouve dans sel_lvr[{}]"
        if n > 1:
            mask = "le livre {} est sel_lvr[{}]"
        signifie = mask.format(str(i), ri)
        ligne = \
        "  <tr>" + \
            td(rri) + \
            td(ri) + \
            td(signifie) + \
        "  </tr>"
        n += 1
        corps = corps + ligne + "\n"
        if n == limite:
            break
    entete = \
        "<tr>" + \
            "<th>sel_n2i[num4c]</th><th>Index sel_lvr</th><th>Signification</th>" + \
        "</tr>"
    corps = "<table class=\"table_touches\" style='margin: 15px auto 25px;'>" + "\n" + entete + corps + "\n" + "</table>"
    aname = "<a name='" + titre + "'><h2>" + titre + "{...}</h2></a>" + "\n"
    return aname + corps


if __name__ == "__main__":
    print("Bienvenue  dans  le module aba (Audiobooks gratuits en ligne)")