Entrada corta.
El problema
Me interesa leer los metadatos de todos los archivos de audio en una carpeta. En este caso son audios de entrevistas que están en tres formatos diferente, .mp3, .m4a y .wav. Después de leerlos querría cargar esa información de manera sistemática en un data frame que tendrá una fila por cada archivo y una columna para el nombre del archivo, otra para la duración y una tercera para la fecha de creación.
Dentro de la paquetería de R
no encontré ningúna librería que haga el trabajo. Sin embargo hay un programa que funciona en Linux (también en otras plataformas, aunque no probé) y que se encarga de leer metadatos de archivos de medios. Se llama mediainfo y tiene la opción de generar su output en formato XML
, es decir, un formato que podría leer desde R.1
system
y stdout
R
tiene la función system
, que se encarga de conectar a la consola de R
con la consola de nuestro sistema operativo. Normalmente la usamos para pasar directamente desde un script instrucciones a la consola (crear o borrar un archivo). Sin embargo tiene otra característica interesante, también puede ejecutar un programa en BASH
y capturar la salida de ese programa como un objeto de R. Esa “salida” está en un formato conocido llamado Standar Output o stdout
. Una de las maravillas de UNIX.
La solución
Los nombres de archivo están censurados por cuestiones de privacidad.
library(rvest)
library(dplyr) # para mutate
library(lubridate) #Para manipular fechas.
# Lista con todos los archivos de audio
archivos <- list.files("~/audio_test", "*.mp3|*.m4a|*.wav",
full.names = TRUE)
# Collapso a lista a una cadena de caracteres.
# mediainfo acepta múltiples nombres de archivo separados por un espacio
archivos <- paste(archivos, collapse = " ")
# Leo los metadatos con una llamada externa a mediainfo, le especificio que la salida sea en XML
metadatos_crudos <- system(paste("mediainfo --output=XML", archivos),
intern = TRUE) #R captura el stdout como un vector de cadenas de caracteres.
metadatos <- paste0(metadatos_crudos, collapse = "") %>% #Colapso el vector de caracteres a una sola cadena.
read_html() #Legible por read_html
# Navego las etiquetas XML y capturo la información que me interesa.
# read_html::xml_structure() imprime la lista de etiquetas.
audios <- data.frame(
archivo = html_nodes(metadatos, "media") %>%
html_attr("ref"),
formato = html_nodes(metadatos, xpath = '//*[@type="General"]') %>%
html_nodes("format") %>%
html_text(),
duracion = html_nodes(metadatos, xpath = '//*[@type="General"]') %>%
html_nodes("duration") %>%
html_text() %>%
as.numeric(), #En segundos
fecha = html_nodes(metadatos, "file_modified_date") %>%
html_text() %>%
ymd_hms() %>%
date(), #Sale la fecha limpia
stringsAsFactors = FALSE
)
audios <- audios %>%
mutate(archivo = "Información privada")
knitr::kable(audios, caption = "Audios de entrevistas")
archivo | formato | duracion | fecha |
---|---|---|---|
Información privada | MPEG-4 | 4108.794 | 2017-11-23 |
Información privada | MPEG-4 | 5986.754 | 2017-11-23 |
Información privada | MPEG-4 | 3100.582 | 2017-11-23 |
Información privada | MPEG-4 | 4710.214 | 2017-10-03 |
Información privada | MPEG Audio | 2270.798 | 2017-10-26 |
Información privada | MPEG-4 | 2776.704 | 2017-10-09 |
Información privada | MPEG-4 | 5813.300 | 2017-10-03 |
Información privada | MPEG-4 | 5989.586 | 2017-09-05 |
Información privada | MPEG-4 | 5442.918 | 2017-09-05 |
Información privada | MPEG-4 | 4904.000 | 2017-10-09 |
Información privada | MPEG-4 | 1427.795 | 2017-11-13 |
Información privada | MPEG-4 | 373.028 | 2017-11-26 |
Información privada | MPEG-4 | 5595.288 | 2017-11-26 |
Información privada | MPEG-4 | 2453.824 | 2017-10-15 |
Información privada | MPEG-4 | 5047.168 | 2017-10-15 |
Información privada | MPEG-4 | 1730.176 | 2017-10-15 |
Información privada | MPEG-4 | 2045.696 | 2017-10-15 |
Información privada | MPEG-4 | 3163.328 | 2017-10-15 |
Información privada | MPEG-4 | 5408.064 | 2017-10-15 |
Información privada | MPEG Audio | 3245.453 | 2017-10-26 |
Información privada | MPEG Audio | 3895.797 | 2017-10-26 |
Información privada | MPEG Audio | 1404.055 | 2017-10-26 |
Información privada | MPEG Audio | 373.577 | 2017-10-26 |
Información privada | MPEG Audio | 2493.466 | 2017-10-26 |
Información privada | MPEG Audio | 1118.406 | 2017-10-26 |
Información privada | MPEG-4 | 2414.595 | 2017-10-03 |
Información privada | MPEG-4 | 2101.428 | 2017-10-03 |
Información privada | MPEG-4 | 1472.143 | 2017-10-03 |
Información privada | MPEG-4 | 1345.712 | 2017-10-03 |
Información privada | MPEG-4 | 2334.208 | 2017-10-03 |
Información privada | MPEG-4 | 2524.705 | 2017-10-03 |
Información privada | MPEG-4 | 3032.317 | 2017-10-03 |
Información privada | MPEG-4 | 3850.912 | 2017-10-03 |
Información privada | MPEG-4 | 2638.855 | 2017-10-03 |
Información privada | Wave | 6712.293 | 2017-10-26 |
Información privada | MPEG Audio | 3350.752 | 2017-10-31 |
Información privada | MPEG Audio | 5097.639 | 2017-10-26 |
Información privada | MPEG-4 | 391.808 | 2017-10-10 |
Información privada | MPEG-4 | 1210.240 | 2017-10-10 |
Información privada | MPEG-4 | 1392.704 | 2017-10-10 |
Información privada | MPEG-4 | 122.304 | 2017-10-10 |
No sin alguna dificultad. Para algunas operaciones hay utilizar la sintaxis
xpath
que es poco intuitiva.↩