Get file name and extension in Ruby - ruby

I'm working on a program to download a video from YouTube, convert it to MP3 and create a directory structure for the files.
My code is:
FileUtils.cd("#{$musicdir}/#{$folder}") do
YoutubeDlhelperLibs::Downloader.get($url)
if File.exists?('*.mp4')
puts 'Remove unneeded tempfile'
Dir['*.mp4'].each do |waste|
File.delete(waste)
end
else
puts 'Temporary file already deleted'
end
Dir['*.m4a'].each do |rip|
rip.to_s
rip.split
puts 'Inside the function'
puts rip
end
end
The first one goes to the already created music folder. Inside that I'm executing get. After that I have two files in the directory: "xyz.mp4" and "xyz.m4a".
I would like to fetch the filename without the extension so I can handle both files differently.
I'm using an array, but an array for just one match sounds crazy for me.
Has anyone another idea?

You can use the following functions for your purpose:
path = "/path/to/xyz.mp4"
File.basename(path) # => "xyz.mp4"
File.extname(path) # => ".mp4"
File.basename(path, ".mp4") # => "xyz"
File.basename(path, ".*") # => "xyz"
File.dirname(path) # => "/path/to"

Related

Ruby: check if a .zip file exists, and extract

2 small questions to create the effect I'm looking for.
How do I check if a file exists within a directory with the extension of .zip?
If it does exist I need to make a folder with the same name as the .zip without the .zip extension for the folder.
Then I need to extract the files into the folder.
Secondly, what do I do if there are more than one .zip files in the folder?
I'm doing something like this and trying to put it into ruby
`mkdir fileNameisRandom`
`unzip fileNameisRandom.zip -d fileNameisRandom`
On a similar post I found something like
Dir.entries("#{Dir.pwd}").select {|f| File.file? f}
which I know checks all files within a directory and makes sure they are a file.
The problem is I don't know how to make sure that it is only an extension of .zip
Also, I found the Glob function which checks the extension of a filename from: http://ruby-doc.org/core-1.9.3/Dir.html
How do I ensure the file exists in that case, and if it doesn't I can print out an error then.
From the comment I now have
if Dir['*.zip'].first == nil #check to see if any exist
puts "A .zip file was not found"
elsif Dir['*.zip'].select {|f| File.file? f} then #ensure each of them are a file
#use a foreach loop to go through each one
Dir['*.zip'].select.each do |file|
puts "#{file}"
end ## end for each loop
end
Here's a way of doing this with less branching:
# prepare the data
zips= Dir['*.zip'].select{ |f| File.file? }
# check if data is sane
if zips.empty?
puts "No zips"
exit 0 # or return
end
# process data
zips.each do |z|
end
This pattern is easier to follow for fellow programmers.
You can also do it using a ruby gem called rubyzip
Gemfile:
source 'https://rubygems.org'
gem 'rubyzip'
run bundle
unzip.rb:
require 'zip'
zips= Dir['*.zip'].select{ |f| File.file? }
if zips.empty?
puts "No zips"
exit 0 # or return
end
zips.each do |zip|
Zip::File.open(zip) do |files|
files.each do |file|
# write file somewhere
# see here https://github.com/rubyzip/rubyzip
end
end
end
I finally pieced together different information from tutorials and used #rogerdpack and his comment for help.
require 'rubygems/package'
#require 'zlib'
require 'fileutils'
#move to the unprocessed directory to unpack the files
#if a .tgz file exists
#take all .tgz files
#make a folder with the same name
#put all contained folders from .tgz file inside of similarly named folder
#Dir.chdir("awaitingApproval/")
if Dir['*.zip'].first == nil #check to see if any exist, I use .first because Dir[] returns an array
puts "A .zip file was not found"
elsif Dir['*.zip'].select {|f| File.file? f} then #ensure each of them are a file
#use a foreach loop to go through each one
Dir['*.zip'].select.each do |file|
puts "" #newlie for each file
puts "#{file}" #print out file name
#next line based on `mkdir fileNameisRandom`
`mkdir #{Dir.pwd}/awaitingValidation/#{ File.basename(file, File.extname(file)) }`
#next line based on `unzip fileNameisRandom.zip -d fileNameisRandom`
placement = "awaitingValidation/" + File.basename(file, File.extname(file))
puts "#{placement}"
`sudo unzip #{file} -d #{placement}`
puts "Unzip complete"
end ## end for each loop
end

