Я хочу умный алгоритм для индексации каталога файлов... указателей?

У меня есть каталог музыки в файлах Ubuntu (.mp3,.wav и т.д.). Этот каталог может иметь как можно больше подкаталогов, без ограничений. Я хочу, чтобы у меня была возможность извлечь из него музыкальную библиотеку, т.е. Вернуть список песен на основе фильтров:

1) членство в плейлисте 2) имя исполнителя 3) поиск строк 4) название песни etc, etc

Однако, если имена файлов изменяются, перемещаются или даже добавляются в мой каталог Music, я должен уметь это отражать в моем движке организации музыки - быстро!

Я изначально думал просто контролировать мой каталог с pyinotify, incron или inotify. К сожалению, мой каталог - это общий ресурс Samba, поэтому мониторинг файлов событий завершился неудачно. Таким образом, моя следующая догадка заключалась в том, чтобы просто рекурсивно искать каталог в python и заполнять базу данных SQL. Затем, при обновлении, я просто посмотрел бы, изменилось ли что-либо (сканирование каждой вложенной папки, чтобы узнать, есть ли в каждой базе данных имя уже в базе данных, а если не добавить ее) и соответственно сделать ОБНОВЛЕНИЕ. К сожалению, это кажется ужасной реализацией O(n^2) - ужасно для многотервальной музыкальной коллекции.

Немного лучше может потребоваться создание древовидной структуры в SQL, таким образом сужая возможных кандидатов для поиска соответствия на любом заданном шаге подпапки к размеру этой подпапки. Все еще кажется неэлегантным.

Какие парадигмы дизайна/пакеты можно использовать, чтобы помочь себе? Очевидно, будет включать множество умных хеш-таблиц. Я просто ищу некоторые указатели в правильном направлении, чтобы подойти к проблеме. (Также я являюсь полным наркоманом для оптимизации.)

Ответ 1

Жесткой частью этого является сканирование каталога, просто потому, что оно может быть дорогостоящим.

Но это жестокая реальность, поскольку вы не можете использовать inotify и др.

В вашей базе данных просто создайте запись типа node:

create table node (
    nodeKey integer not null primary key,
    parentNode integer references node(nodeKey), // allow null for the root, or have root point to itself, whatever
    fullPathName varchar(2048),
    nodeName varchar(2048),
    nodeType varchar(1) // d = directory, f = file, or whatever else you want
)

Это ваша структура node.

Вы можете использовать полный столбец пути, чтобы быстро найти что-либо по абсолютному пути.

Когда файл перемещается, просто пересчитайте путь.

Наконец, сканируйте музыкальные файлы. В unix вы можете сделать что-то вроде:

найти. -тип f | sort > sortedListOfFiles

Далее, просто сосать все имена путей из базы данных.

выберите fullPathName из node, где nodeType!= 'd' order by fullPathName

Теперь у вас есть два отсортированных списка файлов.

Запустите их через DIFF (или comm), и у вас будет список удаленных и новых файлов. У вас не будет списка "перемещенных" файлов. Если вы хотите сделать эвристику, где вы сравниваете новые и старые файлы, и у них есть те же окончания (т.е...../альбом/песня), чтобы попытаться обнаружить "ходы" против нового и старого, то в порядке, без особого труда. Стоит сделать снимок.

Но diff даст вам ваш дифференциал в мгновение ока.

Если у вас есть миллионы файлов, то, извините, это займет некоторое время, но вы уже знаете, что когда вы потеряете возможность inotify. Если бы у вас было это, это было бы просто постепенное обслуживание.

Когда файл перемещается, он становится тривиальным, чтобы найти свой новый абсолютный путь, потому что вы можете попросить родителя указать его путь и просто добавить к нему свое имя. После этого вы не сканируете дерево или что-нибудь еще, если хотите. Работает в обоих направлениях.

Addenda:

Если вы хотите отслеживать фактические изменения имени, вы можете получить немного больше информации.

Вы можете сделать это:

find . -type f -print0 | xargs -0 ls -i | sort -n > sortedListOfFileWithInode

Параметры -print0 и -0 используются для работы с файлами с пробелами в них. Однако цитаты в именах файлов разрушат это. Возможно, вам лучше запустить исходный список через python и fstat, чтобы получить inode. Различные вещи, которые вы можете сделать здесь.

Что это значит, а не просто с именами, вы также получаете inode файла. Индекс - это "реальный" файл, каталог ссылается на имена inode. Таким образом, вы можете иметь несколько имен (жестких ссылок) в файловой системе unix для одного файла, все имена указывают на один и тот же индекс.

Когда файл переименовывается, inode останется прежним. В unix существует одна команда, используемая для переименования и перемещения файлов, mv. Когда mv переименовывает или перемещает файл, inode остается таким же, как LONG, так как файл находится в той же самой файловой системе.

Таким образом, использование inode, а также имя файла позволит вам захватить еще одну интересную информацию, например перемещение файлов.

Это не поможет, если они удаляют файл и добавляют новый файл. Но вы, вероятно, сможете сказать, что это произошло, поскольку маловероятно, что старый индекс будет использован повторно для нового inode.

