package indexer import ( "encoding/xml" "strconv" "github.com/rs/zerolog/log" pb "homelab.lan/music-agregator/gen/music_agregator/indexer/v1" "homelab.lan/music-agregator/internal/release" "homelab.lan/music-agregator/internal/tracker" ) type SearchResult struct { XMLName xml.Name `xml:"rss"` Items []Item `xml:"channel>item"` } type JackettIndexer struct { ID string `xml:"id,attr"` } type Item struct { Title string `xml:"title"` Link string `xml:"link"` Guid string `xml:"guid"` PubDate string `xml:"pubDate"` Size int64 `xml:"size"` Description string `xml:"description"` Categories []string `xml:"category"` Enclosure Enclosure `xml:"enclosure"` TorznabAttrs []TorznabAttr `xml:"attr"` JackettIndexer JackettIndexer `xml:"jackettindexer"` } type Enclosure struct { URL string `xml:"url,attr"` Length int64 `xml:"length,attr"` Type string `xml:"type,attr"` } type TorznabAttr struct { Name string `xml:"name,attr"` Value string `xml:"value,attr"` } type SearchItemResult struct { Title string DownloadLink string TorrentPageUrl string PubDate string Size int64 Description string Categories []string Tracker string Seeders int Peers int Attrs map[string]string Release *release.Release } func (si *SearchItemResult) ToProto() *pb.SearchItem { var pbAttrs []*pb.TorznabAttr for k, v := range si.Attrs { pbAttrs = append(pbAttrs, &pb.TorznabAttr{Name: k, Value: v}) } return &pb.SearchItem{ Title: si.Title, DownloadLink: si.DownloadLink, TorrentPageUrl: si.TorrentPageUrl, PubDate: si.PubDate, Size: si.Size, Description: si.Description, Categories: si.Categories, TorznabAttrs: pbAttrs, Release: si.Release.ToProto(), } } type SearchResponse struct { Items []*SearchItemResult } func (sr *SearchResponse) ToProto() *pb.SearchResponse { pbItems := make([]*pb.SearchItem, len(sr.Items)) for i, item := range sr.Items { pbItems[i] = item.ToProto() } return &pb.SearchResponse{Result: pbItems} } var genericParser = tracker.NewGenericParser() func (sr *SearchResult) ToSearchResponse() *SearchResponse { var items []*SearchItemResult for _, item := range sr.Items { rel := genericParser.Parse(item.Title) log.Trace(). Str("tracker", item.JackettIndexer.ID). Str("title", item.Title). Str("artist", rel.Artist). Str("album", rel.Album). Int("year", rel.Year). Bool("parsed", rel.ParsedSuccessfully). Msg("parsed item") attrs := make(map[string]string, len(item.TorznabAttrs)) for _, attr := range item.TorznabAttrs { attrs[attr.Name] = attr.Value } seeders, _ := strconv.Atoi(attrs["seeders"]) peers, _ := strconv.Atoi(attrs["peers"]) items = append(items, &SearchItemResult{ Title: item.Title, DownloadLink: item.Link, TorrentPageUrl: item.Guid, PubDate: item.PubDate, Size: item.Size, Description: item.Description, Categories: item.Categories, Tracker: item.JackettIndexer.ID, Seeders: seeders, Peers: peers, Attrs: attrs, Release: rel, }) } log.Trace(). Int("total", len(sr.Items)). Int("items", len(items)). Msg("conversion complete") return &SearchResponse{Items: items} }