我一直在尝试找到一种方法来编辑“标签”部分,将其与 MP4 的“标题”部分交换。
问题是我无法使用 ExiftTool、MP3Tag、MediaMonkey 执行此操作,尝试手动右键单击并更改每个文件的标签简直就是一场噩梦。
我最接近的是一个名为 MP4 Video & Auto Tag Editor 的应用程序,因为它可以让您编辑 Microsoft Xtra Atoms,但可惜...它没有办法对整个目录执行此操作
我找到了这个 PowerShell 脚本,它显示了所有元数据。
但是,我不知道如何准确编辑“标签”部分以实际自定义和交易值。我知道 ExiftTool 只能“读”而不能“写”,所以我最大的担心是我面临同样的问题。
SRCDIRECTORY代表我正在使用的目录...
Function Get-MP3MetaData
{
[CmdletBinding()]
[Alias()]
[OutputType([Psobject])]
Param
(
[String] [Parameter(Mandatory=$true, ValueFromPipeline=$true)] $Directory
)
Begin
{
$shell = New-Object -ComObject "Shell.Application"
}
Process
{
Foreach($Dir in $Directory)
{
$ObjDir = $shell.NameSpace($Dir)
$Files = gci $Dir| ?{$_.Extension -in '.mp3','.mp4'}
Foreach($File in $Files)
{
$ObjFile = $ObjDir.parsename($File.Name)
$MetaData = @{}
$MP3 = ($ObjDir.Items()|?{$_.path -like "*.mp3" -or $_.path -like "*.mp4"})
$PropertArray = 0,1,2,12,13,14,15,16,17,18,19,20,21,22,27,28,36,220,223
Foreach($item in $PropertArray)
{
If($ObjDir.GetDetailsOf($ObjFile, $item)) #To avoid empty values
{
$MetaData[$($ObjDir.GetDetailsOf($MP3,$item))] = $ObjDir.GetDetailsOf($ObjFile, $item)
}
}
New-Object psobject -Property $MetaData |select *, @{n="Directory";e={$Dir}}, @{n="Fullname";e={Join-Path $Dir $File.Name -Resolve}}, @{n="Extension";e={$File.Extension}}
}
}
}
End
{
}
}
ForEach($item in ("SRCDIRECTORY" |Get-MP3MetaData)){
$itemTitle = $item.Title
$itemTags = $item.Tags
}
虽然我无法找到“纯”PowerShell 方法来执行此操作,但这里有一篇与如何在 C# 中执行此操作相关的类似帖子:https://stackoverflow.com/a/24053204/433069此人回答的要点是使用(历史上 Microsoft 支持的)Windows® API Code Pack for Microsoft® .NET Framework,然后使用此代码段(这是此代码的 C# 版本):
var shellFile = ShellFile.FromParsingName(filePath);
ShellPropertyWriter w = shellFile.Properties.GetPropertyWriter();
w.WriteProperty(SystemProperties.System.Title, "New Title");
w.Close();
问题是微软不再提供这个API代码包(至少在我的搜索中)。然而,一些开发人员复制了源代码并将其发布到 GitHub here。
这为微软如何做到这一点提供了有趣的见解。
如果您查看 C# 代码,您会发现最终它们会调用以下 Shell 类 IShellItem2::GetPropertyStore,然后调用 IPropertyStore::SetValue,但是这些是 C++ API,它们不会似乎是通过 COM 公开的,这意味着(据我所知)PowerShell 中没有直接的方法来实现此目的。
这里有一个有趣的讨论是否有可能获取不在 shell 命名空间中的项目的 shell 属性?其中该开发人员尝试以各种方式访问此接口,但运气不佳。我相信那里提供的解决方案只会得到您读取了对属性的访问权限(我很愿意纠正)。
理论上,如果您想运送“含电池”,您可以重写 Windows API 代码包的相关部分,然后根据需要
Add-Type
将它们放入。然而对我来说,我只是想让一些东西发挥作用。
因此,我提取了该项目生成的两个 NuGet 包(
windowsapicodepack-core.1.1.2
和 windowsapicodepack-shell.1.1.1
),将 DLL(Microsoft.WindowsAPICodePack.dll
和 Microsoft.WindowsAPICodePack.Shell.dll
)提取到与我的脚本相同的路径中,然后编写了以下内容:
$targetFile = "C:\temp\Sample.mp4"
Add-Type -Path $PSScriptRoot\Microsoft.WindowsAPICodePack.Shell.dll
$shellFile = [Microsoft.WindowsAPICodePack.Shell.ShellFile]::FromFilePath($targetFile)
$propertyWriter = $shellFile.Properties.GetPropertyWriter()
$titlePropertyKey = [Microsoft.WindowsAPICodePack.Shell.PropertySystem.PropertyKey]::new([Guid]::new("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 2)
$propertyWriter.WriteProperty($titlePropertyKey, "New Title")
$propertyWriter.Close()
它是 C# 代码的粗略音译。最大的问题是我无法加载静态属性类型(
Microsoft.WindowsAPICodePack.Shell.PropertySystem.SystemProperties.System.Title
),所以我最终像静态类一样重新创建了PropertyKey
。
我可以确认这适用于我拥有的几个示例文件。