
iPod and Python: sort albums in chronological order
Problem
Sometimes it seems to me that Apple is struggling to simplify its products. On the one hand, this is wonderful, but on the other, it is very regrettable. After all, the additional settings, although complicating our lives in the first couple of weeks of using the product (until you get used to it, you’ll figure it out), but later they allow you to customize it for yourself and get the gadget (or program) of your dreams.

Fortunately, this problem (as well as the problem of adding separately downloaded episodes to an existing iTunes podcast) has a solution. Not as simple as we would like, but very exciting.
We decide on the forehead
A completely simple, but not too elegant solution comes in the first in the first couple of seconds. Just for all albums we turn the contents of the tag % album% into % year% -% album% , and the problem is solved. And it’s sorted out as it should, and you can also see the year directly on iPod-e (without this kind of preparation, the release year of the album on the player itself cannot be figured out).
Perhaps this option will suit you, but it seemed to me unsatisfactory. A folder on a disk is one thing (I used to call them in the format% year% -% album%), and another thing is a smart album with albumart on iPod-e. On top of that, I'm not interested in the specific year in which the album was released. I just want to know which albums belong to the early work of this or that group, and which were released recently, to see how the group changed over time, how its style changed. This is precisely what prompted me to study the problem in more detail and search for its solution.
iTunes
There are no perfect programs. iTunes is no exception. Nevertheless, it is a convenient and pleasant to use application. At least that’s how I started thinking after several months of use, when I had finally come to terms with this Apple simplicity. And when it turned out that iTunes has something that will help solve the problem to a certain extent, I finally got rid of the thought of finding a replacement for iTunes.
He said: “Let's go!”
Entries behind and finally we begin to act. Select all the tracks of an album and open the context menu. There we find the wonderful Get Info item , which hides all the information about the selected tracks. At the moment, we are interested in the Sorting tab :

