Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[enhancement]: support Torznab o=JSON #15036

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

BorisGerretzen
Copy link

Description

Aims to add support for the o parameter as described here, the examples in the standard are only listed for XML, the JSON structure in this PR resembles the XML as closely as possible.

If the parameter is omitted or is not equal to json, XML is assumed.

This is my first PR here, if anything looks wrong please let me know and I'll fix it.

Issues Fixed or Closed by this PR

Copy link
Contributor

@garfield69 garfield69 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you.
its a great start, but I think it still needs more refinement ;-)
compare the t=caps for example:
http://127.0.0.1:9117/api/v2.0/indexers/blutopia-api/results/torznab/api?t=caps&apikey=redacted&o=xml

<?xml version="1.0" encoding="UTF-8"?>
<caps>
  <server title="Jackett" />
  <limits default="100" max="100" />
  <searching>
    <search available="yes" supportedParams="q" />
    <tv-search available="yes" supportedParams="q,season,ep,tvdbid,tmdbid" />
    <movie-search available="yes" supportedParams="q,imdbid,tmdbid" />
    <music-search available="no" supportedParams="q" />
    <audio-search available="no" supportedParams="q" />
    <book-search available="no" supportedParams="q" />
  </searching>
  <categories>
    <category id="2000" name="Movies">
      <subcat id="2020" name="Movies/Other" />
    </category>
    <category id="5000" name="TV" />
    <category id="100003" name="FANRES" />
    <category id="100001" name="Movie" />
    <category id="100005" name="Trailer" />
    <category id="100002" name="TV Show" />
  </categories>
</caps>

http://127.0.0.1:9117/api/v2.0/indexers/blutopia-api/results/torznab/api?t=caps&apikey=redacted&o=json

{"TvSearchParams":[0,1,2,3,4,6],"MovieSearchParams":[0,1,2],"MusicSearchParams":[],"BookSearchParams":[],"Categories":{},"LimitsMax":100,"LimitsDefault":100,"SearchAvailable":true,"SupportsRawSearch":false,"TvSearchAvailable":true,"TvSearchSeasonAvailable":true,"TvSearchEpAvailable":true,"TvSearchImdbAvailable":false,"TvSearchTvdbAvailable":true,"TvSearchTvRageAvailable":false,"TvSearchTmdbAvailable":true,"TvSearchTvMazeAvailable":false,"TvSearchTraktAvailable":false,"TvSearchDoubanAvailable":false,"TvSearchYearAvailable":false,"TvSearchGenreAvailable":false,"MovieSearchAvailable":true,"MovieSearchImdbAvailable":true,"MovieSearchTmdbAvailable":true,"MovieSearchTraktAvailable":false,"MovieSearchDoubanAvailable":false,"MovieSearchYearAvailable":false,"MovieSearchGenreAvailable":false,"MusicSearchAvailable":false,"MusicSearchAlbumAvailable":false,"MusicSearchArtistAvailable":false,"MusicSearchLabelAvailable":false,"MusicSearchTrackAvailable":false,"MusicSearchYearAvailable":false,"MusicSearchGenreAvailable":false,"BookSearchAvailable":false,"BookSearchTitleAvailable":false,"BookSearchAuthorAvailable":false,"BookSearchPublisherAvailable":false,"BookSearchYearAvailable":false,"BookSearchGenreAvailable":false}

looks to me like its not quite the same information? the search params [0,1,...] and the categories set is empty ?

