Class: Omnizip::Parity::Par2Repairer

Inherits:
Object
  • Object
show all
Defined in:
lib/omnizip/parity/par2_repairer.rb

Overview

PAR2 archive repairer

Repairs damaged or missing files using PAR2 recovery blocks and Reed-Solomon error correction.

Examples:

Repair damaged files

repairer = Par2Repairer.new('backup.par2')
result = repairer.repair
puts "Repaired #{result.recovered_blocks} blocks"

Defined Under Namespace

Classes: RepairResult

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(par2_file, progress: nil) ⇒ Par2Repairer

Initialize repairer

Parameters:

  • par2_file (String)

    Path to .par2 index file

  • progress (Proc, nil) (defaults to: nil)

    Progress callback

Raises:

  • (ArgumentError)

    if file doesn't exist



56
57
58
59
60
61
62
63
# File 'lib/omnizip/parity/par2_repairer.rb', line 56

def initialize(par2_file, progress: nil)
  raise ArgumentError, "PAR2 file not found: #{par2_file}" unless
    File.exist?(par2_file)

  @par2_file = par2_file
  @progress_callback = progress
  @verifier = Par2Verifier.new(par2_file)
end

Instance Attribute Details

#par2_fileString (readonly)

Returns Path to PAR2 index file.

Returns:

  • (String)

    Path to PAR2 index file



43
44
45
# File 'lib/omnizip/parity/par2_repairer.rb', line 43

def par2_file
  @par2_file
end

#progress_callbackProc? (readonly)

Returns Progress callback.

Returns:

  • (Proc, nil)

    Progress callback



49
50
51
# File 'lib/omnizip/parity/par2_repairer.rb', line 49

def progress_callback
  @progress_callback
end

#verifierPar2Verifier (readonly)

Returns Verifier instance.

Returns:



46
47
48
# File 'lib/omnizip/parity/par2_repairer.rb', line 46

def verifier
  @verifier
end

Instance Method Details

#repair(output_dir: nil) ⇒ RepairResult

Repair damaged files

Parameters:

  • output_dir (String, nil) (defaults to: nil)

    Output directory (default: same as source)

Returns:



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/omnizip/parity/par2_repairer.rb', line 69

def repair(output_dir: nil)
  report_progress(0, "Verifying files")

  # First verify to find damage
  verification = @verifier.verify

  if verification.all_ok?
    return RepairResult.new(
      success: true,
      recovered_files: [],
      recovered_blocks: 0,
      unrecoverable: [],
      error_message: nil,
    )
  end

  unless verification.repairable?
    return RepairResult.new(
      success: false,
      recovered_files: [],
      recovered_blocks: 0,
      unrecoverable: verification.damaged_files + verification.missing_files,
      error_message: "Insufficient recovery blocks to repair damage",
    )
  end

  report_progress(10, "Loading recovery blocks")

  # Load all data
  data_blocks = load_data_blocks(verification)
  parity_blocks_by_exp = load_parity_blocks_by_exponent

  report_progress(30, "Calculating repairs")

  # Identify erasures (damaged/missing blocks)
  erasures = identify_erasures(verification)

  report_progress(50, "Recovering damaged blocks")

  # Perform Reed-Solomon decoding with new decoder
  begin
    recovered_data = perform_recovery(
      data_blocks,
      parity_blocks_by_exp,
      erasures,
      @verifier.[:block_size],
    )
  rescue StandardError => e
    return RepairResult.new(
      success: false,
      recovered_files: [],
      recovered_blocks: 0,
      unrecoverable: verification.damaged_files,
      error_message: "Recovery failed: #{e.message}",
    )
  end

  # Combine original good blocks with recovered blocks
  recovered_blocks = data_blocks.each_with_index.map do |block, idx|
    if erasures.include?(idx)
      recovered_data[idx]
    else
      block
    end
  end

  report_progress(80, "Writing repaired files")

  # Write recovered files
  recovered_files = write_recovered_files(
    recovered_blocks,
    erasures,
    output_dir,
  )

  report_progress(100, "Repair complete")

  RepairResult.new(
    success: true,
    recovered_files: recovered_files,
    recovered_blocks: erasures.size,
    unrecoverable: [],
    error_message: nil,
  )
end