Recently I've been working on Bevy, a self hosted alternative to Popcorn Time that doubles as an alternative to Sonarr/Radarr (torrent grabbing). I recently added a new system for searching for and grabbing releases which I thought was a huge improvement over existing systems.

diericx/bevy
Self hosted popcorn time alternative software for grabbing, streaming and seeding torrents - diericx/bevy

Brief Context: Motivation for Bevy

I've been using the Ombi+Sonarr+Radarr+Plex stack on my media server for a while and I've been left a bit dissatisfied (with everything except Plex, which is awesome). I hate that I have to have a completely different service setup for letting other people request media (Ombi).

I can solve these all in time, but here's my main gripe: I have great internet, but I am still bothered by having to go request a movie, wait 5-20 minutes for a torrent to download before I can start streaming it and then have to go to another platform to stream it.

Popcorn Time (PT) solves this streaming-while-downloading problem, but I don't like the leech-only ethos behind the app (PT leeches from public trackers and drops the file as soon as it can). This prohibits us from being able to use private trackers with PT which require seeding for x amount of time or a specific ratio.

I want the ability to download torrents from private trackers and let them seed until a specific condition is met, thus allowing the use of private trackers. This is also a more sustainable way to use torrents!

This stream-while-downloading problem isn't huge, but it totally bothers me and is something that I can solve right now.

Bevy aims to solve all these problems by providing a highly customizable grabbing system, streaming-will-downloading and live transcoding (which means watching and chrome casting from the app works!). Currently it seeds forever (or until manually removed), but I will be adding automated removal soon.

My gripes with the grabbing system in Sonarr and Radarr

This post is focusing on the torrent grabbing system for Bevy, so I'd like to detail my gripes with the way Sonarr and Radarr handle this (my inspiration).

Sonarr and Radarr are ok for listening to RSS feeds and grabbing things as they come out, but I find them very opaque and annoying for grabbing old content. I'm often left with missing content and have to manually grab the movies and shows because they were right oustide of some setting (like file size).

They both work by first having the user set up patterns for detecting a quality from a torrent name which are connected to file size limits. When a list of torrents are found, they are sorted into qualities via name, anything outside the size limits is removed, then sorted by quality. It then goes down and attempts to add them based on these scores.

I'm not entirely sure how this scoring works, and I'm never confident I really know what it will do when it begins searching for a release. I don't feel like I have a lot of control over this process. This often leads me to scrolling through debug logs trying to understand what it's doing so I can update my settings accordingly... very annoying.

Using expressions in our config to solve this problem

In Bevy, each release has a few attributes (more can be added easily if requested) that can be used in an expression to calculate a score: seeder count, size and quality.

Note: You specify qualities in a config and each one has a regex pattern that it tries to match with torrent names. In these equations you are getting the index of that matching quality, higher the value the higher the quality.

To utilize these, you set up qualities which work very similar to the method I described above for Sonarr and Radarr. Each quality has a pattern that it matches on the torrent name, some hard limits, and more importantly scoring expressions.

Here's an example config.

[torrent_fetcher]
quality_score_expr = "quality * 0.35"

	[[torrent_fetcher.qualities]]
	regex = "720"
	seeder_score_expr = "1 / (1 + e**(-0.2*seeders+5))"
	size_score_expr = "1 / ( 10**(-16) * (sizeMB-15000)**4 + 1)"
config.toml

Here we've setup a quality scoring expression for the fetcher to score each quality, and the quality has a regex field specifying the pattern to match, and a seeder_score_expr and size_score_expr for scoring seeder counts and size.

Each quality specifies it's own expressions for seeder count and score so you can, for example, optimize more tightly for higher seeders on larger files or optimize for larger files for higher qualities.

You have complete freedom when working on these expressions. You can decide your range for each one which gives you ultimate freedom on deciding how you want to prioritize torrents. Below are my example expressions visualized. They are by no means perfect, but they get the job done.

I try to keep each expression's output in the range of [0,1] so a perfect torrent will have a score of 3. Looking at them from left to right, the ideal torrents for me are 15GB files (15000 MB), generally as many seeders as possible but around 20 is fine (benefit of anything higher decreases exponentially), and higher qualities get a point each.

Here's what this looks like in the Bevy dashboard. Note that I only have one quality here (720) so there is no relevant quality score.

These are sorted by Total Score, and as you can see the top result here is a torrent that is 8GB, has 30 seeders and is 720p. Nice! It could be better, but I've only added one tracker for testing and these functions aren't exactly refined yet.

Mission accomplished!

All in all, I think this is a much better system than we had before! It's much easier to debug and there is so much more control... but there is also a lot of room for improvement.

I'll be actively working on this project, so message me or open an issue on Github if you have any suggestions!