I did not specify the purpose of these fields in the documentation, but my whole life experience suggests that if the Sort Album field is full, then when sorting albums in iTunes (if not “Album by Year” is selected in the album column , but just “Album”) it will act as the key (similarly with other sort-fields). And, most importantly, the same key will be used when sorting albums directly on the player itself. This is what we need. Enter “2007 - Are You Listening” in the indicated field and rejoice how everything turned out smoothly for us. Now we have exactly one album, which is sorted (by itself) in chronological order =)
And ahead is eternity
The problem would seem to be already resolved. But in reality it is only just beginning. After releasing the player to the nth number of gigabytes, Apple should have simply not expected that so much music can be bought in the Apple Store (and other sources probably did not take into account). Or maybe they did not expect that one day someone would like to sort all the albums by date. The fact is that I simply am not able to repeat this procedure with the other five hundred albums in my library. As stated in a recent C # puzzle: "Never send a human to do a machine's job."
And then Python appears on the scene
We use it to process our library. All at once.
As it turned out, iTunes saves the value of the " Sort Album " field in the TSOA tag of the mp3 file (you can read about the rest of iTunes tags here ). Knowing this, we can appropriately fill this tag in our files, and then add them to the iTunes library.
There were not many Python libraries for working with id3 tags, but not a few. My choice fell on mutagen (other options can be found here ).
Mutagenallows working with metadata of a sufficiently large number of audio formats (ASF, FLAC, M4A, Monkey's Audio, MP3, Musepack, Ogg FLAC, Ogg Speex, Ogg Theora, etc.) We are interested in id3 tags in mp3 files. For manipulating them, the library provides two interfaces: ID3 and EasyID3 . I think the difference is not necessary. At first I played around with the first, but then it turned out that the possibilities of the second are quite enough.
Actually code
Immediately make a reservation that my experience in Python-e programming tends to infinity, but so far it is approximately zero. I tried to write everything in such a way that it would not be embarrassing to show to society, but I’m not sure that it turned out well enough. Therefore, I would be grateful for any informative comments expressed in a friendly form.
As for the algorithm, it is quite obvious: we go through the necessary files, get the year of release and the name of the album from the tags, and accordingly fill out the TSOA tag . I came to the conclusion that I would be most comfortable with the format % artist% -% year% -% album% . With this TSOA tag valuealbums will be sorted by artist, then by year, and then alphabetically (if suddenly one fine year your favorite band has spread out more than one album). Python, they say, has a very friendly syntax, so even if you are not familiar with it, making minor adjustments to your liking in the script below will not be a problem.
A lot can happen in the process of processing five thousand tracks, so I added a bit of auxiliary output to watch how things are progressing, and a log for outputting errors about the lack of tags in the file. After all, adding a file to iTunes without an artist or album in tags is a disastrous thing, so it will be useful to find out which files we are not yet ready to migrate to iPod.
I was lazy with docstring, but I diluted the code with comments. I hope this helps to orient those who are not very familiar with Python.
The code:
Copy Source | Copy HTML- #!usr/bin/env python
- # -*- coding: utf-8 -*-
- # Created by KL-7
-
- import logging
- import sys
- from os import path
-
- from mutagen.easyid3 import EasyID3
- from mutagen.id3 import ID3NoHeaderError
-
- # Количество успешно обработанных файлов
- totalfiles = 0
-
-
- # Вывод информации об ошибках
- def error(err):
- # Настраиваем лог-файл для ошибок
- logging.basicConfig(filename="error.log", level=logging.ERROR)
- logging.error(err)
-
-
- # Получаем значение тега tagname из файла audio
- def gettag(audio, tagname):
- taglist = audio.get(tagname, None)
- if taglist:
- return taglist[ 0]
- else:
- return ""
-
-
- # Заполняем тег TSOA
- def fill_tsoa(fpath):
- audio = EasyID3(fpath)
-
- artist = gettag(audio, "artist")
- if not artist:
- error("Missing artist in %s" % fpath)
-
- album = gettag(audio, "album")
- if not album:
- error("Missing album in %s" % fpath)
-
- year = gettag(audio, "date")
- if not year:
- error("Missing year in %s" % fpath)
-
- # Формируем значение для тега TSOA
- tsoa = "%s -- %s - %s" % (artist, year, album)
-
- # EasyID3 репамит названия тегов для большей наглядности.
- # Тег TSOA в этих обозначениях называется albumsort.
- audio["albumsort"] = tsoa
- audio.save()
-
-
- def fill_rec(root):
- # Эта функция будет вызваться в директории root и
- # в каждой ее поддиректории dirname.
- # fnames - содержимое текущей директории dirname.
- # Аргумент arg обязателен, но его мы не используем.
- def process(arg, dirname, fnames):
- for fname in fnames:
- # Конвертируем русские названия в Unicode
- fname = fname.decode("cp1251")
- name, ext = path.splitext(fname)
- fname = path.join(dirname, fname)
- if path.isfile(fname) and ext.lower() == ".mp3":
- try:
- fill_tsoa(fname)
- global totalfiles
- totalfiles = totalfiles + 1
- print fname
- except ID3NoHeaderError:
- error("No tags in %s" % fname)
-
- # Запускаем рекурсивный обход из директории root
- # с функциией process; None будет передан
- # функции process первым параметром.
- path.walk(root, process, None)
-
-
- if __name__ == "__main__":
- # Если есть параметры, интерпретируем их как директории,
- # которые надо обработать.
- if sys.argv[1:]:
- for dirname in sys.argv[1:]:
- fill_rec(dirname)
- # Если параметров нет, то работаем с текущей директорией.
- else:
- fill_rec(".")
-
- print "\nTotal files: %d" % totalfiles
- raw_input();
-
You can copy the script to a directory with music and run without parameters. In this case, all mp3 files in the current directory and in all subdirectories will be processed . And you can pass the script the list of necessary directories in the form of parameters on your own ( subdirectories will be processed in this case too).
When the script completes (it does not take too long), the data in the iTunes library will remain updated. If you have not added these files there yet, now is the time. If you edited the tags in files already present in iTunes, you just need to select them in the iTunes window, open Get Info through the context menu and, without editing anything , click Ok. iTunes will simply update all tags from the selected files.
AS IS
A few notes about the script:
- If you value your library, then perhaps you should first test the script on several files - you never know what can happen. I had a back-up of the entire collection, so I was not worried.
- It is quite possible that problems may arise when a too long line is added to the TSOA tag (I did not read the documentation on the internal structure of the tag block in mp3 files). The longest line in the TSOA tag (with my format% artist% -% year% -% album%), which I happened to meet in my library, contained 93 characters. The tag was successfully saved in the file and everything was fine with it.
- The script processed 5000 files with English names without any problems. I didn’t have Russian music in the library, but the test showed that the tags in the files with the Cyrillic alphabet in the title were successfully added. In order for the names of Russian-language files to be displayed in a readable form in the console (cmd), it was necessary to add decoding of the name (line 58). It was under Windows, as under other axes - I do not know. Therefore, if you are not dealing with Russian file names, it is probably better to comment out this line.
- German and other file names with diacritical marks did not want to be processed personally by me. I have not yet figured out how to solve the problem. The problem is that the file is called, for example, “Verf ü hrer.mp3”, and the script is called “Verf u hrer.mp3”. Of course, there is no file with that name in the folder. Again, this is all Windows. Personally, I have diacritics in tags (there is Unicode and everything is fine), but in file names I avoid them. Therefore, everything went well.
Afterword
If there are still any comments or problems - write. If there are suggestions on how to solve encoding problems - write even faster. To be honest, on all systems and in all languages, all my conscious life constantly had problems with encodings. Maybe we are too different with them, or maybe I should just sit down and deal with all of them well. One day I’ll definitely do it.
That's all for now. I would be glad if someone finds this script useful or at least interesting.
PS
Can anyone tell me what prompted sc.me to add a space before zero in the 22nd line? It was not in the source. If in html to remove nbsp in this place, then 0. disappears along with a space. And this is not good.