Skip to content
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

fix: use mkvmerge to concat and ReMux again #68

Merged
merged 1 commit into from
Jan 6, 2025

Conversation

Tohrusky
Copy link
Member

@Tohrusky Tohrusky commented Jan 6, 2025

Summary by Sourcery

Switch from ffmpeg to mkvmerge for video concatenation and remuxing.

Bug Fixes:

  • Fix timestamp issues when cutting videos.

Enhancements:

  • Improve video merging performance by using mkvmerge.

Copy link

sourcery-ai bot commented Jan 6, 2025

Reviewer's Guide by Sourcery

This pull request refactors the video merging process to use mkvmerge for concatenation and remuxing, improving efficiency and fixing tag issues. It also adds the -reset_timestamps flag to the ffmpeg command used for video cutting to ensure accurate timestamps in the output segments.

Sequence diagram for updated video merging process

sequenceDiagram
    participant Client
    participant MergeVideo
    participant mkvmerge
    participant ffmpeg
    participant mkvpropedit

    Client->>MergeVideo: MergeVideo(originPath, inputFiles, outputPath)
    Note over MergeVideo: Clear temp files
    MergeVideo->>mkvmerge: Concat video clips
    mkvmerge-->>MergeVideo: tempVideoConcatOutputPath
    MergeVideo->>ffmpeg: ReMuxWithSourceVideo
    ffmpeg-->>MergeVideo: tempVideoMergedOutputPath
    MergeVideo->>mkvmerge: Final ReMux
    mkvmerge-->>MergeVideo: outputPath
    MergeVideo->>mkvpropedit: Remove tags
    mkvpropedit-->>MergeVideo: Clean output
    MergeVideo-->>Client: Return result
Loading

Sequence diagram for video cutting process

sequenceDiagram
    participant Client
    participant CutVideo
    participant ffmpeg

    Client->>CutVideo: CutVideo(inputPath, outputFolder)
    Note over CutVideo: Create script based on OS
    CutVideo->>ffmpeg: Execute segment command with reset_timestamps
    ffmpeg-->>CutVideo: Generate segments and list
    CutVideo-->>Client: Return segment files
Loading

State diagram for video processing workflow

stateDiagram-v2
    [*] --> CutVideo
    CutVideo --> Segments: ffmpeg with reset_timestamps
    Segments --> MergeVideo: Process segments
    MergeVideo --> Concatenated: mkvmerge concat
    Concatenated --> ReMuxed: ffmpeg remux
    ReMuxed --> FinalReMux: mkvmerge remux
    FinalReMux --> CleanOutput: mkvpropedit remove tags
    CleanOutput --> [*]
Loading

File-Level Changes

Change Details Files
Use mkvmerge to combine video segments.
  • Replaced ffmpeg concatenation with mkvmerge.
  • Removed temporary file for storing file list.
  • Added handling for merging encoded clips with mkvmerge.
  • Updated temporary file cleanup logic.
module/ffmpeg/merge.go
Use mkvmerge to remux the merged video with the original source.
  • Used mkvmerge to remux the merged video with the original source.
  • Updated ReMuxWithSourceVideo function to use mkvmerge.
  • Removed the iterative codec combination attempts in ReMuxWithSourceVideo.
module/ffmpeg/merge.go
Add -reset_timestamps flag to ffmpeg in CutVideo function.
  • Added the -reset_timestamps 1 flag to the ffmpeg command.
  • Ensures each segment starts with a timestamp of 0.