a query for imdbid search looks much better in terms of content comparison
http://127.0.0.1:9117/api/v2.0/indexers/blutopia-api/results/torznab/api?t=movie&imdbid=tt27592269&apikey=redacted&o=xml

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:torznab="http://torznab.com/schemas/2015/feed">
  <channel>
    <atom:link href="http://127.0.0.1:9117/" rel="self" type="application/rss+xml" />
    <title>Blutopia (API)</title>
    <description>Blutopia (BLU) is a Private Torrent Tracker for HD MOVIES / TV</description>
    <link>https://blutopia.cc/</link>
    <language>en-US</language>
    <category>search</category>
    <item>
      <title>Cool Cat vs Dirty Dog 'The Virus Wars' 2023 1080p VIMEO WEB-DL AAC 2.0 H.264-CONSORTiUM</title>
      <guid>https://blutopia.cc/torrent/download/redacted</guid>
      <jackettindexer id="blutopia-api">Blutopia (API)</jackettindexer>
      <type>private</type>
      <comments>https://blutopia.cc/torrents/redacted</comments>
      <pubDate>Sun, 04 Feb 2024 15:54:23 +1300</pubDate>
      <size>2546028800</size>
      <files>1</files>
      <grabs>19</grabs>
      <description>Action,Comedy,Family</description>
      <link>http://127.0.0.1:9117/dl/blutopia-api/?jackett_apikey=redacted&amp;path=Q2ZESjhIT2pVX2NpZV90QW1MbjJwb0RsNXNYdnRCR1VUQWVnRUQ3SXBMc1Q2aFh0a2s3RDRfR3pHTjRGMkZObGN3U2dydlM2d2taSE9kQVV3bk03UTVQSzhfM1FPTmVvTE5VWFJtZHhwdjc1ckp4TnVFVy1NcjQyZWs5Uk5BcXBvYlR4TS01RDRpWm10LXRkOEp4SDJLOHhDMHRFR1pOcEJSN0IyVkNjdDRfazYxVEQ4OE5jaWtLWDhqR2cwd3kxd0ktT3JlRXRIWTMwUU9EV3lYS1FHVzJuNFRr&amp;file=Cool+Cat+vs+Dirty+Dog+%27The+Virus+Wars%27+2023+1080p+VIMEO+WEB-DL+AAC+2.0+H.264-CONSORTiUM</link>
      <category>2000</category>
      <category>100001</category>
      <enclosure url="http://127.0.0.1:9117/dl/blutopia-api/?jackett_apikey=redacted&amp;path=Q2ZESjhIT2pVX2NpZV90QW1MbjJwb0RsNXNYdnRCR1VUQWVnRUQ3SXBMc1Q2aFh0a2s3RDRfR3pHTjRGMkZObGN3U2dydlM2d2taSE9kQVV3bk03UTVQSzhfM1FPTmVvTE5VWFJtZHhwdjc1ckp4TnVFVy1NcjQyZWs5Uk5BcXBvYlR4TS01RDRpWm10LXRkOEp4SDJLOHhDMHRFR1pOcEJSN0IyVkNjdDRfazYxVEQ4OE5jaWtLWDhqR2cwd3kxd0ktT3JlRXRIWTMwUU9EV3lYS1FHVzJuNFRr&amp;file=Cool+Cat+vs+Dirty+Dog+%27The+Virus+Wars%27+2023+1080p+VIMEO+WEB-DL+AAC+2.0+H.264-CONSORTiUM" length="2546028800" type="application/x-bittorrent" />
      <torznab:attr name="category" value="2000" />
      <torznab:attr name="category" value="100001" />
      <torznab:attr name="tvdbid" value="0" />
      <torznab:attr name="imdb" value="27592269" />
      <torznab:attr name="imdbid" value="tt27592269" />
      <torznab:attr name="tmdbid" value="1206047" />
      <torznab:attr name="genre" value="Action, Comedy, Family" />
      <torznab:attr name="seeders" value="18" />
      <torznab:attr name="peers" value="20" />
      <torznab:attr name="coverurl" value="http://127.0.0.1:9117/img/blutopia-api/?jackett_apikey=redacted&amp;path=Q2ZESjhIT2pVX2NpZV90QW1MbjJwb0RsNXNWSWN1N3lzVF9PN2JqYnhINlFodXpwb3Bjb2NvcTU5MXVYLWJ2SVctTU4tY1FwMmhRMEtrVkJfU1JldlkyeldZZi1fZnF4RGhObnlMWG5VdW5IVExybVVnMXNzMGdFZ01OUHJPc09ZSzhodHVldklWN2dNWXRZdXp0cGV4RlREYklvbFllRDVhdTFYNENpRGNmeG0xS0U3QUtRa2lVcHp0QlBNX3VqQktzeVRR&amp;file=poster" />
      <torznab:attr name="infohash" value="1112c04ce22cc8fda67c42860e15a40c1342ca46" />
      <torznab:attr name="minimumseedtime" value="604800" />
      <torznab:attr name="downloadvolumefactor" value="0" />
      <torznab:attr name="uploadvolumefactor" value="1" />
    </item>
  </channel>
</rss>

http://127.0.0.1:9117/api/v2.0/indexers/blutopia-api/results/torznab/api?t=movie&imdbid=tt27592269&apikey=redacted&o=json

