Board logo

标题: [原创代码] PowerShell設定mp4的縮圖(封面) [打印本页]

作者: nwm310    时间: 2018-7-1 20:57     标题: PowerShell設定mp4的縮圖(封面)

把這個ps1 放在主檔名相同的 mp4  jpg旁邊
按右鍵 → 用PowerShell 執行

只有moov在檔案尾端的mp4,才會處理
只有寫入幾百KB的資料,很快

Win10 測試OK
  1. param($mp4 = $null, $jpg = $null )
  2. function ReadBox( $fs , $start , $end ,$search = $null){
  3.     $h = @{}
  4.     $fs.position = $start
  5.    
  6.     while($fs.position -lt $end){
  7.         $boxSizePos = $fs.position ; $boxSizeLength = 4
  8.         if (($fs.position + 8) -gt $end){ break }
  9.    
  10.         [void]$fs.read($buffer , 0 , 8)
  11.         $boxSize = [System.BitConverter]::ToUInt32( $buffer[3..0] , 0)
  12.         $boxType = ( $buffer[4..7] | %{[char]$_} ) -join ''
  13.         $boxEnd = $fs.position + $boxSize - 8
  14.    
  15.         if ( $boxSize -eq 1 ){ #largesize
  16.             if (($fs.position + 8) -gt $end){ break }
  17.             
  18.             [void]$fs.read($buffer , 0 , 8)
  19.             $boxSize = [System.BitConverter]::ToUInt64( $buffer[7..0] , 0)
  20.             $boxEnd = $fs.position + $boxSize - 16
  21.             $boxSizePos += 8
  22.             $boxSizeLength = 8
  23.         }elseif ($boxSize -eq 0 ) { #box extends to end of file
  24.             $boxEnd = $end
  25.         }
  26.         if ($boxend -gt $end ){ break }
  27.         if ($boxType -eq $search){
  28.             return @{boxSizePos = $boxSizePos ; boxSizeLength = $boxSizeLength
  29.                 boxSize = $boxSize ; boxEnd = $boxEnd  }
  30.         }
  31.         
  32.         $h[$boxType] = @{boxSizePos = $boxSizePos ; boxSizeLength = $boxSizeLength
  33.                         boxSize = $boxSize ; boxEnd = $boxEnd  }
  34.         
  35.         $fs.position = $boxEnd
  36.     }#end of while
  37.     if ($search -ne $null){
  38.         return $null
  39.     }
  40.     return $h
  41. }
  42. function addCover([io.fileinfo]$mp4 , [io.fileinfo]$jpg){
  43.     trap{ $_ ;$fs.close() ;  return }
  44.     if ($jpg.length -gt $bufferSize){echo 'jpg size is too big';pause; return }
  45.     $fs = $mp4.Open('open' , 'readWrite' , 'read')
  46.     $h = ReadBox $fs  0  $fs.length
  47.     if ( $h.ftyp -eq $null -or $h.ftyp.boxSizePos -ne 0 -or
  48.         $h.moov -eq $null -or $h.mdat -eq $null){
  49.         throw "$mp4 :This is not a MP4 file."
  50.     }
  51.     if ($h.moov.boxSizePos -lt $h.mdat.boxSizePos){
  52.         throw "$mp4 : only  meta info in the end of file will be processed"
  53.     }
  54.     $box = @()
  55.     $box += $h.moov
  56.     #============================================================
  57.     $insertPos = $backupPos = $null
  58.                         #udta     meta   hdlr
  59.     [uint32]$TotalSize =   8   +   12  +  33  +  
  60.                         #ilst     covr   data
  61.                            8   +   8   +   16  + $jpg.length
  62.     $boxExist = $true
  63.     [byte[]] $addBox = $null
  64.     $addBox = 'udta','meta','ilst','covr','data'| %{
  65.         $boxHeadSize = 8
  66.         if ( $_ -eq 'ilst' ) {$boxHeadSize += 4} # search in meta fullbox
  67.         if ($boxExist ){
  68.             $box += ReadBox $fs  ($box[-1].boxSizePos + $boxHeadSize) $box[-1].boxEnd  $_
  69.         }
  70.         if ($box[-1] -eq $null ){ #box not found
  71.             $boxExist = $false
  72.             $insertPos = $backupPos = $box[-2].boxEnd
  73.             $arrByte = [System.BitConverter]::GetBytes( $TotalSize )
  74.             $arrByte[3..0] #boxSize
  75.             $_[0..3] #boxType
  76.    
  77.             if ($_ -eq 'meta') {
  78.                 ,0 * 4     #meta  fullbox
  79.                 (,0 * 3 ) + 33  #hdlr size
  80.                 'hdlr'[0..3]
  81.                 ,0 * 8
  82.                 'mdirappl'[0..7]
  83.                 ,0 * 9
  84.             }
  85.             if ($_ -eq 'data'){
  86.                 (,0 * 3 ) + 13
  87.                 ,0 * 4
  88.             }
  89.         }#end of if
  90.         $TotalSize -= 8
  91.         if ($_ -eq 'meta') { $TotalSize -= ( 4 + 33) } #fullbox and hdlr
  92.     }#end of foreach cmdlet
  93.     #===============================================
  94.     if ($insertPos -eq $null){ # all box exist , replace jpg only
  95.         $insertPos = $box[-1].boxSizePos + 16
  96.         $backupPos = $box[-1].boxEnd
  97.         $editSize = $jpg.length -  ($backupPos - $insertPos)
  98.         $addSize = $jpg.length
  99.     }else{#some box not exist
  100.         $editSize = $addSize = $addBox.length  + $jpg.length
  101.     }
  102.     if ( ($insertPos + $addSize) -ne $backupPos){
  103.         #backup
  104.         $backupSize = $fs.length - $backupPos
  105.         if ( $backupSize  -gt $bufferSize ){
  106.             throw "bufferSize $($bufferSize/1mb)mb is too small"
  107.         }
  108.    
  109.         $fs.position = $backupPos
  110.         [void]$fs.read($buffer , 0 , $backupSize )
  111.    
  112.         #write backup
  113.         $fs.position = $insertPos + $addSize
  114.         $fs.write($buffer , 0 , $backupSize )
  115.     }
  116.     #read jpg
  117.     $fs1 = $jpg.OpenRead()
  118.     [void]$fs1.read($buffer , 0 , $jpg.length)
  119.     $fs1.close()
  120.     #write  addbox  jpg
  121.     $fs.position = $insertPos
  122.     if ($addbox -ne $null ){  $fs.write($addBox , 0 , $addBox.length) }
  123.     $fs.write($buffer , 0 , $jpg.length)
  124.     #edit boxSize
  125.     foreach ($a in $box) {
  126.         if ($a -eq $null ){ break }
  127.         if ( $a.boxSizeLength -eq 4 ){
  128.             [uint32]$boxSize = $a.boxSize + $editSize
  129.             $arrByte = [System.BitConverter]::GetBytes($boxSize)
  130.             $arrByte = $arrByte[3..0]
  131.         }else{
  132.             [uint64]$boxSize = $a.boxSize + $editSize
  133.             $arrByte = [System.BitConverter]::GetBytes($boxSize)
  134.             $arrByte = $arrByte[7..0]
  135.         }
  136.    
  137.         $fs.position = $a.boxSizePos
  138.         $fs.write($arrByte , 0 , $a.boxSizeLength)        
  139.     }
  140.     $fs.close()
  141. }#end of function
  142. #============================================================
  143. $bufferSize = 20mb
  144. $buffer = new-object byte[]($bufferSize)
  145. cd -literal $PSScriptRoot
  146. [Environment]::CurrentDirectory = pwd
  147. if ($mp4 -eq $null -or $jpg -eq $null ){
  148.     dir *.mp4 -file |%{
  149.         if (Test-Path  -literal "$($_.BaseName).jpg" ){
  150.             addCover $_  "$($_.BaseName).jpg"
  151.         }
  152.     }
  153. }else{
  154.     addCover $mp4 $jpg
  155. }
