本帖最后由 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時,可以清除縮圖 或 章節資料- 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
复制代码
|