{"Releases":[{"Title":"Cool Cat vs Dirty Dog 'The Virus Wars' 2023 1080p VIMEO WEB-DL AAC 2.0 H.264-CONSORTiUM","Guid":"https://blutopia.cc/torrent/download/redacted","Link":"http://127.0.0.1:9117/dl/blutopia-api/?jackett_apikey=redacted&path=Q2ZESjhIT2pVX2NpZV90QW1MbjJwb0RsNXNVNnJiUXI0ZVh0cmZyNHd2WG93RVBLVnJ5Y3ZIcjNCaUNjakJyOVZhWmxTUG1nWDFsYW9URVhHaW9XUEt1cEE3NDFkRkxaLWhCMG1RcFRESnhjZXgybmRIR1JWSzhBakZHN09JU3k0VGlYYkU4NUxTOXhPdEJjTkxlMkJIZjBOU0pZYndGd2ZFbUt3aUFJa2lwb1JCb0RWclZpZHhZQTVVdE5qY1BfT2tiU2ExWVpqeWJ1elJVR0xSOEhaYlVIc1Zv&file=Cool+Cat+vs+Dirty+Dog+%27The+Virus+Wars%27+2023+1080p+VIMEO+WEB-DL+AAC+2.0+H.264-CONSORTiUM","Details":"https://blutopia.cc/torrents/redacted","PublishDate":"2024-02-04T15:54:23+13:00","Category":[2000,100001],"Size":2546028800,"Files":1,"Grabs":19,"Description":"Action,Comedy,Family","RageID":null,"TVDBId":0,"Imdb":27592269,"TMDb":1206047,"TVMazeId":null,"TraktId":null,"DoubanId":null,"Genres":["Action","Comedy","Family"],"Languages":[],"Subs":[],"Year":null,"Author":null,"BookTitle":null,"Publisher":null,"Artist":null,"Album":null,"Label":null,"Track":null,"Seeders":18,"Peers":20,"Poster":"http://127.0.0.1:9117/img/blutopia-api/?jackett_apikey=redacted&path=Q2ZESjhIT2pVX2NpZV90QW1MbjJwb0RsNXNXMmpJZkl3YzZHc1ZOamRSNV9mdmdkRFoxMjRKZllCcGZfTExMTUE2UGNBdGVyNkYzLU1ZQ1c4UlRnX1B1bWlMTmo3eXZYX3ExX0VUc1hlejBackJlaWYzNVV1ZEpiNTB2NDBjb1BIRDY5OHZmRndET1JkX3lNM3hBakZmdmloTkFRXzF5Nk04ak5kZGwzVUFsRmNZS2dpSjV0TkoyZzVtN1Y0LXZJUmlCUzRB&file=poster","InfoHash":"1112c04ce22cc8fda67c42860e15a40c1342ca46","MagnetUri":null,"MinimumRatio":null,"MinimumSeedTime":604800,"DownloadVolumeFactor":0.0,"UploadVolumeFactor":1.0,"Gain":42.68113374710083}]}

Overall, I think the json response should be prettified so its a little more readable than the default condensed output.
also think there should be a header block containing data similar to the xml, things like the jackett server, and which version of the json specs this is (in this case json version 1.0 ;-), and the indexer details
finally I think any keyword that has an output of null or a empty set [] should not be reported, as is the case in the xml output

But before you rush in to make changes, please wait for the other team members input, in case they make other suggestions or counter mine ;-)
Thanks.

@BorisGerretzen
Copy link
Author

compare the t=caps for example

Yup that indeed looks messed up. After taking a closer look the XML structure is custom built, this would also need to be done for json. The information is the same (except for the missing categories), for example the [0, 1, ...] is the integer representation of the enum values q, season, etc.

Overall, I think the json response should be prettified so its a little more readable than the default condensed output.

I would keep it minified by default, almost all of the time the response would be read by a machine and keeping it minified saves size. And if you're making the request through Postman or Bruno the output will be prettified in the response window.

also think there should be a header block containing data similar to the xml, things like the jackett server, and which version of the json specs this is (in this case json version 1.0 ;-), and the indexer details

I agree

finally I think any keyword that has an output of null or a empty set [] should not be reported, as is the case in the xml output

Also makes sense

Copy link
Contributor

@ilike2burnthing ilike2burnthing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All sounds good so far. Just wait Bogdan to take a look and then you should be good to go.

