2011-07-23 22 views
10

我在Ubuntu(.mp3,.wav等)文件上有一個音樂目錄。該目錄可以具有任意數量的子目錄,無需限制。我希望能夠做一個音樂庫出來的 - 也就是說,基於過濾器的歌曲回報列表:我想要一個聰明的算法索引文件目錄...指針?

1)會員到播放列表 2)藝術家姓名 3)字符串搜索 4)的名字歌曲 等

但是,如果文件名被更改,移動甚至添加到我的音樂目錄,我需要能夠反映這是在我的音樂組織引擎 - 快速!

我原本以爲只是用pyinotify,incron或inotify監視我的目錄。不幸的是,我的目錄是一個Samba共享,因此監控文件事件failed。所以我的下一個猜測是簡單地遞歸搜索python中的目錄,並填充SQL數據庫。然後在更新時,我只會查看是否有任何更改(掃描每個子文件夾以查看每首歌曲的名稱是否已存在於數據庫中,並且是否已添加),然後相應地進行更新。不幸的是,這似乎是一個可怕的O(n^2)實現 - 對於一個多TB音樂收藏可怕。

稍微好一點可能涉及在SQL中創建樹結構,從而縮小可能的候選項,以便在任何給定的子文件夾步驟中搜索匹配項,以達到該子文件夾的大小。仍然看起來不雅。

我可以用什麼樣的設計範例來幫助自己?顯然會涉及很多聰明的哈希表。我只是在尋找正確的方向來解決問題。 (另外我是一個完整的優化玩家。)

+0

爲什麼會被O您最初的想法(N^2)?假設數據庫在歌曲名稱上有一個O(log n)索引(應該很容易安排),它應該是O(n log n)。 – andrewdski

+0

如果你很擔心複雜性,你可以使用'O(n)'作爲'n'查找/更新的散列。 –

+0

我第二個哈希的想法;唯一的問題是,你如何決定一個獨特的密鑰來哈希?我不會去任何正常的文件屬性,因爲這些可以改變。也許根據文件中的樣本製作一個關鍵字? –

回答

3

這很難完成的部分是掃描目錄,只是因爲它可能很昂貴。

但這是一個殘酷的現實,因爲你不能使用inotify等人。

在你的數據庫,只需創建一個節點類型的記錄:

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 
) 

這就是你的節點結構。

您可以使用完整路徑列以絕對路徑快速查找任何內容。

當文件移動時,只需重新計算路徑即可。

最後,掃描你的音樂文件。在unix中,你可以這樣做:

找到。 -type f | sort> sortedListOfFiles

接下來,只需將所有路徑名稱從數據庫中抽取出來即可。從節點

選擇fullPathName其中節點類型!通過fullPathName

=「d」訂單現在你有兩份文件的排序列表。

通過DIFF(或comm)運行它們,您將獲得已刪除文件和新文件的列表。您將不會有「移動」文件的列表。如果你想對新舊文件進行比較,並且它們具有相同的結尾(即.... /專輯/歌曲)來嘗試檢測「移動」與新舊版本的比較,那麼很好,沒什麼大不了的。值得一試。

但差異會給你你的心跳差異。

如果你有很多文件,那麼,很抱歉,這需要一些時間 - 但你已經知道當你失去inotify功能時。如果你有這樣做,那只是增量維護。

當一個文件移動時,找到它的新絕對路徑是微不足道的,因爲你可以向它的父路徑詢問它的路徑,並簡單地將你的名字附加到它。之後,除非你想要,否則你不會爬樹或任何東西。雙向工作。

附錄:

如果你想跟蹤實際名稱的變化,就可以得到多一點的信息。

你可以這樣做:

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

的-print0和-0使用帶空格的文件工作。然而文件名中的引號會破壞這個。通過python和fstat運行原始列表來獲得inode可能會更好。你可以在這裏做不同的事情。

這是做什麼而不是隻有名稱,你也可以得到文件的inode。 inode是「真實」文件,目錄將名稱鏈接到inode。這是如何在unix文件系統中將多個名稱(硬鏈接)分配給單個文件,所有名稱都指向同一個inode。

當文件重命名時,inode將保持不變。在unix中,有一個命令用於重命名和移動文件mv。當mv重命名或移動文件時,inode與文件在同一文件系統上保持一樣。

