PowerShell設定mp4的縮圖(封面)
把這個ps1 放在主檔名相同的 mp4 jpg旁邊按右鍵 → 用PowerShell 執行
只有moov在檔案尾端的mp4,才會處理
只有寫入幾百KB的資料,很快
Win10 測試OK[code]param($mp4 = $null, $jpg = $null )
function ReadBox( $fs , $start , $end ,$search = $null){
$h = @{}
$fs.position = $start
while($fs.position -lt $end){
$boxSizePos = $fs.position ; $boxSizeLength = 4
if (($fs.position + 8) -gt $end){ break }
[void]$fs.read($buffer , 0 , 8)
$boxSize = [System.BitConverter]::ToUInt32( $buffer[3..0] , 0)
$boxType = ( $buffer[4..7] | %{[char]$_} ) -join ''
$boxEnd = $fs.position + $boxSize - 8
if ( $boxSize -eq 1 ){ #largesize
if (($fs.position + 8) -gt $end){ break }
[void]$fs.read($buffer , 0 , 8)
$boxSize = [System.BitConverter]::ToUInt64( $buffer[7..0] , 0)
$boxEnd = $fs.position + $boxSize - 16
$boxSizePos += 8
$boxSizeLength = 8
}elseif ($boxSize -eq 0 ) { #box extends to end of file
$boxEnd = $end
}
if ($boxend -gt $end ){ break }
if ($boxType -eq $search){
return @{boxSizePos = $boxSizePos ; boxSizeLength = $boxSizeLength
boxSize = $boxSize ; boxEnd = $boxEnd }
}
$h[$boxType] = @{boxSizePos = $boxSizePos ; boxSizeLength = $boxSizeLength
boxSize = $boxSize ; boxEnd = $boxEnd }
$fs.position = $boxEnd
}#end of while
if ($search -ne $null){
return $null
}
return $h
}
function addCover([io.fileinfo]$mp4 , [io.fileinfo]$jpg){
trap{ $_ ;$fs.close() ; return }
if ($jpg.length -gt $bufferSize){echo 'jpg size is too big';pause; return }
$fs = $mp4.Open('open' , 'readWrite' , 'read')
$h = ReadBox $fs 0 $fs.length
if ( $h.ftyp -eq $null -or $h.ftyp.boxSizePos -ne 0 -or
$h.moov -eq $null -or $h.mdat -eq $null){
throw "$mp4 :This is not a MP4 file."
}
if ($h.moov.boxSizePos -lt $h.mdat.boxSizePos){
throw "$mp4 : only meta info in the end of file will be processed"
}
$box = @()
$box += $h.moov
#============================================================
$insertPos = $backupPos = $null
#udta meta hdlr
[uint32]$TotalSize = 8 + 12 + 33 +
#ilst covr data
8 + 8 + 16 + $jpg.length
$boxExist = $true
[byte[]] $addBox = $null
$addBox = 'udta','meta','ilst','covr','data'| %{
$boxHeadSize = 8
if ( $_ -eq 'ilst' ) {$boxHeadSize += 4} # search in meta fullbox
if ($boxExist ){
$box += ReadBox $fs ($box[-1].boxSizePos + $boxHeadSize) $box[-1].boxEnd $_
}
if ($box[-1] -eq $null ){ #box not found
$boxExist = $false
$insertPos = $backupPos = $box[-2].boxEnd
$arrByte = [System.BitConverter]::GetBytes( $TotalSize )
$arrByte[3..0] #boxSize
$_[0..3] #boxType
if ($_ -eq 'meta') {
,0 * 4 #meta fullbox
(,0 * 3 ) + 33 #hdlr size
'hdlr'[0..3]
,0 * 8
'mdirappl'[0..7]
,0 * 9
}
if ($_ -eq 'data'){
(,0 * 3 ) + 13
,0 * 4
}
}#end of if
$TotalSize -= 8
if ($_ -eq 'meta') { $TotalSize -= ( 4 + 33) } #fullbox and hdlr
}#end of foreach cmdlet
#===============================================
if ($insertPos -eq $null){ # all box exist , replace jpg only
$insertPos = $box[-1].boxSizePos + 16
$backupPos = $box[-1].boxEnd
$editSize = $jpg.length - ($backupPos - $insertPos)
$addSize = $jpg.length
}else{#some box not exist
$editSize = $addSize = $addBox.length + $jpg.length
}
if ( ($insertPos + $addSize) -ne $backupPos){
#backup
$backupSize = $fs.length - $backupPos
if ( $backupSize -gt $bufferSize ){
throw "bufferSize $($bufferSize/1mb)mb is too small"
}
$fs.position = $backupPos
[void]$fs.read($buffer , 0 , $backupSize )
#write backup
$fs.position = $insertPos + $addSize
$fs.write($buffer , 0 , $backupSize )
}
#read jpg
$fs1 = $jpg.OpenRead()
[void]$fs1.read($buffer , 0 , $jpg.length)
$fs1.close()
#write addbox jpg
$fs.position = $insertPos
if ($addbox -ne $null ){ $fs.write($addBox , 0 , $addBox.length) }
$fs.write($buffer , 0 , $jpg.length)
#edit boxSize
foreach ($a in $box) {
if ($a -eq $null ){ break }
if ( $a.boxSizeLength -eq 4 ){
[uint32]$boxSize = $a.boxSize + $editSize
$arrByte = [System.BitConverter]::GetBytes($boxSize)
$arrByte = $arrByte[3..0]
}else{
[uint64]$boxSize = $a.boxSize + $editSize
$arrByte = [System.BitConverter]::GetBytes($boxSize)
$arrByte = $arrByte[7..0]
}
$fs.position = $a.boxSizePos
$fs.write($arrByte , 0 , $a.boxSizeLength)
}
$fs.close()
}#end of function
#============================================================
$bufferSize = 20mb
$buffer = new-object byte[]($bufferSize)
cd -literal $PSScriptRoot
[Environment]::CurrentDirectory = pwd
if ($mp4 -eq $null -or $jpg -eq $null ){
dir *.mp4 -file |%{
if (Test-Path -literal "$($_.BaseName).jpg" ){
addCover $_ "$($_.BaseName).jpg"
}
}
}else{
addCover $mp4 $jpg
}[/code] 支持楼主原创!谢谢分享! [i=s] 本帖最后由 nwm310 于 2018-7-15 15:04 编辑 [/i]
之前的版本,檔尾可能會殘留多餘資料,現在已修正
新增:
[color=DarkRed][b]支持 PS 2.0[/b][/color]
[color=DarkRed][b]如果有相同主檔名的txt,可以加入章節[/b][/color]
[quote]
按熱鍵可以快速跳到那個時間
VLC media player:Shift + P 、 Shift + N
PotPlayer:Shift + Page Up 、 Shift + Page Down
MPC-HC:Page Up 、 Page Down
並不是每個播放軟體都可顯示章節
[/quote]
txt編碼格式可以是:ANSI、Unicode、UTF-8(需要有BOM)
txt內容
[quote]
章節開始時間 章節名稱(可有可無)
0:3:0
0:5:0 時間和名稱以空格分隔,最前面可以有空格
1:0 1小時0分
0:1:0 1分0秒
-0:3:0 前面有 - 表示取消3分0秒的章節
0:27:38.1234567 精密度,但最好不要寫這麼多
以免要取消某章節時,不知道完整的時間是什麼
這一行,和上一行,不是正確的格式,所以會被略過、不會被加入
1:62:999 錯誤的時間數字,會被略過、不會被加入
可以不按照時間順序擺放
章節名稱 255byte。其中,一個中文字3byte(以UTF-8格式儲存於mp4)
好像最多255個章節? 不確定
[/quote]
當 jpg 或 txt 檔案大小為 0 byte時,可以清除縮圖 或 章節資料[code]
function ReadBox( $fs , $start , $end ,$search = $null ){
$h = @{}
$fs.position = $start
if ( $search -eq $null ){
$searchBox = $null
}else{
$searchBox = @($search)[0]
$L = @($search).length
if( $L -gt 1 ){
$rest = $search[ 1..$L ]
}
}
while($fs.position -lt $end){
$boxSizePos = $fs.position ; $boxSizeLength = 4
if (($fs.position + 8) -gt $end){ break }
[void]$fs.read($buffer , 0 , 8)
$boxSize = [System.BitConverter]::ToUInt32( $buffer[3..0] , 0)
$boxType = ( $buffer[4..7] | %{[char]$_} ) -join ''
$boxEnd = $fs.position + $boxSize - 8
if ( $boxSize -eq 1 ){ #largesize
if (($fs.position + 8) -gt $end){ break }
[void]$fs.read($buffer , 0 , 8)
$boxSize = [System.BitConverter]::ToUInt64( $buffer[7..0] , 0)
$boxEnd = $fs.position + $boxSize - 16
$boxSizePos += 8
$boxSizeLength = 8
}elseif ($boxSize -eq 0 ) { #box extends to end of file
$boxEnd = $fs.length
}
if ($boxend -gt $end ){ break }
if ($boxType -eq $searchBox){
$h1 = @{boxSizePos = $boxSizePos ; boxSizeLength = $boxSizeLength
boxSize = $boxSize ; boxEnd = $boxEnd }
if ($L -eq 1 ){
return $h1
}else{
$boxHeadSize = 8
if ($boxType -eq 'meta') {$boxHeadSize +=4}
return @($h1) + (ReadBox $fs ($boxSizePos + $boxHeadSize ) $boxEnd $rest )
}
}
$h[$boxType] = @{boxSizePos = $boxSizePos ; boxSizeLength = $boxSizeLength
boxSize = $boxSize ; boxEnd = $boxEnd }
$fs.position = $boxEnd
}#end of while
if ($search -eq $null){ return $h }else{ return $null }
}
function addSomeThing([io.fileinfo]$mp4 , [io.fileinfo]$jpg , [io.fileinfo]$txt = $null){
trap{ $_ ;read-host;$fs.close() ; return }
$fs = $mp4.Open('open' , 'readWrite' , 'read')
$h = ReadBox $fs 0 $fs.length
if ( $h.ftyp -eq $null -or $h.ftyp.boxSizePos -ne 0 -or
$h.moov -eq $null -or $h.mdat -eq $null){
throw "$mp4 :This is not a MP4 file."
}
if ($h.moov.boxSizePos -lt $h.mdat.boxSizePos){
throw "$mp4 : only meta info in the end of file will be processed"
}
$box = @()
$box += $h.moov
#============================================================
$fileEmpty = $false
if ($jpg -ne $null){ #add Cover
if ($jpg.length -gt $bufferSize){echo 'jpg size is too big';read-host; $fs.close();return }
if ($jpg.length -eq 0 ) { $fileEmpty = $true}
$arrSearch = 'udta\meta\ilst\covr' -split '\\'
}elseif ($txt -ne $null){ #add chapter
if ($txt.length -eq 0 ){ $fileEmpty = $true }
$arrSearch = 'udta\chpl' -split '\\'
}else{ $fs.close() ;return }#do nothing
#===================================================
$box += ReadBox $fs ($box[-1].boxSizePos + 8) $box[-1].boxEnd $arrSearch
if ( $box[-1] -eq $null ) { # some box not found
if ( $fileEmpty ) { $fs.close() ;return }
$insertPos = $backupPos = $box[-2].boxEnd
}else{ # all box found , still reCreate or clear last box
$insertPos = $box[-1].boxSizePos
$backupPos = $box[-1].boxEnd
}
#==============create last box and box which is no exist================
if ( ! $fileEmpty ){
if ($jpg -ne $null ){ # For Cover
#udta (meta + hdlr)
$TotalSize = 8 + (12 + 33) +
#ilst (covr + data + jpg )
8 + ( 8 + 16 + $jpg.length )
[uint32[]]$arrBoxsize = 0 , 8 , (12 + 33) , 8 |%{
$TotalSize -= $_ ; $TotalSize }
$dataSize = $jpg.length
}else{ #==========read chapter in mp4======================
$chapterInfo = @{}
if ( $insertPos -ne $backupPos ){ #all box exist
if ( ($backupPos - $insertPos) -gt 17 ) {
$fs.Position = $insertPos + 17
while($fs.Position -lt $backupPos){
if ( ($fs.Position + 8) -gt $backupPos ){ break }
[void]$fs.read($buffer , 0 , 8)
$ticks = [System.BitConverter]::ToInt64( $buffer[7..0] , 0)
if ( ($fs.Position + 1) -gt $backupPos ){ break }
[byte]$n = $fs.ReadByte()
if ( ($fs.Position + $n) -gt $backupPos ){ break }
[void]$fs.read($buffer , 0 , $n)
if ($n -gt 0){
$chapterInfo[$ticks] = @([byte]$n) + $buffer[0..($n-1)]
}else{
$chapterInfo[$ticks] = @([byte]$n)
}
}#end of while
}#end of if
}#end of if all box exist
#===============read from txt========================
gc -literal $txt |%{
$a , $b = $_.split('',2,1)
if ($t = $a -as [timespan] ){ #check time format
if ( $t.ticks -lt 0 ){#remove
$chapterInfo.Remove( -($t.ticks) )
}else{#add
$n = [System.Text.Encoding]::UTF8.GetByteCount(@($b))
if ( $n -lt 256 ){
$chapterInfo[$t.ticks] = @([byte]$n) + [System.Text.Encoding]::UTF8.GetBytes(@($b))
}
}
}
}#end of gc
#==============create byte data=====================
$n = 0
$chapter = $chapterInfo.Keys | sort | %{
$n++
$arrByte = [System.BitConverter]::GetBytes( $_ )
$arrByte[7..0]
$chapterInfo[$_]
}
if ( $n -gt 255 ) { $n = 255}
$chapter = @([byte]$n) + $chapter
$dataSize = $chapter.length
#=========================================================
#udta chpl
$TotalSize = 8 + 16 + $chapter.length
[uint32[]]$arrBoxsize = 0 , 8 |%{ $TotalSize -= $_ ; $TotalSize}
}#end of if For chapter
[byte[]]$addBox =
for ($i = ($box.length -1) -1 ; $i -lt $arrSearch.length ; $i++){
$arrByte = [System.BitConverter]::GetBytes( $arrBoxsize[$i] )
$arrByte[3..0] #boxSize
$arrSearch[$i][0..3] #boxType
switch ( $arrSearch[$i] ){
'meta'{
,0 * 4 #meta fullbox
(,0 * 3 ) + 33 ; 'hdlr'[0..3] #hdlr
,0 * 8 ; 'mdirappl'[0..7] ; ,0 * 9
break
}
'covr'{
$arrByte = [System.BitConverter]::GetBytes( $arrBoxsize[$i] - 8 )
$arrByte[3..0] ; 'data'[0..3]
(,0 * 3 ) + 13 ; ,0 * 4
break
}
'chpl'{
1 ; ,0 * 7
break
}
}#end of switch
}#end of for
}#end of if ! $fileEmpty
#=====================================================================
if ($fileEmpty){ #clear content
$addSize = 0
}else{ #add or replace content
$addSize = $addBox.length + $dataSize
}
$editSize = $addSize - ($backupPos - $insertPos)
#==============backup & restore=====================
if ( ($insertPos + $addSize) -ne $backupPos){
#backup
$backupSize = $fs.length - $backupPos
if ( $backupSize -gt $bufferSize ){
throw "bufferSize $($bufferSize/1mb)mb is too small"
}
$fs.position = $backupPos
[void]$fs.read($buffer , 0 , $backupSize )
#restore
$fs.position = $insertPos + $addSize
$fs.write($buffer , 0 , $backupSize )
}
#==============write $addbox、jpg or chapter==============
if ( ! $fileEmpty){ # jpg or txt is not empty
$fs.position = $insertPos
$fs.write($addBox , 0 , $addBox.length)
if ($jpg -ne $null){ #write jpg
$fs1 = $jpg.OpenRead()
[void]$fs1.read($buffer , 0 , $jpg.length)
$fs1.close()
$fs.write($buffer , 0 , $jpg.length)
}else{ #write chapter
$fs.write($chapter , 0 , $chapter.length)
}
}
#================edit boxSize===============================
for ($i = 0 ; $i -lt ($box.length - 1) ; $i++) {
if ( $box[$i].boxSizeLength -eq 4 ){
[uint32]$boxSize = $box[$i].boxSize + $editSize
$arrByte = [System.BitConverter]::GetBytes($boxSize)
$arrByte = $arrByte[3..0]
}else{
[uint64]$boxSize = $box[$i].boxSize + $editSize
$arrByte = [System.BitConverter]::GetBytes($boxSize)
$arrByte = $arrByte[7..0]
}
$fs.position = $box[$i].boxSizePos
$fs.write($arrByte , 0 , $box[$i].boxSizeLength)
}
if ($editSize -lt 0){
$fs.SetLength($fs.length + $editSize)
}
$fs.close()
}#end of function
#============================================================
$bufferSize = 20mb
$buffer = new-object byte[]($bufferSize)
cd -Literal (Split-Path $MyInvocation.MyCommand.Path)
#cd -literal $PSScriptRoot
[Environment]::CurrentDirectory = pwd
#dir *.mp4 -file |%{
dir *.mp4 |?{! $_.PSIsContainer}|%{
if (Test-Path -literal "$($_.BaseName).jpg" -PathType Leaf){
addSomeThing $_ "$($_.BaseName).jpg"
}
if (Test-Path -literal "$($_.BaseName).txt" -PathType Leaf){
addSomeThing $_ $null "$($_.BaseName).txt"
}
}#end of foreach
[/code]
页:
[1]