@mynameisbogdan mynameisbogdan added the Don't Merge Hold up - don't merge this label Feb 5, 2024
@mynameisbogdan
Copy link
Contributor

Hello @BorisGerretzen, thank you for your contribution.

Sadly now it's late here so I can't provide any concrete feedback, but I'll give it a try.

Looking at your examples this doesn't really respects the Tornzab spec per se but we can improve on that. I was thinking at a resource mapper here with ToJson() and ToXml(), and with a model for the JSON resource with custom property names.

This is what I had in mind:

{
  "@attributes": {
    "version": "2.0"
  },
  "channel": {
    "item": [
      {
        "title": "...",
        "guid": "...",
        "link": "...",
        "comments": "...",
        "pubDate": "Mon, 1 Feb 2024 12:34:56 +0000",
        "size": "1",
        "files": "1",
        "grabs": "1",
        "category": [
          "5000",
          "5040"
        ],
        "description": "...",
        "enclosure": {
          "@attributes": {
            "url": "....",
            "length": "1",
            "type": "application/x-bittorrent"
          }
        },
        "torznab:size": "1",
        "torznab:poster": "",
        "torznab:seeders": "1",
        "torznab:leechers": "1",
        "torznab:peers": "1",
        "torznab:files": "1",
        "torznab:grabs": "1",
        "torznab:infohash": "...",
        "torznab:downloadvolumefactor": "1",
        "torznab:uploadvolumefactor": "1",
        "torznab:category": [
          "5000",
          "5040"
        ],
        "torznab:imdbid": 0,
        "torznab:tvdbid": 0,
        "torznab:tvmazeid": 1
      }
    ]
  }
}

@BorisGerretzen
Copy link
Author

BorisGerretzen commented Feb 8, 2024

@mynameisbogdan @garfield69

I made some changes for the serialization of caps, it looks much more like the original XML now. It uses the GetXDocument function already present and converts this to json, that way we don't need to make this structure twice.

We can do the same for the regular output. Let me know what you think.

Also, is there a way to run this linter before committing?

@ilike2burnthing
Copy link
Contributor

Jackett/azure-pipelines.yml

Lines 314 to 349 in 96eeff5

- stage: CodeStyle
displayName: Code Style Compliance
dependsOn: []
jobs:
- job: Linting_Dotnet
displayName: Linting Dotnet
pool:
vmImage: ubuntu-22.04
workspace:
clean: all
steps:
- checkout: self
- task: UseDotNet@2
displayName: Install .NET Core SDK
inputs:
packageType: sdk
version: $(netCoreSdkVersion)
installationPath: $(Agent.ToolsDirectory)/dotnet
- task: DotNetCoreCLI@2
displayName: Install Dotnet Format
inputs:
command: custom
custom: tool
arguments: update -g dotnet-format
- task: Bash@3
displayName: Lint Dotnet
inputs:
workingDirectory: $(Build.SourcesDirectory)
targetType: inline
failOnStderr: true
# execute this command to format all files:
# dotnet-format --fix-whitespace --verbosity diagnostic --folder ./src
script: dotnet-format --check --verbosity diagnostic --folder ./src

@mynameisbogdan
Copy link
Contributor

Not what I had in mind.

@BorisGerretzen
Copy link
Author

@mynameisbogdan, I know, but otherwise you have to maintain two versions of the same structure.

@mynameisbogdan
Copy link
Contributor

Sadly we must, so this is a nay from me so far.

Sorry about the merge rebase.

@BorisGerretzen
Copy link
Author

BorisGerretzen commented Feb 8, 2024

That's fine, I will rewrite it.
The idea is then to have the same as below but instead of XDocument it's JObject? Or make an actual class out of this so it can be used for both XML and JSON? The same question for the regular results.

