Skip to content

Instantly share code, notes, and snippets.

@ACUVE

ACUVE/main.rb Secret

Last active January 20, 2016 03:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ACUVE/7324918f98c63a6f7441 to your computer and use it in GitHub Desktop.
Save ACUVE/7324918f98c63a6f7441 to your computer and use it in GitHub Desktop.
#!/usr/bin/ruby
require 'shellwords'
require 'fileutils'
maxfilesize = 24700000000
Samefilefilter = [
%r!(?:^|.+/)[^/]+(?=\.[^/.]+$)!,
%r!(?:^|.+/)[^/]+(?=(?:\.MP4|M01\.XML))!i,
]
path = Dir.glob('**/*').select{|file| File.file?(file)}.sort
flag = Array.new(path.size){false}
pathmss = path.map{|file| Samefilefilter.map{|m| file.match(m)}}
files = []
path.zip(pathmss, 0...path.size) do |file, ms, i|
next if flag[i]
files << [file]
# 本来は path.size だけで良いのだが,流石に O(n^2) のアルゴリズムは遅すぎた
# 間引きのせいで意図通り動かない場合があるけれども,仕方ないね
#((i + 1)...[path.size,i + 2].min).each do |j|
((i + 1)...path.size).each do |j|
next if flag[j]
if ms.lazy.zip(pathmss[j]).any?{|om, cm| om && cm && om[0] == cm[0]}
files.last << path[j]
flag[j] = true
end
end
end
files = files.flat_map do |fs|
tmp = []
fs = fs.map do |f|
size = File.size(f)
if size > maxfilesize
i = 0
while size > maxfilesize
tmp << {files: [f + '.part%d' % (i += 1)], size: maxfilesize}
size -= maxfilesize
end
{file: f + '.part%d' % (i += 1), size: size}
else
{file: f, size: File.size(f)}
end
end
sumsize = fs.inject(0){|r, i| r + i[:size]}
if sumsize > maxfilesize
# まだ実装してない.
# ここに到達したら初めて実装する.
raise 0
end
tmp << {files: fs.map{|f| f[:file]}, size: sumsize}
end
bag = []
files.each do |file|
if b = bag.sort_by{|a| a[:size]}.drop_while{|a| a[:size] <= 0}.find{|t| t[:size] >= file[:size]}
b[:files] << file[:files]
b[:size] -= file[:size]
else
bag << {
files: [file[:files]],
size: maxfilesize - file[:size],
}
end
end
bag.each_with_index do |b, i|
puts "No.%<no>d - %<size>.3f GB" % {no: i + 1, size: (maxfilesize - b[:size]) / 1e9}
b[:files].each do |fs|
puts " - " + fs.join(" + ")
end
end
exit unless (dir = ARGV.shift) && !Dir.exist?(dir)
puts "----------------------------------"
puts "copy to #{dir}"
Dir.mkdir(dir)
bag.each_with_index do |v, i|
path = File.join(dir, 'No.%d' % (i + 1))
Dir.mkdir(path)
v[:files].each do |fs|
fs.each do |f|
if File.exist?(f)
newname = File.join(path, f)
FileUtils.mkdir_p(File.dirname(newname))
cmd = Shellwords.join(%W|cp -pn --reflink #{f} #{newname}|)
`#{cmd}`
cmd = Shellwords.join(%W|touch -r #{f} #{newname}|)
`#{cmd}`
elsif (m = f.match(/^(.+)\.part([1-9]\d*)$/)) && File.exist?(m[1])
realf = m[1]
partno = m[2].to_i
newname = File.join(path, f)
copy_size = File.size(realf) - maxfilesize * (partno - 1)
if copy_size > maxfilesize
copy_size = maxfilesize
end
FileUtils.mkdir_p(File.dirname(newname))
if partno == 1
cmd = Shellwords.join(%W|cp -pn --reflink #{realf} #{newname}|)
`#{cmd}`
File.truncate(newname, copy_size)
else
puts "copy #{realf} to #{newname}: #{copy_size.to_s.gsub(/(\d)(?=(?:\d{3})+(?!\d))/, '\1,')} bytes"
IO.copy_stream(realf, newname, copy_size, maxfilesize * (partno - 1))
end
cmd = Shellwords.join(%W|touch -r #{realf} #{newname}|)
`#{cmd}`
end
end
end
end
Dir.glob('**/*').select{|file| Dir.exist?(file) && Dir.entries(file).size == 2}.each{|f| FileUtils.mkdir_p(File.join(dir, 'No.1', f))}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment