diff --git a/internal/dl/dl.go b/internal/dl/dl.go
index 784d056..a2a6781 100644
--- a/internal/dl/dl.go
+++ b/internal/dl/dl.go
@@ -1,23 +1,23 @@
/*
- * ALR - Any Linux Repository
- * Copyright (C) 2024 Евгений Храмов
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
+* ALR - Any Linux Repository
+* Copyright (C) 2024 Евгений Храмов
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*/
-// 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 ""
}
-// 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
}
diff --git a/internal/dl/file.go b/internal/dl/file.go
index f3a80f5..a3da657 100644
--- a/internal/dl/file.go
+++ b/internal/dl/file.go
@@ -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 {
@@ -261,4 +279,4 @@ func getFilename(res *http.Response) (name string) {
} else {
return path.Base(res.Request.URL.Path)
}
-}
+}
\ No newline at end of file