原文:Mp3文件标签信息读取和写入(Kotlin) - Stars-One的杂货小窝
最近准备抽空完善了自己的星之音乐下载器,发现下载下来的mp3文件没有对应的标签
也是了解可以通过mpatric这个库来实现标签的读取和写入,下面介绍一下关于mp3标签和贴上对应的代码示例
在研究过程,发现mp3音乐标签主要有以下几种格式:
ID3v1ID3v2(目前常用)APEv2ID3v1位于文件尾部,不支持封面,不支持一些特殊字符,长度短,但兼容老设备。
ID3v2是ID3v1的后继替代者,位于文件头部,支持封面和特殊字符,长度任意。推荐使用。
APEv2位于文件尾部(同时与ID3v1存在时,在ID3v1之前),同样支持封面和特殊字符,但不推荐用于mp3。
常用的主要是ID3v2.而ID3v2格式又可具体分为3种:
推荐用ID3v2.4 UTF-8,如果设备不支持可退一步用ID3v2.3 UTF-16,如果设备仍不支持可再退一步用ID3v2.3 ISO-8859-1。
如果数码设备不支持,那么再试APEv2或ID3v1。根据设备支持情况进行调整就可以。
引入依赖:
<dependency>
<groupId>com.mpatric</groupId>
<artifactId>mp3agic</artifactId>
<version>0.9.1</version>
</dependency>
@Test
fun testRead() {
val mp3Path = "D:\\temp\\music-download-test\\封印されし神々(東方風神録) - Whirling Truth.mp3"
val mp3File = Mp3File(mp3Path)
if (mp3File.hasId3v2Tag()) {
val id3v2Tag = mp3File.id3v2Tag
println("唱片歌曲数量: " + id3v2Tag.track)
println("艺术家: " + id3v2Tag.artist)
println("歌曲名: " + id3v2Tag.title)
println("唱片名: " + id3v2Tag.album)
println("歌曲长度:" + mp3File.lengthInSeconds + "秒")
println("码率: " + mp3File.bitrate + " kbps " + if (mp3File.isVbr) "(VBR)" else "(CBR)")
println("专辑插画类型" + id3v2Tag.albumImageMimeType)
println("发行时间: " + id3v2Tag.year)
println("流派: " + id3v2Tag.genre + " (" + id3v2Tag.genreDescription + ")")
println("注释: " + id3v2Tag.comment)
println("歌词: " + id3v2Tag.lyrics)
println("作曲家: " + id3v2Tag.composer)
println("发行公司: " + id3v2Tag.publisher)
println("Original artist: " + id3v2Tag.originalArtist)
println("Album artist: " + id3v2Tag.albumArtist)
println("版权: " + id3v2Tag.copyright)
println("URL: " + id3v2Tag.url)
println("编码格式: " + id3v2Tag.encoder)
//专辑插画
val albumImageData = id3v2Tag.albumImage
if (albumImageData != null) {
println("专辑插图长度: " + albumImageData.size + " bytes")
println("专辑插图类型: " + id3v2Tag.albumImageMimeType)
}
val imgFile = File("D:\\temp\\output.jpg")
imgFile.writeBytes(albumImageData)
}
}
@Test
fun testWrite() {
//todo m4a转MP3
//val mp3Path = "D:\\temp\\music-download-test\\Romantic Night.mp3"
val mp3Path = "D:\\temp\\music-download-test\\test.mp3"
val imgFile = File("D:\\temp\\music-download-test\\109951167834013257.jpg")
val mp3File = Mp3File(mp3Path)
val tag = mp3File.id3v2Tag
//歌曲名
tag.title = mp3File.filename
//歌手
tag.artist = "张三"
//唱片名(专辑)
tag.album = "张三的专辑"
tag.setAlbumImage(imgFile.readBytes(), MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(imgFile))
mp3File.save("D:\\temp\\music-download-test\\output.mp3")
}
之后测试,在window系统右键属性就可以看到显示了对应的属性
| 方法名 | 说明 |
|---|---|
| getFrameCount() | 获取MP3文件帧数 |
| getStartOffset() | 获取起始设置 |
| getEndOffset() | 获取结束设置 |
| getLengthInMilliseconds() | 获取MP3长度,单位毫秒 |
| getLengthInSeconds() | 获取MP3长度,单位秒 |
| isVbr() | 是否为VBR编码,不是为 CBR编码 |
| getBitrate() | 获取码率 |
| getBitrates() | 获取码率,返回map,key为码率,value为 MutableInteger 对象 |
| getChannelMode() | 获取渠道模式 |
| isCopyright() | 是否有版权 |
| getEmphasis() | 获取强调信息 |
| getLayer() | 获取压缩级别 |
| getModeExtension() | 获取模式扩展 |
| isOriginal() | 是否是原版 |
| getSampleRate() | 获取音频采样率 |
| getVersion() | 获取版本 |
| hasXingFrame() | 判断是否有 xing帧 |
| getXingOffset() | 获取xing设置 |
| getXingBitrate() | 获取xing比特率 |
| hasId3v1Tag() | 判断是否有3v1本版标签 |
| getId3v1Tag() | 获取3v1本版标签 |
| setId3v1Tag(ID3v1 var1) | 设置3v1本版标签 |
| removeId3v1Tag() | 移除3v1本版标签 |
| hasId3v2Tag() | 判断是否有3v2本版标签 |
| getId3v2Tag() | 获取3v2本版标签 |
| setId3v2Tag(ID3v2 var1) | 设置3v2本版标签 |
| removeId3v2Tag() | 移除3v2本版标签 |
| hasCustomTag() | 判断是否有自定义标签 |
| getCustomTag() | 获取自定义标签 |
| setCustomTag(byte[] var1) | 设置自定义标签 |
| removeCustomTag() | 移除自定义标签 |
| save(String var1) | 保存mp3文件 |
| 方法名 | 说明 |
|---|---|
| getVersion() | 获取版本 |
| getTrack() | 获取唱片歌曲数量 |
| setTrack(String var1) | 设置唱片歌曲数量 |
| getArtist() | 获取艺术家 |
| setArtist(String var1) | 设置艺术家 |
| getTitle() | 获取歌曲名 |
| setTitle(String var1) | 设置歌曲名 |
| getAlbum() | 获取唱片名 |
| setAlbum(String var1) | 设置唱片名 |
| getYear() | 获取发行时间 |
| setYear(String var1) | 设置发行时间 |
| getGenre() | 获取流派 |
| setGenre(int var1) | 设置流派 |
| getGenreDescription() | ; 获取流派描述 |
| getComment() | ; 获取注释 |
| setComment(String var1) | 设置注释 |
| toBytes() | 转换为字节数组 |
| 方法名 | 说明 |
|---|---|
| getPadding() | 判断是否填充 |
| setPadding(boolean var1) | 设置是否填充 |
| hasFooter() | 判断是否有页脚 |
| setFooter(boolean var1) | 设置页脚 |
| hasUnsynchronisation() | 判断是否有不同步 |
| setUnsynchronisation(boolean var1) | 设置是否有不同步 |
| getBPM() | 获取每分钟节拍数 |
| setBPM(int var1) | 设置每分钟节拍数 |
| getGrouping() | 获取分组 |
| setGrouping(String var1) | 设置分组 |
| getKey() | 获取调号,它关系到我们整首歌曲的音高范围 |
| setKey(String var1) | 设置调号 |
| getDate() | 获取日期 |
| setDate(String var1) | 设置日期 |
| getComposer() | 获取作曲家 |
| setComposer(String var1) | 设置作曲家 |
| getPublisher() | 获取发版者 |
| setPublisher(String var1) | 设置发版者 |
| getOriginalArtist() | 获取原创艺术家 |
| setOriginalArtist(String var1) | 设置原创艺术家 |
| getAlbumArtist() | 获取专辑艺术家 |
| setAlbumArtist(String var1) | 设置专辑艺术家 |
| getCopyright() | 获取版权信息 |
| setCopyright(String var1) | 设置版权信息 |
| getArtistUrl() | 获取艺术家url地址 |
| setArtistUrl(String var1) | 设置艺术家url地址 |
| getCommercialUrl() | 获取广告url地址 |
| setCommercialUrl(String var1) | 设置广告url地址 |
| getCopyrightUrl() | 获取版权url地址 |
| setCopyrightUrl(String var1) | 设置版权url地址 |
| getAudiofileUrl() | 获取音频文件路径 |
| setAudiofileUrl(String var1) | 设置音频文件路径 |
| getAudioSourceUrl() | 获取音频资源路径 |
| setAudioSourceUrl(String var1) | 设置音频资源路径 |
| getRadiostationUrl() | 获取广播url地址 |
| setRadiostationUrl(String var1) | 设置广播url地址 |
| getPaymentUrl() | 获取付款url地址 |
| setPaymentUrl(String var1) | 设置付款url地址 |
| getPublisherUrl() | 获取发版url地址 |
| setPublisherUrl(String var1) | 设置发版url地址 |
| getUrl() | 获取MP3地址 |
| setUrl(String var1) | 设置MP3地址 |
| getPartOfSet() | 获取部分配置信息 |
| setPartOfSet(String var1) | 设置部分配置信息 |
| isCompilation() | 获取是否汇编 |
| setCompilation(boolean var1) | 设置是否汇编 |
| getChapters() | 获取章节 |
| setChapters(ArrayList |
设置章节 |
| getChapterTOC() | 获取章节目录 |
| setChapterTOC(ArrayList |
设置章节目录 |
| getEncoder() | 获取编码格式 |
| setEncoder(String var1) | 设置编码格式 |
| getAlbumImage() | 专辑插画 |
| setAlbumImage(byte[] var1, String var2) | 设置专辑插画 |
| setAlbumImage(byte[] var1, String var2, byte var3, String var4) | 设置专辑插画 |
| clearAlbumImage() | 清除专辑插画 |
| getAlbumImageMimeType() | 专辑插画类型 |
| getWmpRating() | 获取评分 |
| setWmpRating(int var1) | 设置评分 |
| getItunesComment() | 获取调音方式 |
| setItunesComment(String var1) | 设置调音方式 |
| getLyrics() | 获取歌词 |
| setLyrics(String var1) | 设置歌词 |
| setGenreDescription(String var1) | 设置类型说明 |
| getDataLength() | 获取数据长度 |
| getLength() | 获取长度 |
| getObseleteFormat() | 获取过时的格式 |
| getFrameSets() | 获取帧组 |
| clearFrameSet(String var1) | 清除帧组 |
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我试图使用yard记录一些Ruby代码,尽管我所做的正是所描述的here或here#@param[Integer]thenumberoftrials(>=0)#@param[Float]successprobabilityineachtrialdefinitialize(n,p)#initialize...end虽然我仍然得到这个奇怪的错误@paramtaghasunknownparametername:the@paramtaghasunknownparametername:success然后生成的html看起来很奇怪。我称yard为:$yarddoc-mmarkdown我做错了什么?