How do I change the directory but still write to existing file?

I wrote a script that creates a text file, and saves everything I need to save. The problem comes after I make a cycle and change the directory to upload files in another directory, but the text file is left in the old directory.
How can I change the directory but still write to the text file? This is my code:
kolekcija.each do |fails|
#b.send_keys :tab
#b.span(:class => "btnText", :text => "Save", :index => 1).when_present.click
#b.frame(:id, "uploadManagerFrame").table(:id, "ctrlGetUploadedFiles_gvUploadedFiles").wait_until_present
sleep 30
# I need to edit it so it opens the TXT file in its existing location
output = File.open("#{Time.now.strftime("%Y.%m.%d")} DemoUser.txt", "a")
output.puts ""
output.puts "Korpusā ielādētais fails: #{File.basename(#fails)} augšuplādēts sekmīgi..."
output.close
progress.increment
end
I'll edit this if the question changes, because as is it's not clear; I don't see any change of directory, so I'm assuming that the user:
Changes directory somewhere in there.
Wants to continue appending information to the same text document.
If that's true, the answer is going to be either to use an absolute path to the text file:
file = File.open("/full/path/to/file", "a")
kolekcija.each do |fails|
# ...
file.puts "some stuff"
# ...
end
file.close
If you're doing these long sleeps, that might be a problem, but you could also hold on to the path:
path = "/full/path/to/file"
kolekcija.each do |fails|
# ...
file = File.open(path, "a")
file.puts "some stuff"
file.close
# ...
end
Or use a Dir.chdir block in the part of the script where you want to change the directory to something else and back:
Dir.chdir(ENV["HOME"]) # now you're in your home directory ~
Dir.chdir("files") do # now in ~/files
upload_files
end # aaand you're back home
file = File.open("/full/path/to/file", "a")
file.puts "stuff"
file.close
I'll admit I'm not 100% sure about what the question is asking, but the solution is either going to be holding on to the filehandle, using an absolute path, or changing back to the original directory before writing to the text file.

send_file for a tempfile in Sinatra

