# frozen_string_literal: true ## --- BEGIN LICENSE BLOCK --- # Copyright (c) 2016-present WeWantToKnow AS # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. ## --- END LICENSE BLOCK --- require 'u3d/utils' require 'u3d/download_validator' module U3d # Take care of downloading files and packages module Downloader # Name of the directory for the package downloading DOWNLOAD_DIRECTORY = 'Unity_Packages' # Path to the directory for the package downloading DOWNLOAD_PATH = "#{ENV['HOME']}/Downloads" # Regex to get the name of a localization asset UNITY_LANGUAGE_FILE_REGEX = %r{/\d+/[0-9.]+/([\w-]+)$}.freeze # Regex to get the name of a package out of its file name UNITY_MODULE_FILE_REGEX = %r{/([\w\-_.+]+\.(?:pkg|dmg|exe|zip|sh|deb|msi|xz))[^/]*$}.freeze class << self def download_directory File.expand_path(ENV['U3D_DOWNLOAD_PATH'] || File.join(DOWNLOAD_PATH, DOWNLOAD_DIRECTORY)) end # fetch modules and put them in local cache def fetch_modules(definition, packages: [], download: nil) if download download_modules(definition, packages: packages) else local_files(definition, packages: packages) end end # download packages def download_modules(definition, packages: []) files = [] validator, downloader = setup_os(definition.os) packages.each do |package| get_package(downloader, validator, package, definition, files) end files end # find already downloaded packages def local_files(definition, packages: []) files = [] validator, downloader = setup_os(definition.os) packages.each do |package| path = downloader.destination_for(package, definition) if File.file?(path) if validator.validate(package, path, definition) files << [package, path, definition[package]] else UI.important "File present at #{path} is not correct, will not be used. Skipping #{package}" end else UI.error "No file has been downloaded for #{package}, or it has been moved from #{path}" end end files end private #----------------------------------------------------------------- def setup_os(os) # downloader = Downloader::StandardPackageDownloader.new case os when :linux validator = LinuxValidator.new downloader = Downloader::LinuxDownloader.new when :mac validator = MacValidator.new downloader = Downloader::MacDownloader.new when :win validator = WindowsValidator.new downloader = Downloader::WindowsDownloader.new else raise ArgumentError, "Operating system #{os.id2name} is not recognized" end return validator, downloader end def get_package(downloader, validator, package, definition, files) path, url = downloader.destination_and_url_for(package, definition) package_info = definition[package] if File.file?(path) UI.verbose "Installer file for #{package_info.name} seems to be present at #{path}" if validator.validate(package, path, definition) UI.message "#{package_info.name} is already downloaded" files << [package, path, package_info] return else extension = File.extname(path) new_path = File.join(File.dirname(path), "#{File.basename(path, extension)}_CORRUPTED#{extension}") UI.important "File present at #{path} is not correct, it has been renamed to #{new_path}" File.rename(path, new_path) end end UI.header "Downloading #{package_info.name} version #{definition.version}" UI.message "Downloading from #{url.to_s.cyan.underline}" UI.message "Download will be found at #{path}" download_package(path, url, size: package_info.download_size) if validator.validate(package, path, definition) UI.success "Successfully downloaded #{package}." files << [package, path, package_info] else UI.error "Failed to download #{package}" end end def download_package(path, url, size: nil) Utils.download_file(path, url, size: size) rescue Interrupt => e # Ensure that the file is deleted if download is aborted File.delete path raise e end end class StandardPackageDownloader # for backward compatibility def destination_for(package, definition) destination_and_url_for(package, definition)[0] end def destination_and_url_for(package, definition) final_url = url_for(package, definition) dir = File.join(Downloader.download_directory, definition.version) Utils.ensure_dir(dir) file_name = if (language_match = UNITY_LANGUAGE_FILE_REGEX.match(final_url)) "#{language_match[1]}.po" # Unity uses PO (Portable object files) for localization elsif (module_match = UNITY_MODULE_FILE_REGEX.match(final_url)) module_match[1] else raise "Unable to download file at #{final_url}. Please report it to the u3d issues on Github: https://github.com/DragonBox/u3d/issues/new" end destination = File.expand_path(file_name, dir) [destination, final_url] end def url_for(package, definition) url = definition[package].url if url if url =~ /^http/ Utils.final_url(url) else definition.url + url end else definition.url end end end # for backward compatibility class MacDownloader < StandardPackageDownloader end class LinuxDownloader < StandardPackageDownloader end class WindowsDownloader < StandardPackageDownloader end end end