-
Notifications
You must be signed in to change notification settings - Fork 271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixes #848 add CLI mode to manually concatenate ts parts #888
Merged
Merged
Changes from 50 commits
Commits
Show all changes
55 commits
Select commit
Hold shift + click to select a range
c603097
#tsmerge Update README.md
superbonaci baf2271
#tsmerge Update Program.cs
superbonaci 9fc7c74
#tsmerge Add MergeTS.cs
superbonaci fefe961
#tsmerge Add TsMergeArgs.cs
superbonaci 0a00e81
#tsmerge Add TsMerger.cs
superbonaci 550c265
#tsmerge Add TsMergeOptions.cs
superbonaci 0946fcc
Update Program.cs
superbonaci 638d5b1
#tsmerge Update README.md
superbonaci 36cc340
Program.cs Remove newline at the end
superbonaci 8fb6a4d
Delete TwitchDownloaderCLI/Program.cs
superbonaci 68d686b
Try to upload again Program.cs
superbonaci b1d48b1
Merge pull request #1 from lay295/master
superbonaci a69c641
Merge pull request #2 from lay295/master
superbonaci 37a2e14
Merge pull request #3 from lay295/master
superbonaci 32e6b0d
Delete TwitchDownloaderCLI/README.md
superbonaci a192207
Add files via upload
superbonaci 7a41051
Delete TwitchDownloaderCLI/Program.cs
superbonaci b71b6b8
Add files via upload
superbonaci b9bfa63
Delete TwitchDownloaderCore/TsMerger.cs
superbonaci 84172c6
Add files via upload
superbonaci 286f71c
Delete TwitchDownloaderCLI/Modes/Arguments/TsMergeArgs.cs
superbonaci e155335
Add files via upload
superbonaci 8288281
Delete TwitchDownloaderCLI/Modes/Arguments/TsMergeArgs.cs
superbonaci f8fbf31
Add files via upload
superbonaci ee02787
Delete TwitchDownloaderCore/TsMerger.cs
superbonaci 393e3c0
Add files via upload
superbonaci 00a7d4d
Delete TwitchDownloaderCLI/Modes/Arguments/TsMergeArgs.cs
superbonaci 81ecdbe
Add files via upload
superbonaci abe5b40
Delete TwitchDownloaderCore/TsMergeArgs.cs
superbonaci 4c72064
Add files via upload
superbonaci 60b3491
Delete TwitchDownloaderCLI/README.md
superbonaci 263dff8
Add files via upload
superbonaci b1d95af
Merge pull request #4 from lay295/master
superbonaci b3f7bea
Merge pull request #5 from lay295/master
superbonaci 92dcb03
Merge pull request #6 from lay295/master
superbonaci 47022d7
Merge pull request #7 from lay295/master
superbonaci 4d9f801
Merge pull request #8 from lay295/master
superbonaci 10fd997
Merge pull request #9 from lay295/master
superbonaci 2dc7026
Merge pull request #10 from lay295/master
superbonaci b7fe2a7
Merge pull request #11 from lay295/master
superbonaci 4aa7309
Merge pull request #12 from lay295/master
superbonaci 2e1a47d
Merge pull request #13 from lay295/master
superbonaci 3dbebb9
Merge pull request #14 from lay295/master
superbonaci ba343bd
Merge pull request #15 from lay295/master
superbonaci a6ef5ee
Merge pull request #16 from lay295/master
superbonaci 14b0728
Merge pull request #17 from lay295/master
superbonaci 5f40948
Merge pull request #18 from lay295/master
superbonaci a3134c9
Cleanup code and fix Readme entry
ScrubN 592f28f
Merge pull request #19 from lay295/master
superbonaci a4b4b5c
Merge pull request #20 from lay295/master
superbonaci 5a7fb47
Merge pull request #21 from lay295/master
superbonaci 4d607b3
Merge pull request #22 from lay295/master
superbonaci a11aa5b
Merge pull request #23 from lay295/master
superbonaci 719e3e4
Merge pull request #24 from lay295/master
superbonaci 98a3771
Cleanup
ScrubN File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using CommandLine; | ||
|
||
namespace TwitchDownloaderCLI.Modes.Arguments | ||
{ | ||
[Verb("tsmerge", HelpText = "Concatenates multiple .ts/.tsv/.tsa/.m2t/.m2ts (MPEG Transport Stream) files into a single file")] | ||
public class TsMergeArgs : ITwitchDownloaderArgs | ||
{ | ||
[Option('i', "input", Required = true, HelpText = "Path a text file containing the absolute paths of the files to concatenate, separated by newlines. M3U/M3U8 is also supported.")] | ||
public string InputList { get; set; } | ||
|
||
[Option('o', "output", Required = true, HelpText = "Path to output file.")] | ||
public string OutputFile { get; set; } | ||
|
||
[Option("banner", Default = true, HelpText = "Displays a banner containing version and copyright information.")] | ||
public bool? ShowBanner { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
using System; | ||
using System.Threading; | ||
using TwitchDownloaderCLI.Modes.Arguments; | ||
using TwitchDownloaderCLI.Tools; | ||
using TwitchDownloaderCore; | ||
using TwitchDownloaderCore.Options; | ||
|
||
namespace TwitchDownloaderCLI.Modes | ||
{ | ||
internal static class MergeTs | ||
{ | ||
internal static void Merge(TsMergeArgs inputOptions) | ||
{ | ||
Progress<ProgressReport> progress = new(); | ||
progress.ProgressChanged += ProgressHandler.Progress_ProgressChanged; | ||
|
||
var mergeOptions = GetMergeOptions(inputOptions); | ||
TsMerger tsMerger = new(mergeOptions, progress); | ||
tsMerger.MergeAsync(new CancellationToken()).Wait(); | ||
} | ||
|
||
private static TsMergeOptions GetMergeOptions(TsMergeArgs inputOptions) | ||
{ | ||
TsMergeOptions mergeOptions = new() | ||
{ | ||
OutputFile = inputOptions.OutputFile, | ||
InputFile = inputOptions.InputList | ||
}; | ||
|
||
return mergeOptions; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace TwitchDownloaderCore.Options | ||
{ | ||
public class TsMergeOptions | ||
{ | ||
public string OutputFile { get; set; } | ||
public string InputFile { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using TwitchDownloaderCore.Options; | ||
using TwitchDownloaderCore.Tools; | ||
|
||
namespace TwitchDownloaderCore | ||
{ | ||
public sealed class TsMerger | ||
{ | ||
private readonly TsMergeOptions mergeOptions; | ||
private readonly IProgress<ProgressReport> _progress; | ||
|
||
public TsMerger(TsMergeOptions tsMergeOptions, IProgress<ProgressReport> progress) | ||
{ | ||
mergeOptions = tsMergeOptions; | ||
_progress = progress; | ||
} | ||
|
||
public async Task MergeAsync(CancellationToken cancellationToken) | ||
{ | ||
if (!File.Exists(mergeOptions.InputFile)) | ||
{ | ||
throw new FileNotFoundException("Input file does not exist"); | ||
} | ||
|
||
var fileList = new List<string>(); | ||
await using (var fs = File.Open(mergeOptions.InputFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) | ||
{ | ||
using var sr = new StreamReader(fs); | ||
while (await sr.ReadLineAsync() is { } line) | ||
{ | ||
if (string.IsNullOrWhiteSpace(line)) | ||
continue; | ||
|
||
if (line.StartsWith('#')) | ||
continue; | ||
|
||
fileList.Add(line); | ||
} | ||
} | ||
|
||
_progress.Report(new ProgressReport(ReportType.SameLineStatus, "Verifying Parts 0% [1/2]")); | ||
|
||
await VerifyVideoParts(fileList, cancellationToken); | ||
|
||
_progress.Report(new ProgressReport() { ReportType = ReportType.NewLineStatus, Data = "Combining Parts 0% [2/2]" }); | ||
|
||
await CombineVideoParts(fileList, cancellationToken); | ||
|
||
_progress.Report(new ProgressReport(100)); | ||
} | ||
|
||
private async Task VerifyVideoParts(IReadOnlyCollection<string> fileList, CancellationToken cancellationToken) | ||
{ | ||
var failedParts = new List<string>(); | ||
var partCount = fileList.Count; | ||
var doneCount = 0; | ||
|
||
foreach (var part in fileList) | ||
{ | ||
var isValidTs = await VerifyVideoPart(part); | ||
if (!isValidTs) | ||
{ | ||
failedParts.Add(part); | ||
} | ||
|
||
doneCount++; | ||
var percent = (int)(doneCount / (double)partCount * 100); | ||
_progress.Report(new ProgressReport(ReportType.SameLineStatus, $"Verifying Parts {percent}% [1/2]")); | ||
_progress.Report(new ProgressReport(percent)); | ||
|
||
cancellationToken.ThrowIfCancellationRequested(); | ||
} | ||
|
||
if (failedParts.Count != 0) | ||
{ | ||
if (failedParts.Count == fileList.Count) | ||
{ | ||
// Every video part returned corrupted, probably a false positive. | ||
return; | ||
} | ||
|
||
_progress.Report(new ProgressReport(ReportType.Log, $"The following TS files appear to be invalid or corrupted: {string.Join(", ", failedParts)}")); | ||
} | ||
} | ||
|
||
private static async Task<bool> VerifyVideoPart(string filePath) | ||
{ | ||
const int TS_PACKET_LENGTH = 188; // MPEG TS packets are made of a header and a body: [ 4B ][ 184B ] - https://tsduck.io/download/docs/mpegts-introduction.pdf | ||
|
||
if (!File.Exists(filePath)) | ||
{ | ||
return false; | ||
} | ||
|
||
await using var fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); | ||
var fileLength = fs.Length; | ||
if (fileLength == 0 || fileLength % TS_PACKET_LENGTH != 0) | ||
{ | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
private async Task CombineVideoParts(IReadOnlyCollection<string> fileList, CancellationToken cancellationToken) | ||
{ | ||
DriveInfo outputDrive = DriveHelper.GetOutputDrive(mergeOptions.OutputFile); | ||
string outputFile = mergeOptions.OutputFile; | ||
|
||
int partCount = fileList.Count; | ||
int doneCount = 0; | ||
|
||
await using var outputStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.Read); | ||
foreach (var partFile in fileList) | ||
{ | ||
await DriveHelper.WaitForDrive(outputDrive, _progress, cancellationToken); | ||
|
||
await using (var fs = File.Open(partFile, FileMode.Open, FileAccess.Read, FileShare.Read)) | ||
{ | ||
await fs.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false); | ||
} | ||
|
||
doneCount++; | ||
int percent = (int)(doneCount / (double)partCount * 100); | ||
_progress.Report(new ProgressReport(ReportType.SameLineStatus, $"Combining Parts {percent}% [2/2]")); | ||
_progress.Report(new ProgressReport(percent)); | ||
|
||
cancellationToken.ThrowIfCancellationRequested(); | ||
} | ||
} | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function should be functionally equivalent to
cat
/copy /b
, no? It quite literally opens a file, copies the bytes to a destination, then closes the file:And I'm not sure why this says the ts files are partially recoded? We rely solely on FFmpeg for video encoding, and this function doesn't ever call FFmpeg.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No it's not a raw concatenation, the resulting file has different size than the sum of its parts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
cat *.ts > cat.ts
under WSL I get the exact same result as I do fromCombineVideoParts
.In order to achieve this result with cat, however, I had to pad the filenames with zeroes so they were enumerated in the correct order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've downloaded all the parts and both match too. It's a corner case, I don't have anymore the test stream which caused the issue, I think it was for paid subscribers only and maybe it's removed because it's more than 2 months old.
Anyway I've fixed it already, I'm sure the issue will happen again eventually with some other video, maybe it's the older ones only, no idea about that. What I'm most worried now, is that ffmpeg converts ts files to mp4 with random stream errors, this is how I came about to test the parts concatenation in the first place, and I've found that the mp4/mkv can be fault for those 2 reasons:
I've already asked you to implement the feature to keep ts parts for manual concatenation (apart from automatic one): #844 which would help me a lot to make some batch testing. please take this as high priority.
If you want me to implement the feature I could try and submit another PR.
If I find another stream which does not concatenate ts parts correctly in raw mode I'll tell about it don't worry.