0
0
Fork 0
mirror of https://gitea.plemya-x.ru/Plemya-x/ALR.git synced 2025-01-10 17:26:45 +00:00

Небольшие переводы и комментарии

This commit is contained in:
Евгений Храмов 2024-07-10 13:10:14 +03:00
parent 47d1d97496
commit 93e82b43a7
2 changed files with 69 additions and 79 deletions

View file

@ -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
}

View file

@ -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 {