批处理之家's Archiver

nwm310 发表于 2018-7-1 20:57

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]

狄钦dQ 发表于 2018-7-2 11:29

支持楼主原创!谢谢分享!

nwm310 发表于 2018-7-15 10:14

[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]

Powered by Discuz! Archiver 7.2  © 2001-2009 Comsenz Inc.