forked from Plemya-x/ALR
		
	Небольшие переводы и комментарии
This commit is contained in:
		| @@ -16,8 +16,8 @@ | ||||
| * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| */ | ||||
|  | ||||
| // Package dl contains abstractions for downloadingfiles and directories | ||||
| // from various sources. | ||||
| // Пакет dl содержит абстракции для загрузки файлов и каталогов | ||||
| // из различных источников. | ||||
| package dl | ||||
|  | ||||
| import ( | ||||
| @@ -43,31 +43,32 @@ import ( | ||||
| 	"plemya-x.ru/alr/pkg/loggerctx" | ||||
| ) | ||||
|  | ||||
| // Константа для имени файла манифеста кэша | ||||
| const manifestFileName = ".alr_cache_manifest" | ||||
|  | ||||
| // ErrChecksumMismatch occurs when the checksum of a downloaded file | ||||
| // does not match the expected checksum provided in the Options struct. | ||||
| // Объявление ошибок для несоответствия контрольной суммы и отсутствия алгоритма хеширования | ||||
| var ( | ||||
| 	ErrChecksumMismatch = errors.New("dl: checksums did not match") | ||||
| 	ErrNoSuchHashAlgo   = errors.New("dl: invalid hashing algorithm") | ||||
| ) | ||||
|  | ||||
| // Downloaders contains all the downloaders in the order in which | ||||
| // they should be checked | ||||
| // Массив доступных загрузчиков в порядке их проверки | ||||
| var Downloaders = []Downloader{ | ||||
| 	GitDownloader{}, | ||||
| 	TorrentDownloader{}, | ||||
| 	FileDownloader{}, | ||||
| } | ||||
|  | ||||
| // Type represents the type of download (file or directory) | ||||
| // Тип данных, представляющий тип загрузки (файл или каталог) | ||||
| type Type uint8 | ||||
|  | ||||
| // Объявление констант для типов загрузки | ||||
| const ( | ||||
| 	TypeFile Type = iota | ||||
| 	TypeDir | ||||
| ) | ||||
|  | ||||
| // Метод для получения строки, представляющей тип загрузки | ||||
| func (t Type) String() string { | ||||
| 	switch t { | ||||
| 	case TypeFile: | ||||
| @@ -78,8 +79,7 @@ func (t Type) String() string { | ||||
| 	return "<unknown>" | ||||
| } | ||||
|  | ||||
| // Options contains the options for downloading | ||||
| // files and directories | ||||
| // Структура Options содержит параметры для загрузки файлов и каталогов | ||||
| type Options struct { | ||||
| 	Hash             []byte | ||||
| 	HashAlgorithm    string | ||||
| @@ -92,6 +92,7 @@ type Options struct { | ||||
| 	LocalDir         string | ||||
| } | ||||
|  | ||||
| // Метод для создания нового хеша на основе указанного алгоритма хеширования | ||||
| func (opts Options) NewHash() (hash.Hash, error) { | ||||
| 	switch opts.HashAlgorithm { | ||||
| 	case "", "sha256": | ||||
| @@ -119,49 +120,26 @@ func (opts Options) NewHash() (hash.Hash, error) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Manifest holds information about the type and name | ||||
| // of a downloaded file or directory. It is stored inside | ||||
| // each cache directory for later use. | ||||
| // Структура Manifest хранит информацию о типе и имени загруженного файла или каталога | ||||
| type Manifest struct { | ||||
| 	Type Type | ||||
| 	Name string | ||||
| } | ||||
|  | ||||
| // Интерфейс Downloader для реализации различных загрузчиков | ||||
| type Downloader interface { | ||||
| 	// Name returns the name of the downloader | ||||
| 	Name() string | ||||
| 	// MatchURL checks if the given URL matches | ||||
| 	// the downloader. | ||||
| 	MatchURL(string) bool | ||||
| 	// Download downloads the object at the URL | ||||
| 	// provided in the options, to the destination | ||||
| 	// given in the options. It returns a type, | ||||
| 	// a name for the downloaded object (this may be empty), | ||||
| 	// and an error. | ||||
| 	Download(Options) (Type, string, error) | ||||
| } | ||||
|  | ||||
| // UpdatingDownloader extends the Downloader interface | ||||
| // with an Update method for protocols such as git, which | ||||
| // allow for incremental updates without changing the URL. | ||||
| // Интерфейс UpdatingDownloader расширяет Downloader методом Update | ||||
| type UpdatingDownloader interface { | ||||
| 	Downloader | ||||
| 	// Update checks for and performs any | ||||
| 	// available updates for the object | ||||
| 	// described in the options. It returns | ||||
| 	// true if an update was performed, or | ||||
| 	// false if no update was required. | ||||
| 	Update(Options) (bool, error) | ||||
| } | ||||
|  | ||||
| // Download downloads a file or directory using the specified options. | ||||
| // It first gets the appropriate downloader for the URL, then checks | ||||
| // if caching is enabled. If caching is enabled, it attempts to get | ||||
| // the cache directory for the URL and update it if necessary. | ||||
| // If the source is found in the cache, it links it to the destination | ||||
| // using hard links. If the source is not found in the cache, | ||||
| // it downloads the source to a new cache directory and links it | ||||
| // to the destination. | ||||
| // Функция Download загружает файл или каталог с использованием указанных параметров | ||||
| func Download(ctx context.Context, opts Options) (err error) { | ||||
| 	log := loggerctx.From(ctx) | ||||
| 	normalized, err := normalizeURL(opts.URL) | ||||
| @@ -216,9 +194,6 @@ func Download(ctx context.Context, opts Options) (err error) { | ||||
| 				return nil | ||||
| 			} | ||||
| 		} else { | ||||
| 			// If we cannot read the manifest, | ||||
| 			// this cache entry is invalid and | ||||
| 			// the source must be re-downloaded. | ||||
| 			err = os.RemoveAll(cacheDir) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| @@ -256,7 +231,7 @@ func Download(ctx context.Context, opts Options) (err error) { | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // writeManifest writes the manifest to the specified cache directory. | ||||
| // Функция writeManifest записывает манифест в указанный каталог кэша | ||||
| func writeManifest(cacheDir string, m Manifest) error { | ||||
| 	fl, err := os.Create(filepath.Join(cacheDir, manifestFileName)) | ||||
| 	if err != nil { | ||||
| @@ -266,7 +241,7 @@ func writeManifest(cacheDir string, m Manifest) error { | ||||
| 	return msgpack.NewEncoder(fl).Encode(m) | ||||
| } | ||||
|  | ||||
| // getManifest reads the manifest from the specified cache directory. | ||||
| // Функция getManifest считывает манифест из указанного каталога кэша | ||||
| func getManifest(cacheDir string) (m Manifest, err error) { | ||||
| 	fl, err := os.Open(filepath.Join(cacheDir, manifestFileName)) | ||||
| 	if err != nil { | ||||
| @@ -278,7 +253,7 @@ func getManifest(cacheDir string) (m Manifest, err error) { | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // handleCache links the cache directory or a file within it to the destination | ||||
| // Функция handleCache создает жесткие ссылки для файлов из каталога кэша в каталог назначения | ||||
| func handleCache(cacheDir, dest, name string, t Type) (bool, error) { | ||||
| 	switch t { | ||||
| 	case TypeFile: | ||||
| @@ -313,12 +288,7 @@ func handleCache(cacheDir, dest, name string, t Type) (bool, error) { | ||||
| 	return false, nil | ||||
| } | ||||
|  | ||||
| // linkDir recursively walks through a directory, creating | ||||
| // hard links for each file from the src directory to the | ||||
| // dest directory. If it encounters a directory, it will | ||||
| // create a directory with the same name and permissions | ||||
| // in the dest directory, because hard links cannot be | ||||
| // created for directories. | ||||
| // Функция linkDir рекурсивно создает жесткие ссылки для файлов из каталога src в каталог dest | ||||
| func linkDir(src, dest string) error { | ||||
| 	return filepath.Walk(src, func(path string, info os.FileInfo, err error) error { | ||||
| 		if err != nil { | ||||
| @@ -329,6 +299,8 @@ func linkDir(src, dest string) error { | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
|  | ||||
|  | ||||
| 		rel, err := filepath.Rel(src, path) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| @@ -343,6 +315,7 @@ func linkDir(src, dest string) error { | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // Функция getDownloader возвращает загрузчик, соответствующий URL | ||||
| func getDownloader(u string) Downloader { | ||||
| 	for _, d := range Downloaders { | ||||
| 		if d.MatchURL(u) { | ||||
| @@ -352,8 +325,7 @@ func getDownloader(u string) Downloader { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // normalizeURL normalizes a URL string, so that insignificant | ||||
| // differences don't change the hash. | ||||
| // Функция normalizeURL нормализует строку URL, чтобы незначительные различия не изменяли хеш | ||||
| func normalizeURL(u string) (string, error) { | ||||
| 	const normalizationFlags = purell.FlagRemoveTrailingSlash | | ||||
| 		purell.FlagRemoveDefaultPort | | ||||
| @@ -373,7 +345,7 @@ func normalizeURL(u string) (string, error) { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	// Fix magnet URLs after normalization | ||||
| 	// Исправление URL-адресов magnet после нормализации | ||||
| 	u = strings.Replace(u, "magnet://", "magnet:", 1) | ||||
| 	return u, nil | ||||
| } | ||||
|   | ||||
| @@ -36,40 +36,47 @@ import ( | ||||
| 	"plemya-x.ru/alr/internal/shutils/handlers" | ||||
| ) | ||||
|  | ||||
| // FileDownloader downloads files using HTTP | ||||
| // FileDownloader загружает файлы с использованием HTTP | ||||
| type FileDownloader struct{} | ||||
|  | ||||
| // Name always returns "file" | ||||
| // Name всегда возвращает "file" | ||||
| func (FileDownloader) Name() string { | ||||
| 	return "file" | ||||
| } | ||||
|  | ||||
| // MatchURL always returns true, as FileDownloader | ||||
| // is used as a fallback if nothing else matches | ||||
| // MatchURL всегда возвращает true, так как FileDownloader | ||||
| // используется как резерв, если ничего другого не соответствует | ||||
| func (FileDownloader) MatchURL(string) bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // Download downloads a file using HTTP. If the file is | ||||
| // compressed using a supported format, it will be extracted | ||||
| // Download загружает файл с использованием HTTP. Если файл | ||||
| // сжат в поддерживаемом формате, он будет распакован | ||||
| func (FileDownloader) Download(opts Options) (Type, string, error) { | ||||
| 	// Разбор URL | ||||
| 	u, err := url.Parse(opts.URL) | ||||
| 	if err != nil { | ||||
| 		return 0, "", err | ||||
| 	} | ||||
|  | ||||
| 	// Получение параметров запроса | ||||
| 	query := u.Query() | ||||
|  | ||||
| 	// Получение имени файла из параметров запроса | ||||
| 	name := query.Get("~name") | ||||
| 	query.Del("~name") | ||||
|  | ||||
| 	// Получение параметра архивации | ||||
| 	archive := query.Get("~archive") | ||||
| 	query.Del("~archive") | ||||
|  | ||||
| 	// Кодирование измененных параметров запроса обратно в URL | ||||
| 	u.RawQuery = query.Encode() | ||||
|  | ||||
| 	var r io.ReadCloser | ||||
| 	var size int64 | ||||
|  | ||||
| 	// Проверка схемы URL на "local" | ||||
| 	if u.Scheme == "local" { | ||||
| 		localFl, err := os.Open(filepath.Join(opts.LocalDir, u.Path)) | ||||
| 		if err != nil { | ||||
| @@ -85,6 +92,7 @@ func (FileDownloader) Download(opts Options) (Type, string, error) { | ||||
| 		} | ||||
| 		r = localFl | ||||
| 	} else { | ||||
| 		// Выполнение HTTP GET запроса | ||||
| 		res, err := http.Get(u.String()) | ||||
| 		if err != nil { | ||||
| 			return 0, "", err | ||||
| @@ -107,6 +115,7 @@ func (FileDownloader) Download(opts Options) (Type, string, error) { | ||||
| 	defer fl.Close() | ||||
|  | ||||
| 	var bar io.WriteCloser | ||||
| 	// Настройка индикатора прогресса | ||||
| 	if opts.Progress != nil { | ||||
| 		bar = progressbar.NewOptions64( | ||||
| 			size, | ||||
| @@ -134,18 +143,21 @@ func (FileDownloader) Download(opts Options) (Type, string, error) { | ||||
| 	} | ||||
|  | ||||
| 	var w io.Writer | ||||
| 	// Настройка MultiWriter для записи в файл, хеш и индикатор прогресса | ||||
| 	if opts.Hash != nil { | ||||
| 		w = io.MultiWriter(fl, h, bar) | ||||
| 	} else { | ||||
| 		w = io.MultiWriter(fl, bar) | ||||
| 	} | ||||
|  | ||||
| 	// Копирование содержимого из источника в файл назначения | ||||
| 	_, err = io.Copy(w, r) | ||||
| 	if err != nil { | ||||
| 		return 0, "", err | ||||
| 	} | ||||
| 	r.Close() | ||||
|  | ||||
| 	// Проверка контрольной суммы | ||||
| 	if opts.Hash != nil { | ||||
| 		sum := h.Sum(nil) | ||||
| 		if !bytes.Equal(sum, opts.Hash) { | ||||
| @@ -153,6 +165,7 @@ func (FileDownloader) Download(opts Options) (Type, string, error) { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Проверка необходимости постобработки | ||||
| 	if opts.PostprocDisabled { | ||||
| 		return TypeFile, name, nil | ||||
| 	} | ||||
| @@ -162,6 +175,7 @@ func (FileDownloader) Download(opts Options) (Type, string, error) { | ||||
| 		return 0, "", err | ||||
| 	} | ||||
|  | ||||
| 	// Идентификация формата архива | ||||
| 	format, ar, err := archiver.Identify(name, fl) | ||||
| 	if err == archiver.ErrNoMatch { | ||||
| 		return TypeFile, name, nil | ||||
| @@ -169,21 +183,25 @@ func (FileDownloader) Download(opts Options) (Type, string, error) { | ||||
| 		return 0, "", err | ||||
| 	} | ||||
|  | ||||
| 	// Распаковка архива | ||||
| 	err = extractFile(ar, format, name, opts) | ||||
| 	if err != nil { | ||||
| 		return 0, "", err | ||||
| 	} | ||||
|  | ||||
| 	// Удаление исходного архива | ||||
| 	err = os.Remove(path) | ||||
| 	return TypeDir, "", err | ||||
| } | ||||
|  | ||||
| // extractFile extracts an archive or decompresses a file | ||||
| // extractFile извлекает архив или распаковывает файл | ||||
| func extractFile(r io.Reader, format archiver.Format, name string, opts Options) (err error) { | ||||
| 	fname := format.Name() | ||||
|  | ||||
| 	// Проверка типа формата архива | ||||
| 	switch format := format.(type) { | ||||
| 	case archiver.Extractor: | ||||
| 		// Извлечение файлов из архива | ||||
| 		err = format.Extract(context.Background(), r, nil, func(ctx context.Context, f archiver.File) error { | ||||
| 			fr, err := f.Open() | ||||
| 			if err != nil { | ||||
| @@ -224,6 +242,7 @@ func extractFile(r io.Reader, format archiver.Format, name string, opts Options) | ||||
| 			return err | ||||
| 		} | ||||
| 	case archiver.Decompressor: | ||||
| 		// Распаковка сжатого файла | ||||
| 		rc, err := format.OpenReader(r) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| @@ -247,10 +266,9 @@ func extractFile(r io.Reader, format archiver.Format, name string, opts Options) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // getFilename attempts to parse the Content-Disposition | ||||
| // HTTP response header and extract a filename. If the | ||||
| // header does not exist, it will use the last element | ||||
| // of the path. | ||||
| // getFilename пытается разобрать заголовок Content-Disposition | ||||
| // HTTP-ответа и извлечь имя файла. Если заголовок отсутствует, | ||||
| // используется последний элемент пути. | ||||
| func getFilename(res *http.Response) (name string) { | ||||
| 	_, params, err := mime.ParseMediaType(res.Header.Get("Content-Disposition")) | ||||
| 	if err != nil { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user