-
-
Save lizkes/0cd0b313dca2c4596199e82a29f49ed4 to your computer and use it in GitHub Desktop.
#!/usr/bin/env pwsh | |
$InputPath = $args[0] | |
# https://stackoverflow.com/questions/8761888/capturing-standard-out-and-error-with-start-process | |
function Start-Command ([String]$Path, [String]$Arguments) { | |
$pinfo = New-Object System.Diagnostics.ProcessStartInfo | |
$pinfo.FileName = $Path | |
$pinfo.RedirectStandardError = $true | |
$pinfo.RedirectStandardOutput = $true | |
$pinfo.UseShellExecute = $false | |
$pinfo.Arguments = $Arguments | |
$p = New-Object System.Diagnostics.Process | |
$p.StartInfo = $pinfo | |
$p.Start() | Out-Null | |
$p.WaitForExit() | |
return @{ | |
stdout = $p.StandardOutput.ReadToEnd() | |
stderr = $p.StandardError.ReadToEnd() | |
ExitCode = $p.ExitCode | |
} | |
} | |
# https://stackoverflow.com/questions/1783554/fast-and-simple-binary-concatenate-files-in-powershell | |
function Join-File ([String[]]$Path, [String]$Destination) { | |
$OutFile = [IO.File]::Create($Destination) | |
foreach ($File in $Path) { | |
$InFile = [IO.File]::OpenRead($File) | |
$InFile.CopyTo($OutFile) | |
$InFile.Dispose() | |
} | |
$OutFile.Dispose() | |
} | |
function Get-RandomBytes ([Int32]$Length) { | |
$RNG = [Security.Cryptography.RandomNumberGenerator]::Create() | |
$Bytes = [Byte[]]::new($Length) | |
$RNG.GetBytes($Bytes) | |
return $Bytes | |
} | |
function Invoke-EncryptFile ([String]$Path, [Byte[]]$Key, [Byte[]]$IV, [String]$OutFile) { | |
$File = [IO.File]::ReadAllBytes($Path) | |
$AES = [Security.Cryptography.Aes]::Create() | |
$AES.Key = $Key | |
$AES.IV = $IV | |
$Encryptor = $AES.CreateEncryptor($AES.Key, $AES.IV) | |
$Encrypted = $Encryptor.TransformFinalBlock($File, 0, $File.Length) | |
[IO.File]::WriteAllBytes($OutFile, $Encrypted) | |
} | |
$CurlExec = 'E:\Binary\curl.exe' | |
$FFmpegExec = 'E:\Binary\ffmpeg.exe' | |
# $FFmpegExec = 'ffmpeg' | |
# $CurlExec = 'curl' | |
if (Test-Path alias:\curl) { | |
Remove-Item alias:\curl | |
} | |
$Videos = @() | |
$Output = @() | |
if (Test-Path -Path $InputPath -PathType Leaf) { | |
$Videos += $InputPath | |
} | |
else { | |
foreach ($Video in (Get-ChildItem $InputPath -Filter *.mp4)) { | |
$Videos += $Video.FullName | |
} | |
} | |
foreach ($Video in $Videos) { | |
$TempDirectory = $PSScriptRoot + '/' + [GUID]::NewGuid().ToString('N').substring(0, 6) | |
New-Item -Path $TempDirectory -ItemType Directory | Out-Null | |
# 按每秒一个ts文件的标准切片 | |
Write-Host '[SPLIT]' -NoNewline -BackgroundColor DarkGray -ForegroundColor White | |
Write-Host ' Spliting video file...' | |
&$FFmpegExec -loglevel error -hide_banner -i $Video -c copy -hls_list_size 0 -hls_allow_cache 1 -hls_time 1 -hls_flags split_by_time -hls_segment_filename ($TempDirectory + '/%d.ts') ($TempDirectory + '/video.m3u8') | |
$tsCount = (Get-ChildItem $TempDirectory -Filter *.ts).Length | |
do { | |
$LimitExceed = $false | |
foreach ($ts in (Get-ChildItem $TempDirectory -Filter *.ts)) { | |
if ($ts.Length -gt 5MB) { | |
$LimitExceed = $true | |
Write-Host '[ERROR]' -NoNewline -BackgroundColor DarkRed -ForegroundColor White | |
Write-Host (' File size limit exceeded: {0} ({1} MB)' -f $ts.Name, [Math]::Round($ts.Length / 1MB, 2)) | |
} | |
} | |
if ($LimitExceed) { | |
Read-Host 'Compress the files and press enter to continue' | |
} | |
} while ($LimitExceed) | |
Write-Host '[PARSE]' -NoNewline -BackgroundColor DarkGray -ForegroundColor White | |
Write-Host ' Parsing m3u8 file...' | |
$m3u8Source = [IO.File]::ReadAllLines($TempDirectory + '/video.m3u8') | |
$m3u8 = @{ | |
'meta' = @(); | |
'info' = @(); | |
} | |
for ($i = 0; $i -lt $m3u8Source.Count; $i++) { | |
if (($m3u8Source[$i] -eq '#EXTM3U') -or ($m3u8Source[$i] -eq '#EXT-X-ENDLIST')) { | |
continue | |
} | |
if ($m3u8Source[$i].StartsWith('#EXTINF:')) { | |
$m3u8.info += @{ | |
'duration' = [Double]($m3u8Source[$i].Replace('#EXTINF:', '').Replace(',', '')); | |
'file' = $m3u8Source[++$i]; | |
} | |
} | |
else { | |
$m3u8.meta += $m3u8Source[$i] | |
} | |
} | |
Write-Host '[MERGE]' -NoNewline -BackgroundColor DarkGray -ForegroundColor White | |
Write-Host ' Merging TS files...' | |
$FastStart = 3 | |
$tsCount = (Get-ChildItem $TempDirectory -Filter *.ts).Length | |
$LastCount = 0 | |
for ($i = 0; $i -lt ($tsCount - 1); $i++) { | |
$CurrentPath = ('{0}/{1}.ts' -f $TempDirectory, $i) | |
$NextPath = ('{0}/{1}.ts' -f $TempDirectory, ($i + 1)) | |
if ((Get-Item $CurrentPath).Length + (Get-Item $NextPath).Length -le @(4MB, 3MB, 2MB, 1MB)[$FastStart]) { | |
Join-File -Path $CurrentPath, $NextPath -Destination ('{0}/~.ts' -f $TempDirectory) | |
Remove-Item $NextPath | |
Remove-Item $CurrentPath | |
Rename-Item ('{0}/~.ts' -f $TempDirectory) ('{0}.ts' -f ($i + 1)) | |
} | |
else { | |
Write-Host '[MERGE]' -NoNewline -BackgroundColor DarkCyan -ForegroundColor White | |
if ($FastStart -gt 0) { | |
$FastStart-- | |
Write-Host (' {0}-{1} -> {2}.ts ({3} MB FastStart)' -f $LastCount, ($i - 1), $i, [Math]::Round((Get-Item $CurrentPath).Length / 1MB, 2)) | |
} | |
else { | |
Write-Host (' {0}-{1} -> {2}.ts ({3} MB)' -f $LastCount, ($i - 1), $i, [Math]::Round((Get-Item $CurrentPath).Length / 1MB, 2)) | |
} | |
$LastCount = $i + 1 | |
} | |
if ($i -eq ($tsCount - 2)) { | |
Write-Host '[MERGE]' -NoNewline -BackgroundColor DarkCyan -ForegroundColor White | |
Write-Host (' {0}-{1} -> {2}.ts ({3} MB)' -f $LastCount, $i, ($i + 1), [Math]::Round((Get-Item $NextPath).Length / 1MB, 2)) | |
} | |
} | |
$MergedInfo = @() | |
$tsLast = 0 | |
for ($i = 0; $i -lt $tsCount; $i++) { | |
if (-not [IO.File]::Exists(('{0}/{1}.ts' -f $TempDirectory, $i))) { | |
continue | |
} | |
$MergedDuration = 0 | |
for ($j = $tsLast; $j -le $i; $j++) { | |
$MergedDuration += $m3u8.info[$j].duration | |
} | |
$MergedInfo += @{ | |
'duration' = $MergedDuration; | |
'file' = $m3u8.info[$i].file | |
} | |
$tsLast = $i + 1 | |
} | |
$m3u8.info = $MergedInfo | |
$m3u8Content = @('#EXTM3U') | |
foreach ($meta in $m3u8.meta) { | |
$m3u8Content += $meta | |
} | |
foreach ($info in $m3u8.info) { | |
$m3u8Content += '#EXTINF:' + $info.duration + ',' | |
$m3u8Content += $info.file | |
} | |
$m3u8Content += '#EXT-X-ENDLIST' | |
[IO.File]::WriteAllLines($TempDirectory + '/video.m3u8', $m3u8Content) | |
# Read-Host 'Press enter to start uploading' | |
Write-Host '[UPLOAD]' -NoNewline -BackgroundColor DarkGray -ForegroundColor White | |
Write-Host (' Uploading files...') | |
$EncryptKey = Get-RandomBytes 16 | |
$EncryptIV = Get-RandomBytes 16 | |
[IO.File]::WriteAllBytes($TempDirectory + '/KEY', $EncryptKey) | |
$Response = (Start-Command $CurlExec (@( | |
'https://kfupload.alibaba.com/mupload', | |
'-H "User-Agent: iAliexpress/8.27.0 (iPhone; iOS 12.1.2; Scale/2.00)"', | |
'-X POST', | |
'-F scene=productImageRule', | |
'-F name=image.jpg', | |
'-F file=@{0}' -f ($TempDirectory + '/KEY') | |
) -join ' ')).stdout | ConvertFrom-Json | |
$EncryptKeyURL = $Response.url | |
Remove-Item ($TempDirectory + '/KEY') | |
$m3u8.meta += ('#EXT-X-KEY:METHOD=AES-128,URI="{0}",IV=0x{1}' -f $EncryptKeyURL, (($EncryptIV | ForEach-Object { '{0:x2}' -f $_ }) -join '')) | |
$URLMapping = @{} | |
$FailUpload = $false | |
foreach ($ts in (Get-ChildItem $TempDirectory -Filter *.ts)) { | |
$FileName = [IO.Path]::GetFileName($ts.FullName) | |
$FileEncrypted = $ts.FullName + '.encrypt' | |
Invoke-EncryptFile $ts.FullName $EncryptKey $EncryptIV $FileEncrypted | |
$Response = (Start-Command $CurlExec (@( | |
'https://kfupload.alibaba.com/mupload', | |
'-H "User-Agent: iAliexpress/8.27.0 (iPhone; iOS 12.1.2; Scale/2.00)"', | |
'-X POST', | |
'-F scene=productImageRule', | |
'-F name=image.jpg', | |
'-F file=@{0}' -f $FileEncrypted | |
) -join ' ')).stdout | ConvertFrom-Json | |
Remove-Item $FileEncrypted | |
if ([Bool][Int32]$Response.code) { | |
Write-Host '[ERROR]' -NoNewline -BackgroundColor DarkRed -ForegroundColor White | |
Write-Host (' {0} Failed to upload' -f $FileName) | |
$FailUpload = $true | |
break | |
} | |
else { | |
Write-Host '[UPLOAD]' -NoNewline -BackgroundColor DarkCyan -ForegroundColor White | |
Write-Host (' {0} -> {1}' -f $FileName, $Response.url) | |
$URL = $Response.url | |
} | |
$URLMapping.$FileName = $URL | |
} | |
if ($FailUpload) { | |
continue | |
} | |
$m3u8Content = @('#EXTM3U') | |
foreach ($meta in $m3u8.meta) { | |
$m3u8Content += $meta | |
} | |
foreach ($info in $m3u8.info) { | |
$m3u8Content += '#EXTINF:' + $info.duration + ',' | |
$m3u8Content += $URLMapping.($info.file) | |
} | |
$m3u8Content += '#EXT-X-ENDLIST' | |
[IO.File]::WriteAllLines($TempDirectory + '/video_online.m3u8', $m3u8Content) | |
$Response = (Start-Command $CurlExec (@( | |
'https://kfupload.alibaba.com/mupload', | |
'-H "User-Agent: iAliexpress/8.27.0 (iPhone; iOS 12.1.2; Scale/2.00)"', | |
'-X POST', | |
'-F scene=productImageRule', | |
'-F name=image.jpg', | |
'-F file=@{0}' -f ($TempDirectory + '/video_online.m3u8') | |
) -join ' ')).stdout | ConvertFrom-Json | |
if ([Bool][Int32]$Response.code) { | |
Write-Host '[Error]' -NoNewline -BackgroundColor DarkRed -ForegroundColor White | |
Write-Host (' {0} Failed to upload' -f $Video) | |
$Output += @{ | |
'file' = $Video; | |
'url' = ''; | |
} | |
} | |
else { | |
Write-Host '[UPLOAD]' -NoNewline -BackgroundColor DarkGreen -ForegroundColor White | |
Write-Host (' {0} -> {1}' -f $Video, $Response.url) | |
$Output += @{ | |
'file' = $Video; | |
'url' = $Response.url; | |
} | |
} | |
# Read-Host 'Press enter to remove temp directory' | |
Write-Host '[CLEAN]' -NoNewline -BackgroundColor DarkGray -ForegroundColor White | |
Write-Host ' Temp directory get deleted' | |
Remove-Item -Force -Recurse -Path $TempDirectory | |
} | |
$Output | ConvertTo-Json -depth 100 | Out-File ($PSScriptRoot + '/output.json') | |
Write-Host '' | |
Write-Host '[DONE]' -NoNewline -BackgroundColor DarkMagenta -ForegroundColor White | |
Write-Host ' All urls have been written to output.json' |
@ludashi2020 已经测试过是可以正常上传的
老哥,你的脚本上传文件失败了。`Key: BF CC 40 E6 95 7F 3C B3 7C 15 66 03 40 45 B9 1E
IV: D8 D8 6F 19 F0 E6 BC A9 4F 23 B5 DC 21 57 38 F5
Uploading Key File...
[WARNING] 1.ts Failed to upload
[WARNING] 10.ts Failed to upload
[WARNING] 5.ts Failed to upload
Writing M3U8 with URL...'
作者说是ua需要修改,请教一下,改成多少?
搞定了,谢谢分享脚本
上传报错了,是不是接口挂了?
`ConvertFrom-Json: /home/2.ps1:179
Line |
179 | ) -join ' ')).stdout | ConvertFrom-Json
| ~~~~~~~~~~~~~~~~
| Conversion from JSON failed with error: Unexpected character encountered while parsing value: <. Path '', line 0, position 0.
ConvertFrom-Json: /home/2.ps1:198
Line |
198 | ) -join ' ')).stdout | ConvertFrom-Json
| ~~~~~~~~~~~~~~~~
| Conversion from JSON failed with error: Unexpected character encountered while parsing value: <. Path '', line 0, position 0.
[UPLOAD] 0.ts
ConvertFrom-Json: /home/2.ps1:198
Line |
198 | ) -join ' ')).stdout | ConvertFrom-Json
| ~~~~~~~~~~~~~~~~
| Conversion from JSON failed with error: Unexpected character encountered while parsing value: <. Path '', line 0, position 0.
[UPLOAD] 1.ts
ConvertFrom-Json: /home/2.ps1:198
Line |
198 | ) -join ' ')).stdout | ConvertFrom-Json
| ~~~~~~~~~~~~~~~~
| Conversion from JSON failed with error: Unexpected character encountered while parsing value: <. Path '', line 0, position 0.
[UPLOAD] 14.ts
ConvertFrom-Json: /home/2.ps1:198
Line |
198 | ) -join ' ')).stdout | ConvertFrom-Json
| ~~~~~~~~~~~~~~~~
| Conversion from JSON failed with error: Unexpected character encountered while parsing value: <. Path '', line 0, position 0.
[UPLOAD] 19.ts
ConvertFrom-Json: /home/2.ps1:198
Line |
198 | ) -join ' ')).stdout | ConvertFrom-Json
`
你这个能正常上传吗?我试过,转码后上传不了。