#!/usr/bin/env python3
################################################################################
# BAL : Bandcamp Album Downloader
# Licence MIT
# Un logiciel qui permet de aggréger les liens d’album (et de tracks)
# à partir d’une page Bandcamp téléchargée.
### Librairy :
# Il est nécessaire d’avoir d’installer les bibliothèques python
# suivantes : youtube_dl, BeautifulSoup, requests
### TODO :
# * Récupérer la liste des urls sans télécharger la page auparavant
# * Gestion des options de téléchargement : choix de la source des
# urls, qualité de conversion, convertion ou format brut.
################################################################################

import re
import youtube_dl
import os
import shutil
import requests
from bs4 import BeautifulSoup
import time

def BandcampParser(url): #WIP
    """
    Retrieves Bandcamp's Artist and Album name for a given album url
    """
    #Open webpage with GET Method
    open=requests.get(url)
    #Website Parsing
    soup=BeautifulSoup(open.text,'html.parser')
    soup.encode("utf8")
    urls=PageExtractor(open.text)
    save = './list.txt'
    SaveUrl(urls,save)
    return open

def PageExtractor(file):
    """
    Extract a list of album and tracks url from a file (html or txt)
    """
    #Extract url from a bandcamp's website save on txt format
    urls =[]
    #This regex recovers urls
    pattern = "(https://).*?(/album/|/track/)[a-z 0-9 -]*"
    for line in  open(file): #name of the file who contains brut text save of bandcamp page
        url = re.search(pattern, line)
        if url != None:
            urls.append(str(url[0])) #Must be url[0] for avoid truncanate url on url list
    urls=list(set(urls)) #Remove duplicates url
    return urls

def SaveUrl(urls,save):
    """
    Save extracted url from PageExtrator()
    """
    file = open(save, 'w')
    for url in urls :
        file.write(url+'\n')
    file.close

def ReadUrlFile(file):
    """
    Read url from txt file
    """
    urls = []
    file = open(file, 'r')
    for line in file:
        line = line.replace('\n', '')
        urls.append(line)
    file.close
    return urls
                
def BandcampAlbumInfo(url):
    """
    Retrieves Bandcamp's Artist and Album name for a given album url
    Find alse the icon of the album
    """
    #Open webpage with GET Method
    open=requests.get(url)
    #Website Parsing
    soup=BeautifulSoup(open.text,'html.parser')
    soup.encode("utf8")
    #Retrieve all the info from the name section of a album
    meta1=soup.findAll('div',attrs={'id':'name-section'})
    for div in meta1: #On retire du div l’artiste et l’album
        artist = (div.find('a'))
        album = (div.find('h2', attrs={'class':'trackTitle'}))
    meta2=soup.findAll('div',attrs={'class':'middleColumn'})
    for div in meta2:
        jacket = (div.find('a', attrs={'class':'popupImage'}))
    #Data cleaning
    artist = artist.get_text() #Remove HTML language, keep only text
    artist = artist.replace('\n', '')#remove breaklines
    artist = artist.replace('/', '-')
    artist = artist.strip() #Remove excess whitespace
    album = album.get_text()
    album = album.replace('\n', '')
    album = album.replace('/','-')
    album = album.strip()
    jacket = jacket.get('href') #Grab url of jacket
    #jacket return 2 url. A great picture and thumbail. I try to take the biggest.
    return artist,album,jacket

def Bandcamp_dl(urls,origin_path):
    """
    Use Youtube-dl to download bandcamp album.
    """
    #Log system
    log = open(origin_path +'log-dl-'+time.strftime("%Y-%m-%d-%H.%M")+'.txt', 'w')
    dl_sum = len(urls) #Only for information
    dl_upd = 0
    for url in urls:
        #Grab artist and album name
        info=BandcampAlbumInfo(url) #grab artist and album name
        #Create folder for each artist and album
        path = origin_path + "/"+info[0]+"/"+info[1]+"/"
        shutil.rmtree(path, ignore_errors=True) #Check if folder exist already, and delete them if true
        os.makedirs(path) #Creates folders if necessary, supports recursive creation
        #for ydl_options see : https://github.com/ytdl-org/youtube-dl/blob/master/README.md#output-template
        # or : https://github.com/ytdl-org/youtube-dl/blob/master/README.md#embedding-youtube-dl
        ydl_opts = {
            'outtmpl': path + "%(title)s" + '.' + "%(ext)s",
            'continue_dl': True,
            'ignoreerrors': True,
            'format': 'bestaudio/best',
            'postprocessors': [{
                'key': 'FFmpegExtractAudio',
                'preferredcodec': 'mp3',
                'preferredquality':'192',
            }]
        }
        with youtube_dl.YoutubeDL(ydl_opts) as ydl:
            ydl.download([url])
            log.write(url+'\n')
        #Download jacket
        response = requests.get(info[2]).content
        with open (path + info[1] + '.jpg', 'wb') as pic:
            pic.write(response)
        pic.close()
        log.write(url + ";" + time.strftime("%Y-%m-%d-%H.%M.%S") + '\n')
        dl_upd += 1
        #$
        print ("\n\n Téléchargement", dl_upd, "sur", dl_sum, "effectué !\n\n")
    log.close