public XDocument GetXDocument()
{
var xdoc = new XDocument(
new XDeclaration("1.0", "UTF-8", null),
new XElement("caps",
new XElement("server",
new XAttribute("title", "Jackett")
),
LimitsMax != null || LimitsDefault != null ?
new XElement("limits",
LimitsDefault != null ? new XAttribute("default", LimitsDefault) : null,
LimitsMax != null ? new XAttribute("max", LimitsMax) : null
)
: null,
new XElement("searching",
new XElement("search",
new XAttribute("available", SearchAvailable ? "yes" : "no"),
new XAttribute("supportedParams", "q"),
SupportsRawSearch ? new XAttribute("searchEngine", "raw") : null
),
new XElement("tv-search",
new XAttribute("available", TvSearchAvailable ? "yes" : "no"),
new XAttribute("supportedParams", SupportedTvSearchParams()),
SupportsRawSearch ? new XAttribute("searchEngine", "raw") : null
),
new XElement("movie-search",
new XAttribute("available", MovieSearchAvailable ? "yes" : "no"),
new XAttribute("supportedParams", SupportedMovieSearchParams()),
SupportsRawSearch ? new XAttribute("searchEngine", "raw") : null
),
new XElement("music-search",
new XAttribute("available", MusicSearchAvailable ? "yes" : "no"),
new XAttribute("supportedParams", SupportedMusicSearchParams()),
SupportsRawSearch ? new XAttribute("searchEngine", "raw") : null
),
// inconsistent but apparently already used by various newznab indexers (see #1896)
new XElement("audio-search",
new XAttribute("available", MusicSearchAvailable ? "yes" : "no"),
new XAttribute("supportedParams", SupportedMusicSearchParams()),
SupportsRawSearch ? new XAttribute("searchEngine", "raw") : null
),
new XElement("book-search",
new XAttribute("available", BookSearchAvailable ? "yes" : "no"),
new XAttribute("supportedParams", SupportedBookSearchParams()),
SupportsRawSearch ? new XAttribute("searchEngine", "raw") : null
)
),
new XElement("categories",
from c in Categories.GetTorznabCategoryTree(true)
select new XElement("category",
new XAttribute("id", c.ID),
new XAttribute("name", c.Name),
from sc in c.SubCategories
select new XElement("subcat",
new XAttribute("id", sc.ID),
new XAttribute("name", sc.Name)
)
)
)
)
);
return xdoc;
}

@mynameisbogdan
Copy link
Contributor

t=caps

{
  "server": {
    "@attributes": {
      "version": "1.3",
      "title": "...",
      "strapline": "...",
      "url": "...",
      "image": "..."
    }
  },
  "limits": {
    "@attributes": {
      "max": "100",
      "default": "25"
    }
  },
  "searching": {
    "search": {
      "@attributes": {
        "available": "yes",
        "supportedParams": "q,imdbid,tvdbid,tvmazeid,tag"
      }
    },
    "tv-search": {
      "@attributes": {
        "available": "yes",
        "supportedParams": "q,tvdbid,season,ep,imdbid,tmdbid,tvmazeid,tag"
      }
    },
    "movie-search": {
      "@attributes": {
        "available": "yes",
        "supportedParams": "q,imdbid,tmdbid,tag"
      }
    }
  },
  "categories": {
    "category": [
      {
        "@attributes": {
          "id": "2000",
          "name": "Movies"
        },
        "subcat": [
          {
            "@attributes": {
              "id": "2030",
              "name": "Movies\/SD"
            }
          },
          {
            "@attributes": {
              "id": "2040",
              "name": "Movies\/HD"
            }
          },
          {
            "@attributes": {
              "id": "2045",
              "name": "Movies\/UHD"
            }
          },
          {
            "@attributes": {
              "id": "2050",
              "name": "Movies\/BluRay"
            }
          }
        ]
      },
      {
        "@attributes": {
          "id": "5000",
          "name": "TV"
        },
        "subcat": [
          {
            "@attributes": {
              "id": "5030",
              "name": "TV\/SD"
            }
          },
          {
            "@attributes": {
              "id": "5040",
              "name": "TV\/HD"
            }
          },
          {
            "@attributes": {
              "id": "5045",
              "name": "TV\/UHD"
            }
          },
          {
            "@attributes": {
              "id": "5060",
              "name": "TV\/Sport"
            }
          }
        ]
      }
    ]
  },
  "tags": {
    "tag": [
      {
        "@attributes": {
          "name": "scene",
          "description": "Torrent release is from a scene group"
        }
      },
      {
        "@attributes": {
          "name": "internal",
          "description": "Torrent release is internal"
        }
      }
    ]
  }
}

@BorisGerretzen
Copy link
Author

BorisGerretzen commented Feb 8, 2024

Right so why wrap all the things in @attributes, is that just so it can be converted to/from xml? I don't see why that is necessary. The current implementation is the same but without the @attributes.

@mynameisbogdan
Copy link
Contributor

Because this is the next best thing to emulate the Torznab XML in JSON.