module/ffmpeg/cut.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Tohrusky - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider using unique temporary filenames (e.g. with UUID suffixes) to avoid potential conflicts during concurrent operations
Here's what I looked at during the review
  • 🟡 General issues: 1 issue found
  • 🟡 Security: 1 issue found
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@@ -11,52 +10,59 @@ import (

// MergeVideo 使用 ffmpeg 进行视频合并,使用 mkvpropedit 清除 tags
func MergeVideo(originPath string, inputFiles []string, outputPath string) error {
listPath := "temp_list.txt" // 写入文件列表
tempVideoConcatOutputPath := "temp_video_concat_output.mkv"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 suggestion (security): Consider using os.CreateTemp for temporary file paths to avoid conflicts

Hardcoded temporary file paths could cause issues with concurrent operations or in environments with restricted working directory permissions.

Suggested implementation:

import (
	"fmt"
	"os"
	"os/exec"

	"github.com/TensoRaws/FinalRip/module/log"
func MergeVideo(originPath string, inputFiles []string, outputPath string) error {
	// Create temporary files
	tempVideoConcatOutput, err := os.CreateTemp("", "video_concat_*.mkv")
	if err != nil {
		return fmt.Errorf("failed to create temporary concat output file: %w", err)
	}
	defer os.Remove(tempVideoConcatOutput.Name())
	tempVideoConcatOutputPath := tempVideoConcatOutput.Name()

	tempVideoMergedOutput, err := os.CreateTemp("", "video_merged_*.mkv")
	if err != nil {
		return fmt.Errorf("failed to create temporary merged output file: %w", err)
	}
	defer os.Remove(tempVideoMergedOutput.Name())
	tempVideoMergedOutputPath := tempVideoMergedOutput.Name()

The rest of the function should continue using tempVideoConcatOutputPath and tempVideoMergedOutputPath as before, but now they'll point to unique temporary files that will be automatically cleaned up when the function returns (due to the defer statements).

Make sure the function properly closes the temporary file handles after creating them if they're not needed to remain open. If the rest of the code needs to write to these files, you might need to keep the file handles open longer.

Comment on lines +30 to 32
}
mkvmergeArgs = append(mkvmergeArgs, file)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider checking mkvmerge exit codes more carefully as non-zero doesn't always indicate failure

mkvmerge can return non-zero exit codes for warnings while still producing valid output. Consider parsing the output to determine if it's a real error.

Suggested implementation:

	mkvmergeArgs := []string{"--output", tempVideoConcatOutputPath}
	for i, file := range inputFiles {
		if i > 0 {
			mkvmergeArgs = append(mkvmergeArgs, "+")
		}
		mkvmergeArgs = append(mkvmergeArgs, file)
	}

	cmd := exec.Command("mkvmerge", mkvmergeArgs...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		// mkvmerge exit code 1 indicates warnings but successful merge
		// exit code 2 indicates fatal errors
		if exitErr, ok := err.(*exec.ExitError); ok {
			if exitErr.ExitCode() == 1 {
				// Warning occurred but output should be valid
				log.Logger.Warnf("mkvmerge completed with warnings: %s", string(output))
				return nil
			}
		}
		log.Logger.Errorf("mkvmerge failed: %v\nOutput: %s", err, string(output))
		return fmt.Errorf("mkvmerge failed: %v", err)

You'll need to ensure the following imports are available:

  • "os/exec"
  • "fmt"

Also, this assumes the code continues with executing mkvmerge after the shown snippet. If there's existing mkvmerge execution code, you'll need to modify that instead of adding the new execution code shown here.

@@ -11,52 +10,59 @@

// MergeVideo 使用 ffmpeg 进行视频合并,使用 mkvpropedit 清除 tags
func MergeVideo(originPath string, inputFiles []string, outputPath string) error {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider simplifying video merging by using mkvmerge for both concatenation and stream mapping in a single command.

The current implementation uses three different tools and multiple conversion steps, making the code harder to maintain. The same result can be achieved using just mkvmerge for both concatenation and stream mapping, eliminating the need for ffmpeg and temporary files.

Here's a simplified approach:

func MergeVideo(originPath string, inputFiles []string, outputPath string) error {
    // Build mkvmerge command to handle both concatenation and stream mapping
    args := []string{
        "-o", outputPath,
        "--no-global-tags",  // Removes tags directly
        originPath,  // Add original file first for audio/subtitle streams
        "--no-video",  // Don't take video from original file
        "--audio-tracks", "all",  // Keep all audio tracks
        "--subtitle-tracks", "all",  // Keep all subtitle tracks
    }

    // Add input files for concatenated video
    for i, file := range inputFiles {
        if i > 0 {
            args = append(args, "+")
        }
        args = append(args, "--no-audio", "--no-subtitles", file)
    }

    cmd := exec.Command("mkvmerge", args...)
    out, err := cmd.CombinedOutput()
    if err != nil {
        log.Logger.Errorf("Video merge failed: %v", err)
        return err
    }
    log.Logger.Infof("Merge output: %s", out)
    return nil
}

This solution:

  1. Eliminates temporary files and multiple conversion steps
  2. Removes the need to try different codec combinations
  3. Handles stream mapping and concatenation in a single operation
  4. Maintains the same functionality with less code and complexity

@Tohrusky Tohrusky merged commit df4ce4f into TensoRaws:main Jan 6, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant