Articolo originale: How to Download and Trim MP3s from YouTube with Python

Siamo tutti diversi, ma penso che quasi tutti amiamo ascoltare la musica.

Se vuoi tenere una versione locale degli stream audio che ascolti spesso, dovrai scaricare questi file. A volte potresti anche voler tagliare una porzione di questo file audio invece di avere solo il file intero disponibile.

Puoi sviluppare uno script di Python per fare esattamente queste cose. Puoi anche ampliarlo con delle funzionalità aggiuntive se preferisci. E ti mostrerò come farlo in questo tutorial.

Se hai mai usato internet prima d'ora, probabilmente sei consapevole del fatto che i problemi di copyright possono dare fastidio a molte persone - qualsiasi sia la loro posizione su come dovrebbero essere i contenuti gratuiti.

Anche la stessa libreria che andremo ad usare ha avuto la sua buona parte di problemi di copyright.

Fortunatamente, c'è del materiale non protetto da copyright disponibile con cui possiamo sia divertirci che giocare nei nostri programmi. Per questo motivo, in questo tutorial useremo il brano non protetto da diritti d'autore del quarto movimento della nona sinfonia di Beethoven.

Questa guida dà per scontato che userai i seguenti metodi per scaricare del materiale non protetto da copyright. Non usare queste informazioni per infrangere alcun copyright!

Cosa Faremo in Questo Tutorial

Per prima cosa installeremo la dipendenza di base, FFMPEG. Poi installeremo la libreria youtube-dl (che funziona anche con Vimeo ed altre piattaforme) per scaricare audio da un URL di YouTube e usarlo nel nostro codice di Python.

Poi scaricheremo la libreria pydub per ritagliare i file audio e implementare questa funzionalità nel nostro codice.

Infine, creeremo alcune amichevoli interfacce utente così da poter riutilizzare questo script in un secondo momento senza dover modificare il codice.

Questo è tutto ciò che verrà eseguito nella funzione main() così possiamo separare funzionalità, implementazione ed uso.

Come Installare il Pacchetto FFMPEG

Questo pacchetto è la parte essenziale di molti programmi multimediali (e di tutti quelli open-source che ho usato finora). Ne avremo bisogno per entrambe le librerie di Python che presto installeremo.

Come Installare su Linux

Se sei su un sistema basato su Debian (come Ubuntu o Kali), ecco il comando per installare FFMPEG:

sudo apt-get install ffmpeg

Se stai usando altre tipologie di distribuzioni, le istruzioni per installare sono qui.

Come Installare su Windows

Per prima cosa, installa il Chocolatey Package Manager. Le istruzioni per installarlo sono qui, ti aspetto.

Una volta che avrai correttamente installato chocolatey (se non lo avevi già), scarica il pacchetto eseguendo questo comando come amministratore su Powershell:

choco install ffmpeg

Come Installare su Mac

Se già non lo hai, installa homebrew. Poi, in un terminale:

brew install ffmpeg

Come Scaricare Audio da URL di YouTube Programmaticamente

Prima di tutto, scarica il pacchetto youtube-dl da pip. È uno tra quelli con più stelle su GitHub.

pip install youtube-dl

Importeremo questo modulo per poi dichiarare una funzione che scarica l'audio in un formato mp3 di qualità accettabile da un URL di YouTube.

import youtube_dl # client per molti portali multimediali

# scarica yt_url nella stessa cartella da cui viene eseguito lo script
def download_audio(yt_url):
    ydl_opts = {
        'format': 'bestaudio/best',
        'postprocessors': [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': 'mp3',
            'preferredquality': '192',
        }],
    }
    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        ydl.download([yt_url])

def main():
    yt_url = "https://www.youtube.com/watch?v=8OAPLk20epo"
    download_audio(yt_url)

main()

Il metodo .download() scaricherà gradualmente lo stream audio come file .webm. Una volta che avrà determinato che l'intero file è disponibile, userà ffmpeg per convertirlo in un file MP3. Ciò significa che se succedesse qualcosa, ad esempio se ci fosse un calo di connessione quando solo il 90% del file è stato scaricato, il download riprenderà dal 90% invece che dall'inizio - molto conveniente.

Nota che se hai già il file mp3 scaricato, il download ricomincerà e sovrascriverà il file.