@mynameisbogdan mynameisbogdan removed their request for review February 8, 2024 20:02
@BorisGerretzen
Copy link
Author

BorisGerretzen commented Feb 8, 2024

But when using the Json output it doesn't matter if the property I access is an XML element or an attribute. If someone wants that distinction they can use the xml directly right?

If you really want that distinction I propose using what Newtonsoft does by default, which is prefixing the attributes with an @. That way we can just convert the already existing XML output.

@mynameisbogdan
Copy link
Contributor

I didn't find anything to show us exactly how the JSON response should be. But do as you seem fit, but that's just a simple API endpoint that supports search.

@BorisGerretzen
Copy link
Author

BorisGerretzen commented Mar 10, 2024

First off all my apologies for the long delay on this, I was busy with other things.

@mynameisbogdan I did some further investigation on how exactly the format should look like, I couldn't find examples in the Torznab standard either so I looked at one of my Newznab indexers to see how they formatted it. It turns out they use the format you described earlier, with the @attributes. I have implemented a converter from XML to JSON following this format with some accompanying tests to demonstrate it's use.

I can send you some examples of Newznab JSON output to compare if you'd like.

Some examples of the output currently:
Caps:

{
  "server": {
    "@attributes": {
      "title": "Jackett"
    }
  },
  "limits": {
    "@attributes": {
      "default": "1000",
      "max": "1000"
    }
  },
  "searching": {
    "search": {
      "@attributes": {
        "available": "yes",
        "supportedParams": "q"
      }
    },
    "tv-search": {
      "@attributes": {
        "available": "yes",
        "supportedParams": "q,season,ep"
      }
    },
    "movie-search": {
      "@attributes": {
        "available": "yes",
        "supportedParams": "q"
      }
    }
  },
  "categories": {
    "category": [
      {
        "@attributes": {
          "id": "2000",
          "name": "Movies"
        }
      },
      {
        "@attributes": {
          "id": "3000",
          "name": "Audio"
        },
        "subcat": [
          {
            "@attributes": {
              "id": "3010",
              "name": "Audio/MP3"
            }
          },
          {
            "@attributes": {
              "id": "3040",
              "name": "Audio/Lossless"
            }
          }
        ]
      }
    ]
  }
}

search:
Not sure what to set atom:link.type to, application/rss+xml seems wrong cause it's json now, but I'm not sure if application/rss+json is a thing. The Newznab indexer I looked at does not have this field.

{
  "@attributes": {
    "version": "2.0",
    "xmlns:atom": "http://www.w3.org/2005/Atom",
    "xmlns:torznab": "http://torznab.com/schemas/2015/feed"
  },
  "channel": {
    "atom:link": {
      "@attributes": {
        "href": "http://127.0.0.1:9117/",
        "rel": "self",
        "type": "application/rss+xml"
      }
    },
    "title": "AggregateSearch",
    "description": "This feed includes all configured trackers",
    "link": "http://127.0.0.1/",
    "language": "en-US",
    "category": "search",
    "item": [
      {
        "title": "...",
        "guid": "...",
        "jackettindexer": "BitSearch",
        "type": "public",
        "comments": "...",
        "pubDate": "Sat, 09 Dec 2023 00:00:00 +0100",
        "size": "1610612736",
        "grabs": "1200",
        "description": {},
        "link": "...",
        "category": "5000",
        "enclosure": {
          "@attributes": {
            "url": "...",
            "length": "1610612736",
            "type": "application/x-bittorrent"
          }
        },
        "torznab:attr": [
          {
            "@attributes": {
              "name": "category",
              "value": "5000"
            }
          },
          {
            "@attributes": {
              "name": "genre",
              "value": ""
            }
          },
          {
            "@attributes": {
              "name": "seeders",
              "value": "84"
            }
          },
          {
            "@attributes": {
              "name": "peers",
              "value": "165"
            }
          },
          {
            "@attributes": {
              "name": "infohash",
              "value": "..."
            }
          },
          {
            "@attributes": {
              "name": "magneturl",
              "value": "..."
            }
          },
          {
            "@attributes": {
              "name": "downloadvolumefactor",
              "value": "0"
            }
          },
          {
            "@attributes": {
              "name": "uploadvolumefactor",
              "value": "1"
            }
          }
        ]
      }
    ]
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Don't Merge Hold up - don't merge this
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[enhancement]: support Torznab o=JSON
4 participants