因此,使用inode以及文件名可以讓您捕獲一些更有趣的信息,如文件移動。

如果他們刪除文件並添加一個新文件,這將毫無幫助。但是你可能會(可能)能夠知道它發生了,因爲舊的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 

另一個需要注意的是,如果您失去了驅動器並從備份中恢復,很可能所有的inode都會發生變化,從而強制重建索引。

如果您真的冒險,可以嘗試使用擴展文件系統屬性進行播放,並將其他感興趣的元數據分配給文件。雖然沒有做太多的事情,但它也有可能性,並且有可能看不到的危險,但是...

+0

謝謝! unix'find'命令閃電般 - 比python好得多。 DIFF是什麼意思?我搜索並找不到它在MySQL文檔 – lollercoaster

+0

oh nvm。你的意思是unix diff命令(我認爲)。這個問題沒有解決的唯一問題是,如果文件被重命名,數據庫中的播放列表表將無法跟蹤哪些路徑屬於哪個播放列表 - 我將無法跟蹤它們。除非我錯了。 – lollercoaster

2

現實是,這是一個的問題。你也從一個劣勢開始:Python和mySQL並不是用於此目的的最快工具。

即使iTunes被抱怨,因爲導入庫和索引新文件需要花費時間。你能想象一下,讓iTunes變得如此的好嗎?

最好的辦法是看的主要的開源音樂播放器如

然後嘗試將它們的算法適應您的目的和Python成語。

+0

以及我認爲python不是,但我認爲MySQL只是在本地機器上獲得大型數據庫的最快速度... – lollercoaster

3

我的aggregate_digup程序讀取由digup程序生成的擴展sha1sum.txt格式文件。這讓我可以根據它的sha1sum找到一個文件。 digup程序將mtime大小的散列和路徑名存儲在其輸出中。默認情況下,如果mtime和size匹配,它將跳過文件散列。由我的aggregate_digup生成的索引被我的修改版本的open uri上下文菜單gedit插件使用,允許用戶點擊sha1:b7d67986e54f852de25e2d803472f31fb53184d5選項,它將列出它所知道的文件的副本,以便您可以選擇並打開它。

這與問題的關係如何,有兩個部分:一個播放列表和兩個文件。

如果我們可以假設播放器沒有改變文件,那麼文件的散列和大小是恆定的。所以我們應該能夠使用文件的大小和散列作爲唯一標識符。

例如對於文件的密鑰提到:222415:b7d67986e54f852de25e2d803472f31fb53184d5

我發現,在實踐中,這在任何自然收集沒有衝突。

(這是否意味着其附加或前置到MP3數據的ID3元數據不能更改,除非您選擇跳過該元數據而散列)

所以播放列表數據庫也會是這樣的:

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 
+0

你提到的真棒想法。任何想法如何我只能返回音樂(音頻)數據的散列,而不是Python中的ID3標籤?我保證正常播放,重命名和移動文件不會更改非ID3數據嗎? – lollercoaster

+0

您必須使用解析器找到ID3元數據的開始和結束,並在哈希時忽略它。 ID3數據的位置取決於版本,iir,一個版本出現在文件的開頭,其他版本出現在最後。除非你的球員寫得不好,否則不應該改變文件。重命名和移動文件不影響文件的內容,隻影響文件系統元數據,除非文件系統寫得不好。 tbh,我使用命令行mplayer作爲我的主要播放器,我甚至找到了播放音樂播放列表的相當簡單的pymp。 –

+0

跳過ID3元數據的想法,我從mp3dup程序中獲得,它只能在文件的開頭和結尾處跳過字節。 –

0
import os 
import re 

您這裏的其他代碼最初設置了包含dictonary哪些文件你已經在你的庫(我叫它字典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類型的,如果是,它將檢查您設置的當前數據庫的文件名,並且可以繼續從那裏進行處理。這應該能夠讓你開始更新新添加的音樂或什麼。

0

我在過去做過類似的事情,但最終使用了Amarok w/MySQL。 Amarok會爲你創建一個mysql數據庫,並且很好地爲你的所有文件建立索引 - 之後,與數據庫的接口應該是相對簡單的。

這真是對我來說是節省時間:)

HTH