Esegui il tuo script di Python. Il download dell'audio di questa interpretazione del Quarto movimento della Nona sinfonia di Beethoven dovrebbe scaricare un file MP3 di ~33MB con il titolo del video disponibile localmente. Probabilmente sarà un po' lento, quindi vai pure a farti una tazza di tè.

Come puoi vedere, è possibile passare parametri opzionali a youtube-dl (che sarà disponibile anche come programma CLI autonomo al di fuori dello script). Una delle sue capacità è quella di scaricare una serie di video dall'URL di una playlist. Se sei interessato, puoi leggere la loro documentazione. In questo tutorial continuerò a farne un uso più superficiale.

Come Tagliare il File Scaricato

Una volta scaricato il file, andremo a tagliarlo arbitrariamente in locale (potresti esserti chiesto se fosse o meno possibile scaricare semplicemente una clip da YouTube. Tutti i metodi affidabili che ho trovato portano sempre a scaricare l'intero file e poi a modificarlo localmente). Per quello useremo la libreria pydub. Puoi installarla così:

pip install pydub

Questa è una libreria molto buona che ti permette di manipolare completamente l'audio, riducendo o aumentando il volume a determinati intervalli, ripetendo delle clip, e così via. Per ora, ci interessa solo tagliarlo.

Per tagliare il nostro file scaricato, dovremo prendere il nome del nostro nuovo file MP3, convertire il punto di inizio e il punto di fine dell'intervallo audio che desideriamo da 'ore:minuti:secondi' a millisecondi, e finalmente usare pydub per tagliare il nostro file audio.

Come prendere il nome del file

Sfortunatamente, il metodo .download() non restituisce il nome del file generato, a cui non avremmo comunque accesso avendo passato l'URL come parametro. Ma abbiamo Python che è uno strumento fantastico.

Sappiamo che stiamo cercando un file .mp3 che è stato generato giusto poco prima della nostra ricerca del nome del file (Python è un linguaggio a thread singolo e eseguirà il codice in modo sincrono di default). Prenderemo il nome del nostro file MP3 più recente nella cartella dello script, e quello sarà il nostro file.

Possiamo eseguire questa operazione elencando tutti i file .mp3 nella cartella locale, prendendo le loro marche temporali come numeri interi (dunque il tempo in millisecondi contato da una data del passato. Ciò significa che più alto sarà il valore, più lontano nel tempo il file sarà stato creato.) e scegliendo il file con il valore più alto.

Per questo avremo bisogno dei moduli glob per navigare nella cartella e os per prendere le informazioni delle marche temporali, entrambi già disponibili.

import glob
import os

def newest_mp3_filename():
    # elenca tutti gli mp3 nella cartella locale
    list_of_mp3s = glob.glob('./*.mp3')
    # restituisce l'mp3 con il valore di marca temporale più alto
    return max(list_of_mp3s, key = os.path.getctime)

Come ottenere HH:MM:SS in millisecondi

Una volta tagliato il nostro file, pydub si aspetta degli intervalli di tempo in millisecondi. Ma per noi esseri umani, calcolare il momento esatto in millisecondi ogni volta che tagliamo un video potrebbe risultare abbastanza fastidioso, quindi chiederemo gentilmente al computer di farlo per noi.

Il nostro input sarà una stringa nel formato HH:MM:SS. Questa cosa funziona bene se vogliamo tagliare un video più lungo di un'ora, ma la maggior parte delle volte vogliamo solo ottenere l'intervallo di tempo da un intervallo minuto:secondo ad un altro. Quindi dobbiamo tenere in considerazione anche questo.

Un millisecondo è un 1/1000 di un secondo, un minuto è formato da 60 secondi e un'ora è formata da 60 minuti. Dunque dobbiamo prima ottenere il valore per le ore, poi per i minuti, poi per i secondi, eseguire la conversione in millisecondi per ognuno e poi sommare le parti per ottenere il risultato.

def get_video_time_in_ms(video_timestamp):
    vt_split = video_timestamp.split(":")
    if (len(vt_split) == 3): # se in formato HH:MM:SS
        hours = int(vt_split[0]) * 60 * 60 * 1000
        minutes = int(vt_split[1]) * 60 * 1000
        seconds = int(vt_split[2]) * 1000
    else: # formato MM:SS
        hours = 0
        minutes = int(vt_split[0]) * 60 * 1000
        seconds = int(vt_split[1]) * 1000
    # tempo in millisecondi
    return hours + minutes + seconds

Come Ottenere l'Audio Tagliato

Adesso leggeremo il nostro MP3 come oggetto pydub e taglieremo l'intervallo che desideriamo. La sintassi è la stessa dell'operazione di taglio per stringhe e array, ma invece di un indice per ogni elemento useremo i millisecondi per istanti specifici all'interno dell'audio.

def get_trimmed(mp3_filename, initial, final = ""):
    if (not mp3_filename):
        # restituisci un errore per fermare immediatamente l'esecuzione del programma
        raise Exception("Nessun MP3 nella cartella locale.")
    # legge l'mp3 come oggetto PyDub
    sound = AudioSegment.from_mp3(mp3_filename)
    t0 = get_video_time_in_ms(initial)
    print("Inizio del processo di taglio del file ", mp3_filename, ".\n")
    print("Inizia da ", initial, "...")
    if (len(final) > 0):
        print("...fino a ", final, ".\n")
        t1 = get_video_time_in_ms(final)
        return sound[t0:t1] # da t0 fino a t1
    return sound[t0:] # t0 fino alla fine

Come Mettere Tutto Insieme

Alle Menschen werden Brüder,
Wo dein sanfter Flügel weilt.
-- Friedrich Schiller

Nel caso te lo stessi chiedendo, la traduzione del frammento qui sopra è "Tutti gli uomini diventano fratelli, dove la tua ala soave freme." È un frammento dell'Inno alla Gioia, un poema di Friedrich Schiller, che fa da testo alla maggior parte delle parti corali del Quarto movimento.

Questo è il frammento più famoso del movimento più famoso della sinfonia più famosa di Beethoven. Chiunque tu sia, dovunque e comunque tu sia stato cresciuto, quasi sicuramente riconoscerai questo brano.

Adesso metteremo insieme tutto ciò che abbiamo fatto. Scaricheremo l'audio da YouTube, taglieremo la parte corale dell'Inno alla Gioia (da 9:51 a 14:04), e lo salveremo come <filename> - TRIM.mp3.

Se hai seguito correttamente il tutorial, aggiorna la tua funzione main() per eseguire ogni fase in modo da avere sia l'MP3 intero che la versione tagliata nella cartella da cui eseguirai lo script. Non dimenticarti di eseguire la funzione main() alla fine dello script.

def main():
    yt_url = "https://www.youtube.com/watch?v=8OAPLk20epo"
    download_audio(yt_url)
    initial = "9:51"
    final = "14:04"
    filename = newest_mp3_filename()
    trimmed_file = get_trimmed(filename, initial, final)
    trimmed_filename = "".join([filename.split(".mp3")[0], "- TRIM.mp3"])
    print("Processo concluso con successo. Salvataggio del file tagliato con il nome ", trimmed_filename)
    # salva il file con il nuovo nome
    trimmed_file.export(trimmed_filename, format="mp3")

Come Aggiungere l'Interazione dell'Utente Direttamente da CLI

Per questa parte avremo bisogno del modulo sys di Python, che legge l'input passato dalla linea di comando (tra le altre cose). Aggiorneremo semplicemente le variabili della funzione main() per leggere l'input dalla CLI invece dei dati che sono scritti nel codice.

ARGV legge l'input in modo sequenziale come un array, iniziando dall'indice 1 (0 rappresenta il nome dello script di Python in esecuzione). Lo imposteremo in modo da fargli leggere un URL come primo argomento, poi (questo è opzionale) gli istanti iniziali e finali per tagliarlo.

import sys

def main():
    if (not len(sys.argv) > 1):
        print("Per favore, inserisci l'URL di una piattaforma multimediale supportata da youtube-dl come primo argomento.")
        return
    yt_url = sys.argv[1]
    download_audio(yt_url)
    if (not len(sys.argv > 2)): # esci se non vengono inseriti istanti come argomenti
        return
    initial = sys.argv[2]
    final = ""
    if (sys.argv[3]):
        final = sys.argv[3]
    filename = newest_mp3_filename()
    trimmed_file = get_trimmed(filename, initial, final)
    trimmed_filename = "".join([filename.split(".mp3")[0], "- TRIM.mp3"])
    print("Processo concluso con successo. Salvataggio del file tagliato con il nome ", trimmed_filename)
    # salva il file con il nuovo nome
    trimmed_file.export(trimmed_filename, format="mp3")

Esegui il file per collaudarlo. Ricordati di usare il nome dello script che si trova sul tuo dispositivo.

python ytauddown.py https://www.youtube.com/watch?v=8OAPLk20epo 9:51 14:04

Script Finale

Ecco la versione finale di tutto quanto messo insieme. Nota che i commenti sui moduli sono solo relativi a quello per cui dobbiamo usarli e che la funzione main() viene chiamata nell'ultima riga.

import youtube_dl # client per scaricare da molti portali multimediali
import glob # operazioni nelle cartelle
import os # interfaccia per prendere le informazioni sui file fornite dal sistema operativo
import sys # interfaccia per la linea di comando
from pydub import AudioSegment # solo operazioni audio

def newest_mp3_filename():
    # elenca tutti gli mp3 nella cartella locale
    list_of_mp3s = glob.glob('./*.mp3')
    # restituisce l'mp3 con il valore di marca temporale più alto
    return max(list_of_mp3s, key = os.path.getctime)

def get_video_time_in_ms(video_timestamp):
    vt_split = video_timestamp.split(":")
    if (len(vt_split) == 3): # se in formato HH:MM:SS
        hours = int(vt_split[0]) * 60 * 60 * 1000
        minutes = int(vt_split[1]) * 60 * 1000
        seconds = int(vt_split[2]) * 1000
    else: # formato MM:SS
        hours = 0
        minutes = int(vt_split[0]) * 60 * 1000
        seconds = int(vt_split[1]) * 1000
    # tempo in millisecondi
    return hours + minutes + seconds

def get_trimmed(mp3_filename, initial, final = ""):
    if (not mp3_filename):
        # restituisci un errore per fermare immediatamente l'esecuzione del programma
        raise Exception("Nessun MP3 nella cartella locale.")
    # legge l'mp3 come oggetto PyDub
    sound = AudioSegment.from_mp3(mp3_filename)
    t0 = get_video_time_in_ms(initial)
    print("Inizio del processo di taglio del file ", mp3_filename, ".\n")
    print("Inizia da ", initial, "...")
    if (len(final) > 0):
        print("...fino a ", final, ".\n")
        t1 = get_video_time_in_ms(final)
        return sound[t0:t1] # da t0 fino a t1
    return sound[t0:] # t0 fino alla fine



# scarica yt_url nella stessa cartella da cui viene eseguito lo script
def download_audio(yt_url):
    ydl_opts = {
        'format': 'bestaudio/best',
        'postprocessors': [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': 'mp3',
            'preferredquality': '192',
        }],
    }
    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        ydl.download([yt_url])

