我有一个项目,从一个长期播放的广播节目中收集和组织 mp3 文件。由于起源和来源各不相同,命名约定和 ID2/3 标签无处不在。
我想创建一组规范化文件。为此,我开始保留目录结构和文件名,并将其符号链接到标准化的目录结构和文件名。这样,我可以很容易地知道我拥有什么和缺少什么,但也知道何时遇到我已经拥有的“野外”节目。
但仍然存在 ID2/3 标签的问题。我也想标准化它们,但如果不更改源 mp3,我就无法做到这一点。
我在想,在理想的情况下,我更新 mp3 文件的副本,针对原始文件创建一个二进制补丁,然后以某种方式创建该补丁的符号链接以及原始文件,以创建一个新的规范化虚拟文件这对文件系统是透明的。
对于这个问题有类似的解决方案吗?
答案1
我查看了一些熔断文件系统,最简单的工作方式是连接文件系统作者:彼得·施莱尔。如果不出意外的话,这将是一个很好的起点,因为 C 代码实现很小并且是独立的。
基本上,对于任何文件A您想要“符号链接”到某个任意源目录S你创建一个文件乙其中仅包含您想要的 id3 标签信息(版本 2),然后是一个魔术文件C文件名在某处包含字符“-concat-”。这个文件C按顺序包含名称乙和A。
当您运行 concatfs 程序时,您指定源目录S和第二个任意安装点目录中号。在中号您将看到与中相同的所有文件S,但是当你读取文件时C你会得到一个串联的乙和A,即您的新标签后跟真实的 mp3 数据。显然,只有魔术文件真正需要在S,因为您可以在魔术文件中使用绝对或相对路径。
这并不完美,因为如果你的A文件包含 id3v2 标签(在文件的开头),它们将被添加到您的文件中乙文件。此外,id3v1 标签(位于文件末尾)仍然存在。然而,通过一些 C 编码,应该可以抑制A。此外,更改魔术字符串“-concat-”也很简单。
以下是我用来测试 concatfs 的命令。安装该fuse-devel
包以便您可以编译代码。其余的不需要你成为root。
下载并解压源代码并编译它们:
cd concatfs
gcc -Wall src/concatfs.c $(pkg-config fuse --cflags --libs) -o concatfs
创建 2 个目录,启动命令,然后复制一些要播放的 mp3 文件,删除其中的所有标签:
mkdir -p ~/myfuse/src ~/myfuse/mnt
./concatfs ~/myfuse/src ~/myfuse/mnt # runs in background
cd ~/myfuse/src
cp ....sometestfile.mp3 try.mp3
id3v2 --delete-v1 try.mp3
id3v2 --delete-v2 try.mp3
id3info try.mp3
创建虚拟文件,即 magic 文件,并向虚拟文件添加一些 id3v2 标签:
echo -e 'dummy.mp3\ntry.mp3' > try.mp3-concat-.mp3
> dummy.mp3
id3v2 -2 -c 'my description:my comment' -a 'my artist' -A 'my album' -y '2010' -T 1/2 dummy.mp3
hexdump -C dummy.mp3
ls -l
在第二个目录中查找相同的文件,但魔术文件的大小和内容是两个文件的串联:
ls ../mnt/ -l
cat ../mnt/try.mp3-concat-.mp3 | wc -c
id3info ../mnt/try.mp3-concat-.mp3
mediainfo ../mnt/try.mp3-concat-.mp3
当您终止程序时,您可能需要清理安装:
fusermount -u ~/myfuse
答案2
以下是我的案例,说明为什么数据库可能是一个很好的解决方案:
id_tag
由于 SQL 查询语言非常灵活,因此它允许您通过声明性寻址系统将新旧文件名关联起来。- 它可以扩展以提供额外的用途 - 作为示例,我在下面的脚本中添加了一个类别标签。
- 如果使用 SQLite,它可以从命令行使用,它会生成可以通过管道、重定向等方式输出的 STDOUT。
我的示例基于这样的想法:每个节目都是由唯一记录标识的,在单个表中(比多个表更简单)有关该广播节目的所有信息都将包含在该表中的记录中,使用sqlite
安装sqlite
apt-get install sqlite3
或使用yum
, pacman
, 从源代码构建或其他
cd 进入数据库目录并创建脚本
文件:music_db.sh
#!/bin/bash
if [ -f music_db ]
then
rm -v music_db
fi
sqlite3 music_db << 'EOF'
create table radio_shows(
original_filename text,
new_filename text,
id_tag text,
category_tags text
);
EOF
sqlite3 music_db << 'EOF'
insert into radio_shows
(original_filename, new_filename, id_tag, category_tags) values
('first_show-001', 'first_show-001--new_naming_scheme', 'id_tag aaa', 'favourite_shows'),
('first_show-002', 'first_show-002--new_naming_scheme', 'id_tag aab', 'favourite_shows'),
('first_show-003', 'first_show-003--new_naming_scheme', 'id_tag aac', 'crap_shows'),
('first_show-004', 'first_show-004--new_naming_scheme', 'id_tag aad', 'favourite_shows');
EOF
然后从命令行使用:例如添加一个新节目
% sqlite3 music_db <<EOF
insert into radio_shows
(new_filename, id_tag, category_tags) values
('a_new_show', 'xyz_123', 'good_show');
EOF
检索节目(稍后)并打印到 STDOUT
% sqlite3 music_db <<EOF
select new_filename, id_tag
from radio_shows
where category_tags = "good_show";
EOF
结果
a_new_show|xyz_123