I'm trying to use Sinatra's built-in send_file command but it doesn't seem to be working for tempfiles.
I basically do the following to zip an album of mp3s:
get '/example' do
songs = ...
file_name = "zip_test.zip"
t = Tempfile.new(['temp_zip', '.zip'])
# t = File.new("testfile.zip", "w")
Zip::ZipOutputStream.open(t.path) do |z|
songs.each do |song|
name = song.name
name += ".mp3" unless name.end_with?(".mp3")
z.put_next_entry(name)
z.print(open(song.url) {|f| f.read })
p song.name + ' added to file'
end
end
p t.path
p t.size
send_file t.path, :type => 'application/zip',
:disposition => 'attachment',
:filename => file_name,
:stream => false
t.close
t.unlink
end
When I use t = File.new(...) things work as expected, but I don't want to use File as it will have concurrency problems.
When I use t = Tempfile.new(...), I get:
!! Unexpected error while processing request: The file identified by body.to_path does not exist`
Edit: It looks like part of the problem is that I'm sending multiple files. If I just send one song, the Tempfile system works as well.
My guess is that you have a typo in one of your song-names, or maybe a slash in one of the last parts of song.url? I adopted your code and if all the songs exist, sending the zip as a tempfile works perfectly fine.

Unzipping a file and ignoring 'junk files' added by OS X

I am using code like the following to unzip files in Ruby:
def unzip_file (file)
Zip::ZipFile.open(file) do |zip_file|
zip_file.each do |f|
puts f.name if f.file?
end
end
end
I would like to ignore all files generated by compress zip in Mac such as: .DS_Store, etc. How can I best do it?
I believe that this does what you want:
Zip::ZipFile.open(file) do |zip_file|
names = zip_file.select(&:file?).map(&:name)
names.reject!{|n| n=~ /\.DS_Store|__MACOSX|(^|\/)\._/ }
puts names
end
That regular expression says,
Throw away files
that have .DS_Store in the name,
that have __MACOSX in the name,
or that have ._ at the beginning of the name (^) or right after a /.
That should cover all the 'junk' files and hopefully not hit any others.
If you want more than just the names—if you want to process the non-junk files—then instead you might do the following:
Zip::ZipFile.open(file) do |zip_file|
files = zip_file.select(&:file?)
files.reject!{|f| f.name =~ /\.DS_Store|__MACOSX|(^|\/)\._/ }
puts files.map(&:names) # or do whatever else you want with the array of files
end

Using rubyzip to add files and nested directories to a zipoutputstream

I'm struggling with getting rubyzip to append directories to a zipoutputstream. (I want the output stream so I can send it from a rails controller). My code follows this example:
http://info.michael-simons.eu/2008/01/21/using-rubyzip-to-create-zip-files-on-the-fly/
When modified to include directories in the list of files to add I get the following error:
Any help would be greatly appreciated.
UPDATE
After trying a number of solutions I had best success with zipruby which has a clean api and good examples: http://zipruby.rubyforge.org/.
Zip::ZipFile.open(path, Zip::ZipFile::CREATE) do |zip|
songs.each do |song|
zip.add "record/#{song.title.parameterize}.mp3", song.file.to_file.path
end
end
OOOOOuuuhh...you DEFINITELY want ZIPPY. It's a Rails plugin that abstracts a lot of the complexity in rubyzip, and lets you create what you're talking about, including directories (from what I recall).
Here you go:
http://github.com/toretore/zippy
And direct from the zippy site:
Example controller:
def show
#gallery = Gallery.find(params[:id])
respond_to do |format|
format.html
format.zip
end
end
Example view:
zip['description.txt'] = #gallery.description
#gallery.photos.each do |photo|
zip["photo_#{photo.id}.png"] = File.open(photo.url)
end
edit: Amending per user comment:
Hmm...the whole objective of using Zippy is to make it a whole lot easier to use ruby zip.
Ya might want to take a second (or first) look...
Here's how to make a directory with directories:
some_var = Zippy.open('awsum.zip') do |zip|
%w{dir_a dir_b dir_c diri}.each do |dir|
zip["bin/#{dir}/"]
end
end
...
send_file some_var, :file_name => ...
Zippy will work for this. There may be a more cool way to do this but since there are essentially no docs, here's what I came up with for recursively copying directories with Zippy in a Rakefile. This Rakefile is used in a Rails environment so I put gem requirements in my Gemfile:
#Gemfile
source 'http://rubygems.org'
gem 'rails'
gem 'zippy'
And this is the Rakefile
#Rakefile
def add_file( zippyfile, dst_dir, f )
zippyfile["#{dst_dir}/#{f}"] = File.open(f)
end
def add_dir( zippyfile, dst_dir, d )
glob = "#{d}/**/*"
FileList.new( glob ).each { |f|
if (File.file?(f))
add_file zippyfile, dst_dir, f
end
}
end
task :myzip do
Zippy.create 'my.zip' do |z|
add_dir z, 'my', 'app'
add_dir z, 'my', 'config'
#...
add_file z, 'my', 'config.ru'
add_file z, 'my', 'Gemfile'
#...
end
end
Now I can use it like this:
C:\> cd my
C:\my> rake myzip
and it will produce my.zip which contains an inner directory called 'my' with copies of selected files and directories.
I was able to get directories working with the same ZipOutputStream used in the original article.
All I had to do was add the directory when calling zos.put_next_entry.
For example:
require 'zip/zip'
require 'zip/zipfilesystem'
t = Tempfile.new("some-weird-temp-file-basename-#{request.remote_ip}")
# Give the path of the temp file to the zip outputstream, it won't try to open it as an archive.
Zip::ZipOutputStream.open(t.path) do |zos|
some_file_list.each do |file|
# Create a new entry with some arbitrary name
zos.put_next_entry("myfolder/some-funny-name.jpg") # Added myfolder/
# Add the contents of the file, don't read the stuff linewise if its binary, instead use direct IO
zos.print IO.read(file.path)
end
end
# End of the block automatically closes the file.
# Send it using the right mime type, with a download window and some nice file name.
send_file t.path, :type => 'application/zip', :disposition => 'attachment', :filename => "some-brilliant-file-name.zip"
# The temp file will be deleted some time...
t.close
I just changed zos.put_next_entry('some-funny-name.jpg') to zos.put_next_entry('myfolder/some-funny-name.jpg'), and the resulting zipfile had a nested folder called myfolder that contained the files.

Resources