def main():
    if (not len(sys.argv) > 1):
        print("Per favore, inserisci l'URL di una piattaforma multimediale supportata da youtube-dl come primo argomento.")
        return
    yt_url = sys.argv[1]
    download_audio(yt_url)
    if (not len(sys.argv > 2)): # esci se non vengono inseriti istanti come argomenti
        return
    initial = sys.argv[2]
    final = ""
    if (sys.argv[3]):
        final = sys.argv[3]
    filename = newest_mp3_filename()
    trimmed_file = get_trimmed(filename, initial, final)
    trimmed_filename = "".join([filename.split(".mp3")[0], "- TRIM.mp3"])
    print("Processo concluso con successo. Salvataggio del file tagliato con il nome ", trimmed_filename)
    # salva il file con il nuovo nome
    trimmed_file.export(trimmed_filename, format="mp3")

# esempio d'uso:
# python ytauddown.py https://www.youtube.com/watch?v=8OAPLk20epo 9:51 14:04
main()

Esercizi Consigliati

  1. Individua se il primo input è un URL valido oppure no. Dai un'occhiata alle RegEx di Python se non sai da dove partire.
  2. Individua se il secondo e il terzo input sono in un formato valido (ore:minuti:secondi OPPURE minuti:secondi).
  3. Aggiungi un'opzione per rinominare il file MP3 direttamente dalla CLI. Ricorda che gli argomenti di ARGV vengono eseguiti in ordine.
  4. Riscrivi lo script per interagire con le sue funzionalità usando una GUI. Può essere sia un'app locale che un'app web, come preferisci.

Considerazioni Finali

Spero che ti divertirai con questo progetto e che ne farai buon uso.

Ricorda che guadagnarsi da vivere come artista è molto difficile, specialmente per la maggior parte di coloro che non hanno il supporto di un'azienda. Ricorda di supportare gli artisti che ti piacciono ogni volta che puoi e ricordati inoltre di supportare i software open-source.