def help ():
    print ("""
    Pour utiliser ce logiciel, il est necéssaire de procéder par
    étape.\n\n
    **Première étape :**\n
    Il est necéssaire d’alimenter BAL (le logiciel) avec une liste
    d’url. Deux options sont possibles :\n
    - w (non-disponible) : cette fonction demande une url en
    entrée. À partir de l’url donnée, elle va charger la page, puis
    récupérer l’ensemble des urls contenue dans la page (attention :
    aucun support de javascript est prévu). Il sera possible
    d’optionnellement sauvegarder la liste d’url dans un fichier
    texte.\n\n
    - f : récupère l’ensemble des urls présentes dans n’importe
    fichier texte (conseillé : html ou txt). Une regex va chercher les
    urls d’album et de pistes. Il sera possible optionnellement de
    sauvegarder la liste obtenu dans une nouveau fichier texte.\n\n
    - d : à partir de la liste obtenu via "f" ou "w", cette
    fonctionnaltié va télécharger l’ensemble des musiques de la
    liste. Il sera demandé un dossier de destination. Il faut utiliser
    des slash à la unix et non pas des anti-slash.\n
    \t+ L’image de l’album sera téléchargé, prendra le nom de l’album,
    qualité 10 ;\n
    \t+ Un fichier de log sera créé (csv) dans la racine du dossier de
    destination, qui comprendra l’url téléchargé et un timestamp.\n\n
    - o (non-disponible) : une fonctionnalité de paramétrage
    d’option. Il est purger la liste actuellement présente dans le
    logiciel, d’afficher diverses informations, de choisir le type de
    conversion, et la qualité de conversion.
""")


#
# Main 
# 

print ("""
===========================
 BandCamp Album Downloader
===========================

Version 1.0
Sous licence libre GPLv3
""")

choice = None
while choice != 'q':
    choice=input("""
        Liste des choix :
        ~~~~~~~~~~~~~~~~~
        w : Télécharger le contenu d’une page web (wip)
        f : Saisie du fichier source
        d : Télécharger les musiques
        o : Options (wip)
        h : aide
        q : quitter

        Votre choix : """)
    if choice == 'w':
        url=input("""
        Veuillez saisir une url. L’ensemble des urls présents sur les pages seront
        téléchargés. Ne supporte pas javascript :
        """)
        print("\n…Téléchargement de la page…\n")
        print("en construction")
    #Extraction des urls, sauvegarde des urls
    if choice == 'f':
        file=input("""
        Veuillez saisir le fichier d’entrée téléchargé depuis bandcamp (txt ou html) : 
        """)
        print ('\nExtraction des Urls...\n')
        urls = PageExtractor(file)
        print ("\nRéalisé !\nNombre d’url :", len(urls))
        dl_file=input("""
        Souhaitez-vous enregistrer la liste des url extraites ?
        o/N :""")
        if dl_file == 'o' :
            save_file=input("""Veuillez saisir le nom du fichier de
            sauvegarde :""")
            save_file_dir = os.path.dirname(save_file)
            # Check if destination folder exists
            while os.path.exists(save_file_dir) == False :
                save_file=input("""Oups ! Il semble que le chemin donné est invalide, veuillez le ressaisir :""")
                save_file_dir = os.path.dirname(save_file)
            SaveUrl(urls,save_file)
            print ("\n ---Sauvegarde Réalisée---")

    #Téléchargement des zicos
    #TODO : Choix de la source (liste PageExtrator, ou fichier SaveUr)
    if choice == 'd' :
        path=input("""
        Veuillez saisir le dossier où seront placés les musiques téléchargé\n
        Note : un couple de dossier par Artist/Album sera créé automatiquement\n
        Chemin :
        """)
        #Check if directory exists
        while os.path.exists(path) == False :
            path=input ("""
            \nOups ! Il semblerait que le chemin donné soit invalide,
            Veuillez le resaisir :
            """)
            
        Bandcamp_dl(urls,path)
        print ("\n --- TÉLÉCHARGEMENT RÉALISÉ ---\n")
    if choice == 'o':
        print("\n…En construction…\n")
    if choice == 'h':
        help()
    if choice == 'q':
        print ("Fermeture du programme")