复制代码

作者: 狄钦dQ    时间: 2018-7-2 11:29

支持楼主原创!谢谢分享!
作者: nwm310    时间: 2018-7-15 10:14

本帖最后由 nwm310 于 2018-7-15 15:04 编辑

之前的版本,檔尾可能會殘留多餘資料,現在已修正

新增:
支持 PS 2.0

如果有相同主檔名的txt,可以加入章節
按熱鍵可以快速跳到那個時間
VLC media player:Shift + P 、  Shift + N
PotPlayer:Shift + Page Up 、   Shift + Page Down
MPC-HC:Page Up  、  Page Down
並不是每個播放軟體都可顯示章節


txt編碼格式可以是:ANSI、Unicode、UTF-8(需要有BOM)

txt內容
章節開始時間   章節名稱(可有可無)
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個章節? 不確定


當 jpg 或 txt 檔案大小為 0 byte時,可以清除縮圖 或 章節資料
  1. function ReadBox( $fs , $start , $end ,$search = $null ){
  2.     $h = @{}
  3.     $fs.position = $start
  4.     if ( $search -eq $null ){
  5.         $searchBox = $null
  6.     }else{
  7.         $searchBox = @($search)[0]
  8.         $L = @($search).length
  9.         if( $L -gt 1 ){
  10.             $rest = $search[ 1..$L ]
  11.         }
  12.     }
  13.     while($fs.position -lt $end){
  14.         $boxSizePos = $fs.position ; $boxSizeLength = 4
  15.         if (($fs.position + 8) -gt $end){ break }
  16.         [void]$fs.read($buffer , 0 , 8)
  17.         $boxSize = [System.BitConverter]::ToUInt32( $buffer[3..0] , 0)
  18.         $boxType = ( $buffer[4..7] | %{[char]$_} ) -join ''
  19.         $boxEnd = $fs.position + $boxSize - 8
  20.         if ( $boxSize -eq 1 ){ #largesize
  21.             if (($fs.position + 8) -gt $end){ break }
  22.             [void]$fs.read($buffer , 0 , 8)
  23.             $boxSize = [System.BitConverter]::ToUInt64( $buffer[7..0] , 0)
  24.             $boxEnd = $fs.position + $boxSize - 16
  25.             $boxSizePos += 8
  26.             $boxSizeLength = 8
  27.         }elseif ($boxSize -eq 0 ) { #box extends to end of file
  28.             $boxEnd = $fs.length
  29.         }
  30.         if ($boxend -gt $end ){ break }
  31.         if ($boxType -eq $searchBox){
  32.             $h1 = @{boxSizePos = $boxSizePos ; boxSizeLength = $boxSizeLength
  33.                 boxSize = $boxSize ; boxEnd = $boxEnd  }
  34.             if ($L -eq 1 ){
  35.                 return $h1
  36.             }else{
  37.                 $boxHeadSize = 8
  38.                 if ($boxType -eq 'meta') {$boxHeadSize +=4}
  39.                 return @($h1) + (ReadBox $fs ($boxSizePos + $boxHeadSize ) $boxEnd $rest )
  40.             }
  41.         }
  42.         $h[$boxType] = @{boxSizePos = $boxSizePos ; boxSizeLength = $boxSizeLength
  43.                         boxSize = $boxSize ; boxEnd = $boxEnd  }
  44.         $fs.position = $boxEnd
  45.     }#end of while
  46.     if ($search -eq $null){ return $h }else{ return $null }
  47. }
  48. function addSomeThing([io.fileinfo]$mp4 , [io.fileinfo]$jpg , [io.fileinfo]$txt = $null){
  49.     trap{ $_ ;read-host;$fs.close() ;  return }
  50.     $fs = $mp4.Open('open' , 'readWrite' , 'read')
  51.     $h = ReadBox $fs  0  $fs.length
  52.     if ( $h.ftyp -eq $null -or $h.ftyp.boxSizePos -ne 0 -or
  53.         $h.moov -eq $null -or $h.mdat -eq $null){
  54.         throw "$mp4 :This is not a MP4 file."
  55.     }
  56.     if ($h.moov.boxSizePos -lt $h.mdat.boxSizePos){
  57.         throw "$mp4 : only  meta info in the end of file will be processed"
  58.     }
  59.     $box = @()
  60.     $box += $h.moov
  61.     #============================================================
  62.     $fileEmpty = $false
  63.     if ($jpg -ne $null){ #add Cover
  64.         if ($jpg.length -gt $bufferSize){echo 'jpg size is too big';read-host; $fs.close();return }
  65.         if ($jpg.length -eq 0 ) { $fileEmpty = $true}
  66.         $arrSearch = 'udta\meta\ilst\covr' -split '\\'
  67.     }elseif ($txt -ne $null){ #add chapter
  68.         if ($txt.length -eq 0 ){ $fileEmpty = $true }
  69.         $arrSearch = 'udta\chpl' -split '\\'
  70.     }else{ $fs.close() ;return }#do nothing
  71.     #===================================================
  72.     $box += ReadBox $fs  ($box[-1].boxSizePos + 8) $box[-1].boxEnd  $arrSearch
  73.     if ( $box[-1] -eq $null ) { # some box not found
  74.         if ( $fileEmpty ) { $fs.close() ;return }
  75.         $insertPos = $backupPos = $box[-2].boxEnd
  76.     }else{ # all box found , still reCreate or clear last box
  77.         $insertPos = $box[-1].boxSizePos
  78.         $backupPos = $box[-1].boxEnd
  79.     }
  80.     #==============create last box and box which is no exist================
  81.     if ( ! $fileEmpty ){
  82.         if ($jpg -ne $null ){ # For Cover
  83.                         #udta      (meta + hdlr)
  84.             $TotalSize =   8   +   (12  +  33)  +  
  85.                         #ilst      (covr + data + jpg )
  86.                             8   +   ( 8   +   16  + $jpg.length )
  87.             [uint32[]]$arrBoxsize =  0 , 8 , (12 + 33) , 8  |%{
  88.                         $TotalSize -= $_ ; $TotalSize }
  89.             $dataSize = $jpg.length
  90.         }else{ #==========read chapter in mp4======================
  91.             $chapterInfo = @{}
  92.             if ( $insertPos -ne $backupPos ){ #all box exist
  93.                 if ( ($backupPos - $insertPos) -gt 17 ) {
  94.                     $fs.Position = $insertPos + 17
  95.                     while($fs.Position -lt $backupPos){
  96.                         if ( ($fs.Position + 8) -gt $backupPos ){ break }
  97.                         [void]$fs.read($buffer , 0 , 8)
  98.                         $ticks = [System.BitConverter]::ToInt64( $buffer[7..0] , 0)
  99.                         if ( ($fs.Position + 1) -gt $backupPos ){ break }
  100.                         [byte]$n = $fs.ReadByte()
  101.                         if ( ($fs.Position + $n) -gt $backupPos ){ break }
  102.                         [void]$fs.read($buffer , 0 , $n)
  103.                         if ($n -gt 0){
  104.                             $chapterInfo[$ticks] = @([byte]$n) + $buffer[0..($n-1)]
  105.                         }else{
  106.                             $chapterInfo[$ticks] = @([byte]$n)
  107.                         }
  108.                     }#end of while
  109.                 }#end of if
  110.             }#end of if all box exist
  111.             #===============read from txt========================
  112.             gc -literal $txt |%{
  113.                 $a , $b = $_.split('',2,1)
  114.                 if ($t = $a -as [timespan] ){ #check time format
  115.                     if ( $t.ticks -lt 0 ){#remove
  116.                         $chapterInfo.Remove( -($t.ticks)  )
  117.                     }else{#add
  118.                         $n = [System.Text.Encoding]::UTF8.GetByteCount(@($b))
  119.                         if ( $n -lt 256  ){
  120.                             $chapterInfo[$t.ticks] = @([byte]$n) + [System.Text.Encoding]::UTF8.GetBytes(@($b))
  121.                         }
  122.                     }
  123.                 }
  124.             }#end of gc
  125.             #==============create byte data=====================
  126.             $n = 0
  127.             $chapter = $chapterInfo.Keys | sort | %{
  128.                 $n++
  129.                 $arrByte = [System.BitConverter]::GetBytes( $_ )
  130.                 $arrByte[7..0]
  131.                 $chapterInfo[$_]
  132.             }
  133.             if ( $n -gt 255 ) { $n = 255}
  134.             $chapter = @([byte]$n) + $chapter
  135.             $dataSize = $chapter.length
  136.             #=========================================================
  137.                         #udta    chpl
  138.             $TotalSize =   8 +   16 +  $chapter.length
  139.             [uint32[]]$arrBoxsize =  0 , 8  |%{ $TotalSize -= $_ ; $TotalSize}
  140.         }#end of if  For chapter
  141.         [byte[]]$addBox =
  142.         for ($i = ($box.length -1) -1 ; $i -lt $arrSearch.length ; $i++){
  143.             $arrByte = [System.BitConverter]::GetBytes( $arrBoxsize[$i] )
  144.             $arrByte[3..0] #boxSize
  145.             $arrSearch[$i][0..3] #boxType
  146.             switch ( $arrSearch[$i] ){
  147.                 'meta'{
  148.                     ,0 * 4   #meta  fullbox
  149.                     (,0 * 3 ) + 33 ;  'hdlr'[0..3] #hdlr
  150.                     ,0 * 8 ; 'mdirappl'[0..7]  ; ,0 * 9
  151.                     break
  152.                 }
  153.                 'covr'{
  154.                     $arrByte = [System.BitConverter]::GetBytes( $arrBoxsize[$i] - 8 )
  155.                     $arrByte[3..0]  ;  'data'[0..3]
  156.                     (,0 * 3 ) + 13  ;  ,0 * 4
  157.                     break
  158.                 }
  159.                 'chpl'{
  160.                     1 ; ,0 * 7
  161.                     break
  162.                 }
  163.             }#end of switch
  164.         }#end of for
  165.     }#end of if  ! $fileEmpty
  166.     #=====================================================================
  167.     if ($fileEmpty){ #clear content
  168.         $addSize = 0
  169.     }else{ #add or replace content
  170.         $addSize =  $addBox.length  + $dataSize
  171.     }
  172.     $editSize =  $addSize -  ($backupPos - $insertPos)
  173.     #==============backup & restore=====================
  174.     if ( ($insertPos + $addSize) -ne $backupPos){
  175.         #backup
  176.         $backupSize = $fs.length - $backupPos
  177.         
  178.         if ( $backupSize  -gt $bufferSize ){
  179.             throw "bufferSize $($bufferSize/1mb)mb is too small"
  180.         }
  181.    
  182.         $fs.position = $backupPos
  183.         [void]$fs.read($buffer , 0 , $backupSize )
  184.    
  185.         #restore
  186.         $fs.position = $insertPos + $addSize
  187.         $fs.write($buffer , 0 , $backupSize )
  188.     }
  189.     #==============write  $addbox、jpg or chapter==============
  190.     if ( ! $fileEmpty){ # jpg or txt  is not empty
  191.         $fs.position = $insertPos
  192.         $fs.write($addBox , 0 , $addBox.length)
  193.    
  194.         if ($jpg -ne $null){ #write jpg
  195.             $fs1 = $jpg.OpenRead()
  196.             [void]$fs1.read($buffer , 0 , $jpg.length)
  197.             $fs1.close()
  198.             $fs.write($buffer , 0 , $jpg.length)
  199.         }else{ #write chapter
  200.             $fs.write($chapter , 0 , $chapter.length)
  201.         }   
  202.     }
  203.     #================edit boxSize===============================
  204.     for ($i = 0 ; $i -lt ($box.length - 1) ; $i++) {
  205.         if ( $box[$i].boxSizeLength -eq 4 ){
  206.             [uint32]$boxSize = $box[$i].boxSize + $editSize
  207.             $arrByte = [System.BitConverter]::GetBytes($boxSize)
  208.             $arrByte = $arrByte[3..0]
  209.         }else{
  210.             [uint64]$boxSize = $box[$i].boxSize + $editSize
  211.             $arrByte = [System.BitConverter]::GetBytes($boxSize)
  212.             $arrByte = $arrByte[7..0]
  213.         }
  214.         $fs.position = $box[$i].boxSizePos
  215.         $fs.write($arrByte , 0 , $box[$i].boxSizeLength)
  216.     }
  217.     if ($editSize -lt 0){
  218.         $fs.SetLength($fs.length + $editSize)
  219.     }
  220.     $fs.close()
  221. }#end of function
  222. #============================================================
  223. $bufferSize = 20mb
  224. $buffer = new-object byte[]($bufferSize)
  225. cd -Literal (Split-Path  $MyInvocation.MyCommand.Path)
  226. #cd -literal $PSScriptRoot
  227. [Environment]::CurrentDirectory = pwd
  228. #dir *.mp4 -file |%{
  229. dir *.mp4 |?{! $_.PSIsContainer}|%{
  230.     if (Test-Path  -literal "$($_.BaseName).jpg" -PathType Leaf){
  231.         addSomeThing $_  "$($_.BaseName).jpg"
  232.     }
  233.     if (Test-Path  -literal "$($_.BaseName).txt" -PathType Leaf){
  234.         addSomeThing $_  $null  "$($_.BaseName).txt"
  235.     }
  236. }#end of foreach
复制代码





欢迎光临 批处理之家 (http://bbs.bathome.net/) Powered by Discuz! 7.2