
我一直在music积音乐。 我有音乐文件夹的多个备份,并且许多文件夹来自不同的来源。
最近,我决定看看是否可以拿出一份完整清单,列出存储在硬盘驱动器中的所有音乐,而我发现这项任务可能非常艰巨。 我为什么要这样做? 好吧,一方面,当然是因为。 这篇文章的结尾还有另一个原因,但是它与编程完全无关,因此我将其留待以后使用。
- 您应该了解的有关最佳液压缸的所有信息
- Puddles Pity Party脱身美国才华的真正原因
- I Gede Ari Astina,Musisi atau Aktivis
- Sinchan Das博士,健康科学和印度古典音乐的磁性天才
- 虚拟吉他VST:Syntheway的GuitarTempus(尼龙,六十二钢弦,半声学,电吉他)
当然,最大的问题是我们几乎从未以任何常规方式命名文件和文件夹。 不同的人有不同的约定,而且正如我发现的那样,我有时会有非常不同的约定。 即使我基本上只想要一个仅包含专辑,艺术家和年份的简单列表,但要检索这三个项目仍然是一个挑战。 除非您的图书馆组织得井井有条,否则您一定会像我一样最终,必须在流程结束时手动整理列表。 但这比必须手动键入整个内容好几个数量级!
这是我在此过程中学到的。 希望这里会有一些有用的提示。
整理数据
当然,第一件事是使自己井井有条,以便我可以按自己的方式获得所需的数据。 因为我想要我之前提到的3个字段(艺术家,专辑,年份),所以我制作了一个相当简单的Python类来保存它们:
音乐课 :
def __init __ (个人,日期,艺术家,专辑):
self.date =日期
self.artist =艺术家
self.album =专辑
def __eq __ (自己,其他):
返回 ((self.date == other.date)和(self.artist == other.artist)和(self.album == other.album))
def __hash __ (自己):
返回哈希值((self.date,self.artist,self.album))
除了数据之外,所有类都具有__eq__和__hash__的定义,这是我使用集合所需要的。 集合是我存储所有音乐对象的数据结构,并且我使用了集合来尽可能地避免重复。
我使用的其他东西是:
- 音乐所在路径的字符串列表(如果所有音乐都放在一个位置,则不需要)
- 用来存储排除文件夹的字符串列表(我在某些子文件夹中有视频和书籍-是的,我有点混乱)
- 文件夹名称中“不需要的”字符串的列表(诸如“ mp3”,“ flac”,“ 320kbps”之类的名称)
- 如上所述,用于存储所有音乐对象的集合
我还有其他用途,但是它们与获取数据的每种不同方式有关,因此我们将继续进行处理。
一个简单的例子:您实际上正确命名了文件夹
所以是的,实际上是这种情况:在很多情况下,我的相册文件夹已经包含了我所需的所有信息,因此这是我检查的第一件事。 因此,在这种情况下,您只需从文件夹中检索该信息即可。
我选择Python的原因之一是遍历目录结构并从文件中获取信息非常容易。 输入os.walk()。
os.walk允许您通过从上到下(或从下到上,如果需要的话)递归遍历目录,轻松地迭代目录。 该功能存储了每个步骤所需的所有信息,使您的生活变得非常轻松。
对于这一部分,我唯一需要的额外项目是使用正则表达式在文件夹名称中查找年份:
年= r'((?:19 | 20)\ d \ d)'
年=重新编译(年)
之后,我准备开始遍历目录。 从现在开始的所有内容都将发生在os.walk()循环内。 在这里将其分开以便于阅读。 我们首先检查的是文件夹中是否包含年份:
#musicPaths:我要遍历的所有路径的列表
用于 musicPaths 中的 currPath:
用于 os.walk(currPath)中的根目录,dirs文件:
#search子目录
对于范围内的我(len(dirs)-1,-1,-1):
如果 在排除中有 dirs [i]: #excluded是不需要的目录的列表
德尔迪斯[i]
继续
匹配= year.search(dirs [i])
如果 match 不为None :
artistL = root.split(“ /”)
artist = artistL [len(artistL)-1]
musicSet.add(Music(match.group(),artist,dirs [i]))
德尔迪斯[i]
Python的文档相当清晰,但是os.walk()的3个返回基本上是:root,根目录; dirs,一个列表,其中包含当前级别的所有目录; 文件,一个列表,其中所有文件不在当前级别的目录内。
向后遍历目录的原因是因为我实际上是删除其中已有一年的目录(无需进一步探索)或我们不感兴趣的目录。
不要犯我愚蠢的错误,我最初会忘记,如果要从索引中删除列表,则永远不要向前迭代:如果删除索引为0的元素,则删除后现在将在索引为0的位置有一个新元素(位于索引1的那个),但是现在您已移至索引1,最终缺少一个元素。
这部分运行得非常快,但是,距离还远远不够。
从图书馆获得帮助
当您无法从目录名称获取信息时,下一个最佳选择是尝试查找元数据。 这是我使用Python的第二个主要原因:您可以找到几乎所有内容的库,这是一件了不起的事情。
抓取mp3元数据:
我很快就发现,MacOS有一个非常方便的命令:“ mdls”。 这是众所周知的Unix命令“ ls”的扩展,只是此命令获取了文件中的所有元数据信息。 我还发现,有人已经为它编写了一个非常易于使用的包装程序而烦恼,您可以在这里找到它:https://github.com/fractaledmind/metadata
用法非常简单:安装后(仓库中的指令非常清楚),您只需导入元数据,然后在文件上调用list函数。 为此,我得到了os.walk()返回的根元素的最后一个元素,并附加了当前文件。 从列表返回的数据中,通过传递相应的字符串来访问元素:“ recording_year”,“ authors”,“ album”。 我建议先从命令行尝试,以查看list()方法的输出。 另外,请记住,mp3标记本身可能是不完整的,因此我必须使用计数器和布尔值来确保已找到完整的元数据:
对于文件中的f:
found = False #bool存储我们是否找到了元数据
路径= root.split(“ /”)
curr =路径[len(paths)-1]
#搜索mp3。 如果元数据返回记录年份,我们将bool设置为True,则无需检查其他文件
如果 f.endswith(“。mp3”):
尝试 :
file_data = metadata.list(root +“ /” + f)
除了 :
#在这里你可以做你想做的。 我写了一个单独的文件进行调试。 如果数据检索失败,请尝试下一个文件
继续
count = 0 #计数器以确保获得所有3个元素
如果 file_data中为“ recording_year”:
日期= str(file_data [“ recording_year”])
数+ = 1
如果 file_data中的“作者”:
艺术家= str(file_data ['authors'] [0])
数+ = 1
如果 file_data中有 “专辑”:
相册= file_data ['相册']
数+ = 1
如果 count == 3:
找到= 真
musicSet.add(音乐(日期,艺术家,专辑))
break #如果我们在这里,我们有数据,无需继续迭代此目录中的文件
FLAC文件呢?
FLAC元数据的存储方式与mp3元数据的存储方式不同,因此无法通过mdls进行访问,因此,我们无法使用上面的库来检索它。 但是,当然,有一种方法可以检索它,当然,还有一个Python库可以帮助我们。 它被称为“ pyflacmeta”,您也可以在github中找到它:https://github.com/isaaczafuta/pyflacmeta
同样,这非常易于使用,并且逻辑与上面相同,只是我遇到了一个问题,即标签可以包含大小写字符的任意组合,因此我迭代列表而不是直接访问它们由flac_data.keys()返回,并执行以下操作:
计数= 0
对于 flac_data.keys()中的密钥:
如果 key.lower()==“日期”:
日期= flac_data [键]
数+ = 1
#其余部分与mp3的逻辑相同,但使用此基本语法
因此,到目前为止,我们可以相信,每个带有适当标签或正确命名的文件都将正确放置在我们的文件集中。 但是,有两个问题:您可能有其他格式的音乐文件,例如.wav,.ogg等,并且注定会有从未正确标记的文件。
如果其他所有方法都失败了……。
万一以上任何选项都不起作用怎么办? 好吧,当然有互联网数据库。 经过阅读和研究后,我最终选择了Discogs(https://www.discogs.com/)。 您需要注册一个帐户,并获得一个令牌才能连接到API,但是两者都是免费的,并且相当容易使用。 完成操作并获得API后,只需导入discogs_client,然后将身份验证信息放入代码中:
app = [包含您的应用名称的字符串]
令牌= [包含令牌的字符串]
然后,您启动客户端:
discog = discogs_client.Client(app,user_token = token)
然后您就可以使用它了。 我必须创建一个函数来准备路径,以便提取专辑名称+艺术家,以便可以在数据库中找到它。 那是当“不需要的”列表派上用场的时候,因为它使我可以删除可能会使搜索混乱的最频繁的字符串。 路径的其余预处理是基本的字符串操作,它也完全取决于如何设置目录和文件以将其发布到此处。
与以前的情况相比,我实际检索数据的方式不那么直观,并且可能有更好的方法。 我所做的基本上是:首先按名称搜索,它返回结果列表。 从这些文件中,您可以从第一个文件中获得ID,然后通过discogs.release()查找专辑。 这将使您获得年份,艺术家,专辑。
如果找不到: #这是我们在循环开始时设置的布尔值。 如果我们在这里,则上述两个库都无法按预期工作。
相册= preparePath(根)
#首先按名称搜索。 从结果中获取ID,然后按ID搜索发布以获取年份
尝试 :
结果= discog.search(专辑,类型=“发行”)
#如果没有结果,则计数为0
如果 results.count <1:
打破
id =结果[0] .id
版本= discog.release(id)
musicSet.add(音乐(str(release.year),release.artists [0] .name,release.title))
除了 :
继续
打破
Discogs非常出色,并且有可能存在某种东西,它将找到它。 但是,有两个问题:
- 当然,由于它必须通过Internet进行连接,因此它比以前的任何一种方法都要慢得多。 如果收藏量很大,请记住这一点
- 它对拼写错误非常敏感,因此如果拼写错误,它可能找不到东西。 但更糟糕的是,他们的算法可能会决定与您拼写错误的搜索最可能匹配的是您所想不到的东西。
因此,即使如此,大多数情况下您也必须要自己浏览列表。 我想不出办法。 当然,这是不可靠输入的诅咒。
顺便提一句,在某些时候我确实尝试了其他方法,例如使用MusicBrainz Picard应用程序。 问题在于,如果您想要完美的结果,则这些类型的应用程序需要您不断的反馈,因此对于大型库来说几乎是相同的。
我最终得到了大约3500张专辑的列表,并进行了一些重复,主要是由于错别字和不同的拼写。 来源是大约1 TB的音乐,分布在3个硬盘驱动器的多个文件夹中。 整个过程仅需几分钟即可完成,绝大部分时间都花在查询Discogs上,因此总体上来说非常快。 正如我说的那样,此后进行了大量手动工作,但它节省了我很多时间。
无论如何,我希望这对希望对音乐收藏进行分类的人有所帮助。 正如我所说,除了“只是因为”之外,我这样做的原因是因为我决定按时间顺序对我的整个收藏进行聆听(我知道,我会在那儿待会儿!)。 我正在写有关它的信息,因此,如果您对我的杂谈不够(当然,如果您喜欢音乐),可以在这里阅读:https://mymusicintime.blogspot.com.ar/