有没有办法在 Windows 文件资源管理器下按精确长度搜索视频?(即长度:00:31:42)
有一个命令“length:”,但它只支持“非常短、短、中、长、非常长”这样的值。
答案1
您可以搜索视频和其他媒体文件以查找特定的Length
(System.Media.Duration
),但这并不像您希望的那样简单。属性系统查看器显示Length
存储为无符号 8 字节整数(Uint64
在电源外壳)。
姓名 | 价值 |
---|---|
显示名称 | 长度 |
可见 | 是的 |
规范名称 | 系统.媒体.持续时间 |
属性键 | {64440490-4C8B-11D1-8B70-080036B11A03},3 |
财产种类 | 无符号整数(8 字节) |
该数字被解释为.Net系统时间跨度结构。根据文档:
TimeSpan 对象的值是等于所表示时间间隔的刻度数。一个刻度等于 100 纳秒,即千万分之一秒。TimeSpan 对象的值范围可以从 TimeSpan.MinValue 到 TimeSpan.MaxValue。
在电源外壳,您可以将原始值(刻度)转换为格式化的字符串,反之亦然,如下所示:
PS C:\> ([timespan]75553440000).ToString()
02:05:55.3440000
PS C:\>
PS C:\> [timespan]::Parse('02:05:55').Ticks
75550000000
虽然显示的Length
以秒为单位变化,但存储的值精确到最接近的毫秒,但在显示值时它们会被截断。因此,如果你想搜索00:00:01
,你实际上必须指定从00:00:01
到的00:00:01.999
范围。电源外壳将实际存储的值与将显示的值反向转换为刻度后得到的值进行比较:
gci $home\videos *.avi -s | %{
$comFolder = $shell.NameSpace((Split-Path $_.FullName))
$DisplayTime = $comFolder.GetDetailsOf($comFolder.ParseName(($_.Name)) , 27)
$Exactticks = $comFolder.ParseName(($_.Name)).ExtendedProperty("Duration")
$ApproxTicks = [TimeSpan]::Parse( $DisplayTime).Ticks
[PSCustomObject]@{
'Display Time' = $DisplayTime
'Exact Ticks' = $Exactticks
'Approx Ticks' = $ApproxTicks
'Difference' = $Exactticks - $ApproxTicks
}} | sort difference
输出:
Display Time Exact Ticks Approx Ticks Difference
------------ ----------- ------------ ----------
02:43:36 98160065306 98160000000 65306
02:43:36 98160240000 98160000000 240000
... ... ... ...
02:58:12 106929120000 106920000000 9120000
02:33:40 92209920000 92200000000 9920000
搜索条件必须以刻度表示,因此要搜索显示长度为的文件1:53:24
:
PS C:\> ($LowBound = [timespan]::Parse('1:53:24').Ticks)
68040000000
PS C:\> ($HighBound = ([timespan]$LowBound).Add([timespan]::Parse('0:0:0.999')).Ticks)
68049990000
PS C:\> ($SearchExpression = 'Length:>={0} AND <= {1}' -f $LowBound , $HighBound )
Length:>=68040000000 AND <= 68049990000
PS C:\> $SearchExpression | Set-Clipboard
然后,打开探索者窗口到我的Videos
文件夹,单击搜索框并按ctrl+V得到:
执行搜索将返回具有所需长度的视频:
不知道你的工作流程的细节,很难猜测如何让这一切尽可能简单。一种方法是电源外壳hh:mm:ss
接受输入并执行搜索的脚本探索者:
### To keep example simple, the code assumes
### there are no other **Explorer** windows open.
$shell = New-Object -com shell.application
$SearchTarget = $shell.NameSpace("shell:My Video").Self.Path
$shell.Open($SearchTarget)
While ($shell.Windows().Count -eq 0) {sleep -m 25}
$TargetWin = @($shell.Windows())[0]
While ($true) {
$SearchCriteria = Read-Host 'Enter length (hh:mm:ss)'
$SpanTicks = [timespan]::Parse($SearchCriteria).Ticks
$FilterString = 'Length:>= {0} <= {1}' -f $SpanTicks , ($SpanTicks + 9990000)
$TargetWin.Document.FilterVIew($FilterString)
$TargetWin.Visible = $true
}
编辑:改进的脚本:从窗口背景启动,指定范围以及特定值,以及多个值/范围。
将以下内容保存为
.ps1
文件。打开电源外壳窗口并使用 dot-sourcing 运行来安装:
. <path to .ps1 file>
Search media by Duration
现在,右键单击任何文件夹的背景即可使用。
If ($Args) { ### Launched from directory background context menu
$folderPath = $Args[0]
$shell = New-Object -com shell.application
$TargetWindow = @($shell.Windows() | where $folderPath -eq $_.Document.Folder.Self.Path )[0]
Function Get-Filter {
param(
[Parameter(Mandatory)][Uint64]$ts1,
[Parameter(Mandatory)][Uint64]$ts2
)
Process {
'(>= {0} <= {1})' -f $ts1 , ($ts2 + 9990000)
}
}
$Prompt = @"
Enter one or more (comma-separated) criteria as either:
`ta Specific TimeSpan (delimiters optional): "hh[:| |-|.]mm[:| |-|.]ss"
`tor specify a range : "<lowerTimeSpan>..<upperTimeSpan>`n`n
"@
$SearchCriteria = Read-Host $Prompt
$FilterText = 'Length: ({0})' -f (($SearchCriteria -split '\s*\,\s*' | ForEach {
Switch -Regex ($_) {
'^(\d{2})[\:\ \.\-]?(\d{2})[\:\ \.\-]?(\d{2})$' {
$Ticks = [timespan]::Parse('{0}:{1}:{2}' -f $Matches[1..3]).Ticks
$Filter = Get-Filter $Ticks $Ticks
}
'^(\d{2})[\:\ \.\-]?(\d{2})[\:\ \.\-]?(\d{2})\.\.(\d{2})[\:\ \.\-]?(\d{2})[\:\ \.\-]?(\d{2})$' {
$Ticks1 = [timespan]::Parse(('{0}:{1}:{2}' -f $Matches[1..3])).Ticks
$Ticks2 = [timespan]::Parse(('{0}:{1}:{2}' -f $Matches[4..6])).Ticks
$Filter = Get-Filter $Ticks1 $Ticks2
}
}
$Filter
}) -join ' OR ' )
If ($FilterText) {
$TargetWindow.Document.FilterView($FilterText)
}
} Else { ### Create context menu entry
$Key = 'HKCU:\Software\Classes\Directory\Background\Shell\SearchMediaByDuration'
If (!(Test-Path $key)) {
New-Item -Path $key -Value 'Search media by &Duration'
$PSexe = '%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass'
$splat = @{
'Path' = "$key\Command"
'Value' = ('{0} -File {1} "%V"' -f $PSexe , $PSCommandPath)
'ItemType' = 'ExpandString'
}
New-Item @splat
}
}