From 17ee4822a94c36be0e4478790910a61323dbfcbc Mon Sep 17 00:00:00 2001 From: Timon Ringwald Date: Tue, 23 Aug 2022 11:48:26 +0200 Subject: [PATCH] torrent download implemented --- download_torrent_file.go | 41 ++++++++++++++++++++++++++++++++++++++++ errors.go | 4 ++++ local_file_check.go | 35 +++++++++++++++++----------------- main.go | 17 ++++++++++------- 4 files changed, 72 insertions(+), 25 deletions(-) create mode 100644 download_torrent_file.go diff --git a/download_torrent_file.go b/download_torrent_file.go new file mode 100644 index 0000000..df869ae --- /dev/null +++ b/download_torrent_file.go @@ -0,0 +1,41 @@ +package main + +import ( + "fmt" + "io" + "net/http" + "os" + "path" + "path/filepath" + + "git.milar.in/nyaanime/model" +) + +func DownloadTorrent(animeEp model.AnimeEpisode, torrent *model.ParsedTorrent) error { + if err := SetAnimeEpDownloading(animeEp); err != nil { + return ErrLockFileCreationFailed.Wrap(err, animeEp.Anime.Title.Romaji, animeEp.Episode) + } + + torrentFilePath := filepath.Join(TorrentPath, path.Base(torrent.Torrent.Link)) + + fmt.Printf("download: %s -> %s\n", torrent.Torrent.Link, torrentFilePath) + + resp, err := http.Get(torrent.Torrent.Link) + if err != nil { + return ErrDownloadTorrentFileFailed.Wrap(err, torrent.Torrent.Link) + } + defer resp.Body.Close() + + file, err := os.Create(torrentFilePath) + if err != nil { + return ErrSaveTorrentFileFailed.Wrap(err, torrent.Torrent.Link) + } + defer file.Close() + + _, err = io.Copy(file, resp.Body) + if err != nil { + return ErrSaveTorrentFileFailed.Wrap(err, torrent.Torrent.Link) + } + + return nil +} diff --git a/errors.go b/errors.go index c39bb05..0af1dc3 100644 --- a/errors.go +++ b/errors.go @@ -14,4 +14,8 @@ var ( ErrInvalidAnimeStatus = adverr.NewErrTmpl("ErrInvalidAnimeStatus", "invalid status '%s' in ANIME_STATUS (allowed: %s)") ErrInvalidGlobSyntax = adverr.NewErrTmpl("ErrInvalidGlobSyntax", "invalid filepath.Glob syntax: '%s'") ErrNoSuitableFileFound = adverr.NewErrTmpl("ErrNoSuitableFileFound", "no file found with essential properties for anime '%s' and episode %d") + + ErrDownloadTorrentFileFailed = adverr.NewErrTmpl("ErrDownloadTorrentFileFailed", "torrent file download failed: %s") + ErrLockFileCreationFailed = adverr.NewErrTmpl("ErrLockFileCreationFailed", "creation of lock file for anime '%s' and episode %d failed") + ErrSaveTorrentFileFailed = adverr.NewErrTmpl("ErrSaveTorrentFileFailed", "torrent file could not be saved: %s") ) diff --git a/local_file_check.go b/local_file_check.go index 2feaa45..7fa0f15 100644 --- a/local_file_check.go +++ b/local_file_check.go @@ -1,7 +1,7 @@ package main import ( - "fmt" + "errors" "os" "path/filepath" "strings" @@ -27,11 +27,11 @@ func GetAnimeEpFilepath(animeEp model.AnimeEpisode, ext string) string { if err := AnimeEpFilepathPattern.Execute(b, tmplData); err != nil { panic(err) } - return b.String() + return filepath.Join(AnimePath, b.String()) } func AnimeEpExistsLocally(animeEp model.AnimeEpisode) bool { - animeEpPath := filepath.Join(AnimePath, GetAnimeEpFilepath(animeEp, "*")) + animeEpPath := GetAnimeEpFilepath(animeEp, "*") files, err := filepath.Glob(animeEpPath) if err != nil { @@ -42,7 +42,7 @@ func AnimeEpExistsLocally(animeEp model.AnimeEpisode) bool { } func GetLocalAnimeEpProperties(animeEp model.AnimeEpisode) (*FilePriority, bool, error) { - animeEpPath := filepath.Join(AnimePath, GetAnimeEpFilepath(animeEp, "*")) + animeEpPath := GetAnimeEpFilepath(animeEp, "*") files, err := filepath.Glob(animeEpPath) if err != nil { return nil, false, ErrInvalidGlobSyntax.Wrap(err, animeEpPath) @@ -70,18 +70,17 @@ func GetLocalAnimeEpProperties(animeEp model.AnimeEpisode) (*FilePriority, bool, return mostPrio, mostPrio != nil, nil } -// TODO cache? -func TorrentFileDownloading(torrent *model.ParsedTorrent) bool { - torrentFile := filepath.Join(TorrentPath, fmt.Sprintf("%s.torrent", torrent.Torrent.ID)) - - if _, err := os.Stat(torrentFile); err == nil { - return true - } - - addedTorrentFile := filepath.Join(TorrentPath, fmt.Sprintf("%s.torrent.added", torrent.Torrent.ID)) - if _, err := os.Stat(addedTorrentFile); err == nil { - return true - } - - return false +func IsAnimeEpDownloading(animeEp model.AnimeEpisode) bool { + animeEpPath := GetAnimeEpFilepath(animeEp, "lock") + _, err := os.Stat(animeEpPath) + return !errors.Is(err, os.ErrNotExist) +} + +func SetAnimeEpDownloading(animeEp model.AnimeEpisode) error { + animeEpPath := GetAnimeEpFilepath(animeEp, "lock") + file, err := os.Create(animeEpPath) + if err != nil { + defer file.Close() + } + return err } diff --git a/main.go b/main.go index d757bb7..323bbfe 100644 --- a/main.go +++ b/main.go @@ -71,6 +71,7 @@ func main() { fmt.Println("subtitle priorites:", Map2Str(PreferredSubtitles)) fmt.Println("resolution priorites:", Map2Str(PreferredResolutions)) + // TODO ugly code (see issue nyaanime/downloader#1) if len(AnimeStatus) == 0 { AnimeStatus = []anilist.MediaListStatus{ anilist.MediaListStatusCurrent, @@ -122,7 +123,9 @@ func checkTorrents() { } for _, torrentPriority := range torrentPriorities { - if TorrentFileDownloading(torrentPriority.ParsedTorrent) { + torrent := torrentPriority.ParsedTorrent + + if IsAnimeEpDownloading(animeEp) { fmt.Printf("%s | CURRENTLY DOWNLOADING\n", FormatTorrentPriority(torrentPriority)) continue } @@ -132,19 +135,19 @@ func checkTorrents() { fmt.Printf("%s | LOWER PRIORITY\n", FormatTorrentPriority(torrentPriority)) } else { fmt.Printf("%s | HIGHER PRIORITY | STARTING DOWNLOAD\n", FormatTorrentPriority(torrentPriority)) - // TODO start download + if err := DownloadTorrent(animeEp, torrent); err != nil { + panic(err) + } } } else { fmt.Printf("%s | NOT IN COLLECTION | STARTING DOWNLOAD\n", FormatTorrentPriority(torrentPriority)) - // TODO start download + if err := DownloadTorrent(animeEp, torrent); err != nil { + panic(err) + } } - - // TODO download anime episode with highest priority (first one in slice) } } - // TODO store preferred properties of downloaded torrents in db - duration := time.Since(start) fmt.Printf("\ncheck took %s. sleeping for %s\n", duration.Truncate(time.Millisecond), (PollRate - duration).Truncate(time.Millisecond)) }