Índice
A muchos de nosotros nos gusta escuchar música mientras programamos. Hoy se me ocurrió que estaría bien generar listas de reproducción de una duración determinada. La razón tiene sus orígenes en la técnica Pomodoro, que consiste concentrarse en realizar una tarea durante 25 min, sin distraerse, y descansar 5 min. Se repite cuatro veces y al cuarto descanso en lugar de 5 se descansan 15 min. Ésta técnica pretende mejorar la productividad.
Para ello he creado un pequeño script en python que genera automáticamente las listas de reproducción dado un directorio con ficheros mp3 o mp4 y una duración en minutos.
CHANGELOG
- 24/10/2017: Gracias a leotok ahora el programa soporta escanear carpetas de forma recursiva.
Requisitos
Para leer la duración de cada fichero he usado un módulo llamado Mutagen. Para instalarlo:
$ pip install mutagen
Una vez instalado, podremos ejecutar el programa, el código es el siguiente:
#!/usr/bin/env python
'''
Created on Apr 4, 2014
@author: Alejandro Alcalde
Licensed under GPLv3
More info: http://wp.me/p2kdv9-Bq
'''
import argparse
import fnmatch
from mutagen.mp3 import MP3
from mutagen.mp4 import MP4
from mutagen.flac import FLAC
from collections import deque
from os.path import os, basename
from sys import argv
from random import shuffle
curr_length = 0
playlist_number = 1
def main():
parser = argparse.ArgumentParser(description='Generate playlists with the indicated length')
parser.add_argument('-d','--directory', help='Directory with music files',type=str, required=True)
parser.add_argument('-l', '--length', help='Length of the playlist, in minutes', type=int, required=True)
args = parser.parse_args()
directory = args.directory
length = args.length * 60
path = r'./playlists/'
if not os.path.exists(path): os.makedirs(path)
playlist_basename = 'playlist_' #basename(argv[0][:-3]) + '_'
curr_items = []
too_long_items = []
all_items = []
dir_queue = deque([directory])
while len(dir_queue) != 0:
cur_directory = dir_queue.popleft()
for node in os.listdir(cur_directory):
node = os.path.join(cur_directory, node)
if os.path.isdir(node):
dir_queue.append(node)
elif fnmatch.fnmatch(node, '*.mp[43]') or fnmatch.fnmatch(node, '*.flac'):
all_items.append(node)
shuffle(all_items)
for item in all_items:
global curr_length
if curr_length >= length:
create_playlist(path, playlist_basename, curr_items)
else:
encoding = item[-4:]
encodings = {'.mp3': MP3, '.mp4': MP4, 'flac': FLAC}
try:
music_file = encodings[encoding](item)
except Exception as e:
handleException(e)
else:
file_length = music_file.info.length
if file_length > length:
too_long_items.append(item)
print("File %s exceed the given length (%s min)" % (item, file_length/60))
else:
curr_length += file_length
curr_items.append(item+'\n')
if curr_items:
create_playlist(path, playlist_basename, curr_items)
if too_long_items:
print("\nThis files exceeded the given length and were not added to any playlist...\n")
for i in too_long_items:
print(basename(i))
def create_playlist(path, playlist_basename, curr_items):
global playlist_number, curr_length
name = path + str(playlist_number) + '. ' + playlist_basename + str(int(curr_length/60)) + 'min' + '.m3u'
playlist_file = open(name, 'w')
playlist_file.writelines(curr_items)
playlist_file.close()
print('Playlist generated, name: ', name , ' length ', curr_length/60 , 'min')
playlist_number += 1
curr_length = 0
del curr_items[:]
def handleException(e):
print(type(e)) # the exception instance
print(e.args) # arguments stored in .args
print(e) # __str__ allows args to printed directly
if __name__ == '__main__':
main()
Generar listas de reproducción
El uso del programa es sencillo:
$ ./genPlayListByLength.py -h
usage: genPlayListByLength.py [-h] -d DIRECTORY -l LENGTH
Generate playlists with the indicated length
optional arguments:
-h, --help show this help message and exit
-d DIRECTORY, --directory DIRECTORY
Directory with music files
-l LENGTH, --length LENGTH
Length of the playlist, in minutes
[~/Desarrollo/WorkSpaces/Py_WorkSpace/GenPlayListByLenght/src]
$ ./genPlayListByLength.py -d /media/RAID_External/Music/EPIC/Nueva/ -l 20
Playlist generated, name: ./playlists/genPlayListByLength.py20_1.m3u length 24.0001451247 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_2.m3u length 22.7172426304 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_3.m3u length 24.2311836735 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_4.m3u length 20.2354164777 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_5.m3u length 21.8601359014 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_6.m3u length 28.3070597128 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_7.m3u length 22.7946424792 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_8.m3u length 21.2017535903 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_9.m3u length 21.7807044596 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_10.m3u length 21.1862736206 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_11.m3u length 23.5667059713 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_12.m3u length 21.3402993197 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_13.m3u length 25.1046409675 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_15.m3u length 20.4145971277 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_16.m3u length 21.0794618292 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_17.m3u length 20.2702464097 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_18.m3u length 21.2315525321 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_19.m3u length 20.798403305 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_20.m3u length 22.1189417989 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_21.m3u length 21.7013696145 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_22.m3u length 22.971888133 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_23.m3u length 21.8250628874 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_25.m3u length 21.3321723356 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_26.m3u length 21.5465699169 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_27.m3u length 24.9339743008 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_28.m3u length 32.0853333333 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_29.m3u length 25.4854482237 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_30.m3u length 20.7996613757 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_31.m3u length 21.3372033258 min
Playlist generated, name: ./playlists/genPlayListByLength.py20_32.m3u length 22.5926288738 min
Qué queda por mejorar
Ya que el script lo he hecho en media hora, se puede mejorar bastante. Ahora mismo no busca los ficheros de música recursivamente, en el futuro lo añadiré. Tiene un fallo, cuando se está generando la lista, si la duración total es menor que la indicada por parámetro, y el fichero de música procesándose en ese momento es muy grande (por ejemplo 40 min), la lista de reproducción no será del tamaño correcto. (Solucionado)
Para aquellos que estén interesados, el código está en GitHub
¿Has visto algún error?: Por favor, ayúdame a corregirlo contactando conmigo o comentando abajo.