diff --git a/internal/service.go b/internal/service.go index 4745e8b..02c738d 100644 --- a/internal/service.go +++ b/internal/service.go @@ -84,42 +84,81 @@ func (s *MusicAgregatorService) Close() { } func (service *MusicAgregatorService) MonitorAlbum(ctx context.Context, req *pb.MonitorAlbumRequest) (*pb.MonitorAlbumResponse, error) { - resp, err := service.metadataClient.GetAlbum(ctx, &metadataPb.GetAlbumRequest{ - Identifier: &metadataPb.GetAlbumRequest_Id{Id: req.GetAlbumId()}, - }) + album, err := service.fetchAlbumMetadata(ctx, req.GetAlbumId()) if err != nil { - log.Error().Err(err).Str("album_id", req.GetAlbumId()).Msg("metadata GetAlbum failed") return nil, err } + + searchResult, err := service.searchIndexer(album, req.GetIndexerOptions().GetTracker()) + if err != nil { + return nil, err + } + + parsed := service.parseSearchResults(searchResult, album) + + filtered := filterByQuality(parsed, req.GetQuality()) + if len(filtered) == 0 { + log.Warn().Str("album", album.GetTitle()).Str("quality", req.GetQuality().String()).Msg("no releases match quality filter") + return &pb.MonitorAlbumResponse{}, nil + } + + best := selectBestRelease(filtered) + + if err := service.addToTorrentClient(best); err != nil { + return nil, err + } + + return &pb.MonitorAlbumResponse{ + Release: buildMonitoredRelease(best), + }, nil +} + +func (service *MusicAgregatorService) fetchAlbumMetadata(ctx context.Context, albumID string) (*metadataPb.Album, error) { + resp, err := service.metadataClient.GetAlbum(ctx, &metadataPb.GetAlbumRequest{ + Identifier: &metadataPb.GetAlbumRequest_Id{Id: albumID}, + }) + if err != nil { + log.Error().Err(err).Str("album_id", albumID).Msg("metadata GetAlbum failed") + return nil, err + } + album := resp.GetAlbum() artistName := "" if len(album.GetArtists()) > 0 { artistName = album.GetArtists()[0].GetArtist().GetName() } - log.Debug(). - Str("album_id", req.GetAlbumId()). - Str("title", album.GetTitle()). - Str("artist", artistName). - Msg("album found, monitoring") + log.Debug().Str("album_id", albumID).Str("title", album.GetTitle()).Str("artist", artistName).Msg("album metadata fetched") + + return album, nil +} + +func (service *MusicAgregatorService) searchIndexer(album *metadataPb.Album, tracker string) (*indexer.SearchResponse, error) { + artistName := "" + if len(album.GetArtists()) > 0 { + artistName = album.GetArtists()[0].GetArtist().GetName() + } query := album.GetTitle() if artistName != "" { query = artistName + " " + query } - trackerName := req.GetIndexerOptions().GetTracker() - if trackerName == "" { - trackerName = "all" + if tracker == "" { + tracker = "all" } - searchResult, err := service.indexer.Search(query, -1, trackerName) + result, err := service.indexer.Search(query, -1, tracker) if err != nil { - log.Error().Err(err).Str("query", query).Msg("indexer search album failed") + log.Error().Err(err).Str("query", query).Msg("indexer search failed") return nil, err } - log.Debug().Int("results", len(searchResult.Items)).Str("query", query).Msg("indexer search completed") + log.Debug().Int("results", len(result.Items)).Str("query", query).Msg("indexer search completed") + return result, nil +} + +func (service *MusicAgregatorService) parseSearchResults(searchResult *indexer.SearchResponse, album *metadataPb.Album) []parsedItem { parser := torrentParser.NewGenericParser() var parsed []parsedItem @@ -134,27 +173,7 @@ func (service *MusicAgregatorService) MonitorAlbum(ctx context.Context, req *pb. continue } - var r *release.Release - var torrentData []byte - - if strings.HasPrefix(item.DownloadLink, "magnet:") { - log.Trace().Str("title", item.Title).Int("reported_seeders", item.Seeders).Msg("resolving magnet") - torrentData, err = service.magnetResolver.Resolve(item.DownloadLink) - if err != nil { - log.Warn().Err(err).Str("title", item.Title).Int("reported_seeders", item.Seeders).Msg("magnet resolve failed, falling back to title parse") - r = parser.Parse(item.Title) - } else { - r = parser.ParseTorrent(torrentData, album) - } - } else { - torrentData, err = downloadTorrentData(item.DownloadLink) - if err != nil { - log.Warn().Err(err).Str("title", item.Title).Msg("failed to download torrent, falling back to title parse") - r = parser.Parse(item.Title) - } else { - r = parser.ParseTorrent(torrentData, album) - } - } + r, torrentData := service.resolveRelease(parser, item, album) log.Debug(). Str("title", item.Title). @@ -169,28 +188,47 @@ func (service *MusicAgregatorService) MonitorAlbum(ctx context.Context, req *pb. } log.Debug().Int("total", len(searchResult.Items)).Int("parsed", len(parsed)).Msg("parsing complete") + return parsed +} - qualityType := req.GetQuality() +func (service *MusicAgregatorService) resolveRelease(parser *torrentParser.GenericParser, item *indexer.SearchItemResult, album *metadataPb.Album) (*release.Release, []byte) { + if strings.HasPrefix(item.DownloadLink, "magnet:") { + log.Trace().Str("title", item.Title).Int("reported_seeders", item.Seeders).Msg("resolving magnet") + torrentData, err := service.magnetResolver.Resolve(item.DownloadLink) + if err != nil { + log.Warn().Err(err).Str("title", item.Title).Int("reported_seeders", item.Seeders).Msg("magnet resolve failed, falling back to title parse") + return parser.Parse(item.Title), nil + } + return parser.ParseTorrent(torrentData, album), torrentData + } + + torrentData, err := downloadTorrentData(item.DownloadLink) + if err != nil { + log.Warn().Err(err).Str("title", item.Title).Msg("failed to download torrent, falling back to title parse") + return parser.Parse(item.Title), nil + } + return parser.ParseTorrent(torrentData, album), torrentData +} + +func filterByQuality(items []parsedItem, quality pb.QualityType) []parsedItem { var filtered []parsedItem - for _, p := range parsed { - match := qualityType == pb.QualityType_QUALITY_UNSPECIFIED || - (qualityType == pb.QualityType_QUALITY_LOSSLESS && p.rel.Format.IsLossless()) || - (qualityType == pb.QualityType_QUALITY_LOSSY && !p.rel.Format.IsLossless()) + for _, p := range items { + match := quality == pb.QualityType_QUALITY_UNSPECIFIED || + (quality == pb.QualityType_QUALITY_LOSSLESS && p.rel.Format.IsLossless()) || + (quality == pb.QualityType_QUALITY_LOSSY && !p.rel.Format.IsLossless()) if !match { - log.Debug().Str("title", p.item.Title).Str("format", p.rel.Format.String()).Str("wanted", qualityType.String()).Msg("filtered out by quality") + log.Debug().Str("title", p.item.Title).Str("format", p.rel.Format.String()).Str("wanted", quality.String()).Msg("filtered out by quality") continue } filtered = append(filtered, p) } + return filtered +} - if len(filtered) == 0 { - log.Warn().Str("query", query).Str("quality", qualityType.String()).Msg("no releases match quality filter") - return &pb.MonitorAlbumResponse{}, nil - } - - best := filtered[0] - for _, p := range filtered[1:] { +func selectBestRelease(items []parsedItem) parsedItem { + best := items[0] + for _, p := range items[1:] { if p.item.Seeders > best.item.Seeders { best = p } @@ -202,51 +240,41 @@ func (service *MusicAgregatorService) MonitorAlbum(ctx context.Context, req *pb. Int("seeders", best.item.Seeders). Str("tracker", best.item.Tracker). Str("hash", best.rel.InfoHash). - Msg("best release selected, adding to torrent client") + Msg("best release selected") + return best +} + +func (service *MusicAgregatorService) addToTorrentClient(best parsedItem) error { if best.rel.InfoHash != "" { existing, err := service.torrentClient.Find(torrent.FindOptions{Hash: best.rel.InfoHash}) if err == nil && len(existing) > 0 { - log.Info(). - Str("hash", best.rel.InfoHash). - Str("state", existing[0].State). - Str("artist", best.rel.Artist). - Str("album", best.rel.Album). - Str("format", best.rel.Format.String()). - Int("tracks", best.rel.TrackCount). - Msg("torrent already exists in client") - resp := buildMonitoredRelease(best) - log.Trace().Interface("response", resp).Msg("returning existing torrent response") - return &pb.MonitorAlbumResponse{ - Release: resp, - }, nil + log.Info().Str("hash", best.rel.InfoHash).Str("state", existing[0].State).Msg("torrent already exists in client") + return nil } } if strings.HasPrefix(best.item.DownloadLink, "magnet:") { if err := service.torrentClient.AddMagnet(best.item.DownloadLink); err != nil { log.Error().Err(err).Str("title", best.item.Title).Msg("failed to add magnet to client") - return nil, err + return err } } else { if len(best.torrentData) == 0 { log.Error().Str("title", best.item.Title).Msg("no torrent data available") - return nil, fmt.Errorf("no torrent data available for best release") + return fmt.Errorf("no torrent data available for best release") } if err := service.torrentClient.AddTorrent(torrent.TorrentFile{ Filename: best.rel.Album + ".torrent", Data: best.torrentData, }); err != nil { log.Error().Err(err).Str("title", best.item.Title).Msg("failed to add torrent to client") - return nil, err + return err } } log.Info().Str("title", best.item.Title).Str("hash", best.rel.InfoHash).Msg("torrent added to client") - - return &pb.MonitorAlbumResponse{ - Release: buildMonitoredRelease(best), - }, nil + return nil } func buildMonitoredRelease(p parsedItem) *pb.MonitoredRelease {