Итак, если у вас есть список файлов (отсортированных по имени файла):

1234 song1.mp3
1235 song2.mp3
1236 song3.mp3

и кто-то удаляет и добавляет назад песню 2, у вас будет что-то вроде

1234 song1.mp3
1237 song2.mp3
1236 song3.mp3

Но если вы это сделаете:

mv song1.mp3 song4.mp3

Вы получите:

1237 song2.mp3
1236 song3.mp3
1234 song4.mp3

Другой оговоркой является то, что если вы потеряете диск и восстановите его из резервной копии, вероятно, все иноды изменятся, что приведет к эффективному восстановлению вашего индекса.

Если вы настоящие авантюристы, вы можете попробовать играть с расширенными атрибутами файловой системы и назначать другие интересные метаданные в файлы. Не так много сделали с этим, но у него также появились возможности, и есть, вероятно, невидимые опасности, но...

Ответ 2

my aggregate_digup программа считывает расширенный файл формата sha1sum.txt, созданный программой digup. это позволяет мне найти файл на основе его sha1sum. программа digup хранит в своем выходе хэш и mtime-размер mtime. по умолчанию он пропускает хэширование файла, если совпадают mtime и size. индекс, созданный моим aggregate_digup, используется моей модифицированной версией открытого графического контекстного меню uri, позволяющего выбрать один из вариантов на sha1:b7d67986e54f852de25e2d803472f31fb53184d5, и в нем будут перечислены копии файла, который он знает, чтобы вы могли выбрать его и открыть его.

как это связано с проблемой, состоит в том, что есть две части: одна из списков воспроизведения и два файла.

если мы можем предположить, что ничего, что игрок делает, не изменяет файлы, то хэш и размеры файлов являются постоянными. поэтому мы должны иметь возможность использовать размер и хэш файла как уникальный идентификатор.

например, ключ для упомянутого файла: 222415:b7d67986e54f852de25e2d803472f31fb53184d5

Я обнаружил, что на практике это не имеет коллизий в любой естественной коллекции.

(это означает, что метаданные ID3, которые добавляются или добавляются в mp3-данные, не могут измениться, если вы не решите пропустить эти метаданные во время хэширования)

поэтому база данных плейлистов будет такой:

files(file_key, hash, size, mtime, path, flag)
tracks(file_key, title, artist)
playlists(playlistid, index, file_key)

чтобы обновить таблицу файлов:

import os
import stat
# add new files:
update files set flag=0
for path in filesystem:
    s=os.stat(path)
    if stat.S_ISREG(s.st_mode):
        fetch first row of select mtime, hash, size from files where path=path
        if row is not None:
            if s.st_mtime == mtime and s.st_size == size:
                update files set flag=1 where path=path
                continue
        hash=hash_file(path)
        file_key="%s:%s" % (int(s.st_mtime), hash)
        insert or update files set file_key=file_key, size=s.st_size, mtime=s.st_mtime, hash=hash, flag=1 where path=path
# remove non-existent files:
delete from files where flag=0

Ответ 3

Реальность такова, что это проблема hard. Вы также исходите из недостатка: Python и mySQL не являются самыми быстрыми инструментами для этой цели.

Даже iTunes жалуется из-за времени, необходимого для импорта библиотек и индексирования новых файлов. Можете ли вы представить себе часы человека, которые превратили iTunes так хорошо, как есть?

Лучше всего посмотреть на код основных музыкальных проигрывателей с открытым исходным кодом, таких как

И попробуйте адаптировать свои алгоритмы к своей цели и к идиомам Python.

Ответ 4

import os
import re

ваш другой код здесь, который изначально устанавливает диктатор, содержащий файлы, которые у вас уже есть в вашей библиотеке (я назвал словарь archived_music)

music_directory = '/home/username/music'
music_type = '\.mp3$|\.wav$|\.etc$'
found_files = os.popen('find %s -type f -mtime 1 2>/dev/null' % music_directory)
for file in found_files:
    directory, filename = os.path.split()
    if re.compile(music_type).search(filename):
        #found a music file, check if you already have it in the library
        if filename in archived_music:
            continue
        #if you have gotten to this point, the music was not found in the arcchived music directory, so now perform whatever processing you would like to do on the full path found in file.

Вы можете использовать этот код как небольшую функцию или что-то еще и называть его любым временным разрешением. Он будет использовать команду find и найти каждый вновь созданный файл в течение последнего дня. Затем он проверяет, имеет ли он тип music_type, если он будет проверять имя файла против любой текущей базы данных, которую вы настроили, и вы можете продолжить обработку оттуда. Это должно помочь вам начать обновление новой добавленной музыки или чего-то еще.

Ответ 5

В прошлом я делал что-то подобное, но в итоге использовал Amarok w/MySQL. Amarok создаст для вас базу данных mysql и очень хорошо проиндексирует все ваши файлы - после этого взаимодействие с базой данных должно быть относительно простым из python.

Это было для меня время от времени:)

НТН