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

Run a full sync every 19 minutes #165

Merged
merged 3 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ Sync plex watchlists in realtime with Sonarr and Radarr.

There are several ways of fetching watchlist information from Plex

| Method | Pros | Cons | Supported by Watchlistarr | Supported by Overseer/Ombi | Supported by Sonarr/Radarr |
|-----------------------------------------|-------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|---------------------------|------------------------------|----------------------------------------|
| Plex Watchlist RSS Feeds | Fast and instantly updated, can create one for self and one for friends | Will only show the most recent 50 movies/shows | Yes | X | Only for self, refreshes every 6 hours |
| Method | Pros | Cons | Supported by Watchlistarr | Supported by Overseer/Ombi | Supported by Sonarr/Radarr |
|-----------------------------------------|-------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|---------------------------|------------------------------|----------------------------------------|
| Plex Watchlist RSS Feeds | Fast and instantly updated, can create one for self and one for friends | Will only show the most recent 50 movies/shows, currently broken | Yes | X | Only for self, refreshes every 6 hours |
| Plex Watchlist Metadata | Can fetch the full watchlist for the user | Need a plex token for each user who wants to have their watchlist synced, Plex token expires after a few months | Yes | Yes, up to ~20 min intervals | X |
| Plex Watchlist GraphQL Call | Can fetch the full watchlist for every friend of the main user | Slow, relies on main user providing a plex token | Yes | X | X |
| Plex Watchlist Metadata for other users | Can fetch the full watchlist for every user who provides a token | Difficult to ask every user to generate a Plex token, or log into a service | Yes | Yes, up to ~20 min intervals | X |
| Plex Watchlist GraphQL Call | Can fetch the full watchlist for every friend of the main user | Slow, relies on main user providing a plex token | Yes | X | X |
| Plex Watchlist Metadata for other users | Can fetch the full watchlist for every user who provides a token | Difficult to ask every user to generate a Plex token, or log into a service | Yes | Yes, up to ~20 min intervals | X |

In order to fully integrate with Plex Watchlists, Watchlistarr uses a combination of multiple methods to ensure that it
does a comprehensive, yet fast real-time sync.
Expand Down
12 changes: 5 additions & 7 deletions src/main/scala/PlexTokenSync.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,25 @@ object PlexTokenSync extends PlexUtils with SonarrUtils with RadarrUtils {

private val logger = LoggerFactory.getLogger(getClass)

def run(config: Configuration, client: HttpClient, firstRun: Boolean): IO[Unit] = {
val runTokenSync = firstRun || !config.plexConfiguration.hasPlexPass

def run(config: Configuration, client: HttpClient, runFullSync: Boolean): IO[Unit] = {
val result = for {
selfWatchlist <-
if (runTokenSync)
if (runFullSync)
getSelfWatchlist(config.plexConfiguration, client)
else
EitherT.pure[IO, Throwable](Set.empty[Item])
_ = if (runTokenSync)
_ = if (runFullSync)
logger.info(s"Found ${selfWatchlist.size} items on user's watchlist using the plex token")
othersWatchlist <-
if (config.plexConfiguration.skipFriendSync || !runTokenSync)
if (config.plexConfiguration.skipFriendSync || !runFullSync)
EitherT.pure[IO, Throwable](Set.empty[Item])
else
getOthersWatchlist(config.plexConfiguration, client)
watchlistDatas <- EitherT[IO, Throwable, List[Set[Item]]](
config.plexConfiguration.plexWatchlistUrls.map(fetchWatchlistFromRss(client)).toList.sequence.map(Right(_))
)
watchlistData = watchlistDatas.flatten.toSet
_ = if (runTokenSync)
_ = if (runFullSync)
logger.info(s"Found ${othersWatchlist.size} items on other available watchlists using the plex token")
movies <- fetchMovies(client)(
config.radarrConfiguration.radarrApiKey,
Expand Down
29 changes: 20 additions & 9 deletions src/main/scala/Server.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import cats.effect._
import cats.implicits.catsSyntaxTuple3Parallel
import configuration.{Configuration, ConfigurationUtils, FileAndSystemPropertyReader, SystemPropertyReader}
import cats.implicits.catsSyntaxTuple4Parallel
import configuration.{Configuration, ConfigurationUtils, FileAndSystemPropertyReader}
import http.HttpClient
import org.slf4j.LoggerFactory

Expand All @@ -25,8 +25,9 @@ object Server extends IOApp {
configRef <- Ref.of[IO, Configuration](initialConfig)
result <- (
pingTokenSync(configRef, httpClient),
plexTokenSync(configRef, httpClient),
plexTokenDeleteSync(configRef, httpClient)
plexRssSync(configRef, httpClient),
plexTokenDeleteSync(configRef, httpClient),
plexFullSync(configRef, httpClient)
).parTupled.as(ExitCode.Success)
} yield result
}
Expand All @@ -42,16 +43,26 @@ object Server extends IOApp {
_ <- pingTokenSync(configRef, httpClient)
} yield ()

private def plexTokenSync(
private def plexRssSync(
configRef: Ref[IO, Configuration],
httpClient: HttpClient,
firstRun: Boolean = true
httpClient: HttpClient
): IO[Unit] =
for {
config <- fetchLatestConfig(configRef)
_ <- PlexTokenSync.run(config, httpClient, firstRun)
_ <- PlexTokenSync.run(config, httpClient, runFullSync = false)
_ <- IO.sleep(config.refreshInterval)
_ <- plexTokenSync(configRef, httpClient, firstRun = false)
_ <- plexRssSync(configRef, httpClient)
} yield ()

private def plexFullSync(
configRef: Ref[IO, Configuration],
httpClient: HttpClient
): IO[Unit] =
for {
config <- fetchLatestConfig(configRef)
_ <- PlexTokenSync.run(config, httpClient, runFullSync = true)
_ <- IO.sleep(19.minutes)
Copy link
Owner Author

Choose a reason for hiding this comment

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

Forgot to add my reasoning for why 19 minutes specifically - it avoids accidental clustering (e.g. if a few servers in the world are set to restart at midnight every night, this gives enough randomness that they won't start hammering Plex every 20 mins)

_ <- plexFullSync(configRef, httpClient)
} yield ()

private def plexTokenDeleteSync(configRef: Ref[IO, Configuration], httpClient: HttpClient): IO[Unit] =
Expand Down
2 changes: 1 addition & 1 deletion src/test/scala/PlexTokenSyncSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class PlexTokenSyncSpec extends AnyFlatSpec with Matchers with MockFactory {
defaultRadarrMock(mockHttpClient)
defaultSonarrMock(mockHttpClient)

val sync: Unit = PlexTokenSync.run(config, mockHttpClient, firstRun = true).unsafeRunSync()
val sync: Unit = PlexTokenSync.run(config, mockHttpClient, runFullSync = true).unsafeRunSync()

sync shouldBe ()
}
Expand Down
Loading