Skip to content

Commit

Permalink
Ping plex token every 24 hours (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
nylonee authored Nov 16, 2023
1 parent 1b33ef3 commit 6c0e41a
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 6 deletions.
4 changes: 4 additions & 0 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@ if [ -n "$SONARR_SEASON_MONITORING" ]; then
CMD="$CMD -Dsonarr.seasonMonitoring=$SONARR_SEASON_MONITORING"
fi

if [ -n "$PLEX_TOKEN" ]; then
CMD="$CMD -Dplex.token=$PLEX_TOKEN"
fi

exec $CMD
9 changes: 9 additions & 0 deletions src/main/scala/PlexTokenSync.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import cats.effect.IO
import configuration.Configuration
import http.HttpClient
import plex.PlexUtils

object PlexTokenSync extends PlexUtils {

def run(config: Configuration, client: HttpClient): IO[Unit] = ping(client)(config)
}
16 changes: 13 additions & 3 deletions src/main/scala/Server.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@

import cats.effect._
import cats.implicits.catsSyntaxTuple2Parallel
import configuration.{Configuration, ConfigurationUtils, SystemPropertyReader}
import http.HttpClient
import org.slf4j.LoggerFactory

import java.nio.channels.ClosedChannelException
import scala.concurrent.duration.DurationInt

object Server extends IOApp {

Expand All @@ -21,15 +23,23 @@ object Server extends IOApp {

for {
memoizedConfigIo <- ConfigurationUtils.create(configReader, httpClient).memoize
result <- periodicTask(memoizedConfigIo, httpClient).foreverM.as(ExitCode.Success)
result <- (watchlistSync(memoizedConfigIo, httpClient), plexTokenSync(memoizedConfigIo, httpClient)).parTupled.as(ExitCode.Success)
} yield result
}

private def periodicTask(configIO: IO[Configuration], httpClient: HttpClient): IO[Unit] =
private def watchlistSync(configIO: IO[Configuration], httpClient: HttpClient): IO[Unit] =
for {
config <- configIO
_ <- WatchlistSync.run(config, httpClient)
_ <- IO.sleep(config.refreshInterval)
_ <- periodicTask(configIO, httpClient)
_ <- watchlistSync(configIO, httpClient)
} yield ()

private def plexTokenSync(configIO: IO[Configuration], httpClient: HttpClient): IO[Unit] =
for {
config <- configIO
_ <- PlexTokenSync.run(config, httpClient)
_ <- IO.sleep(24.hours)
_ <- plexTokenSync(configIO, httpClient)
} yield ()
}
3 changes: 2 additions & 1 deletion src/main/scala/configuration/Configuration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ case class Configuration(
radarrQualityProfileId: Int,
radarrRootFolder: String,
radarrBypassIgnored: Boolean,
plexWatchlistUrls: List[Uri]
plexWatchlistUrls: List[Uri],
plexToken: Option[String]
)
4 changes: 3 additions & 1 deletion src/main/scala/configuration/ConfigurationUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ object ConfigurationUtils {
(radarrBaseUrl, radarrApiKey, radarrQualityProfileId, radarrRootFolder) = radarrConfig
radarrBypassIgnored = configReader.getConfigOption(Keys.radarrBypassIgnored).exists(_.toBoolean)
plexWatchlistUrls = getPlexWatchlistUrls(configReader)
plexToken = configReader.getConfigOption(Keys.plexToken)
} yield Configuration(
refreshInterval,
sonarrBaseUrl,
Expand All @@ -37,7 +38,8 @@ object ConfigurationUtils {
radarrQualityProfileId,
radarrRootFolder,
radarrBypassIgnored,
plexWatchlistUrls
plexWatchlistUrls,
plexToken
)

private def getSonarrConfig(configReader: ConfigurationReader, client: HttpClient): IO[(Uri, String, Int, String)] = {
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/configuration/Keys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ private[configuration] object Keys {

val plexWatchlist1 = "plex.watchlist1"
val plexWatchlist2 = "plex.watchlist2"
val plexToken = "plex.token"
}
16 changes: 16 additions & 0 deletions src/main/scala/plex/PlexUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package plex

import io.circe.generic.auto._
import cats.effect.IO
import configuration.Configuration
import http.HttpClient
import model.Item
import org.http4s.{Method, Uri}
Expand All @@ -23,4 +24,19 @@ trait PlexUtils {
Set.empty
}
}

protected def ping(client: HttpClient)(config: Configuration): IO[Unit] =
config.plexToken.map { token =>
val url = Uri
.unsafeFromString("https://plex.tv/api/v2/ping")
.withQueryParam("X-Plex-Token", token)
.withQueryParam("X-Plex-Client-Identifier", "watchlistarr")

client.httpRequest(Method.GET, url).map {
case Right(_) =>
logger.info(s"Refreshed the access token expiry")
case Left(err) =>
logger.warn(s"Unable to ping plex.tv: $err")
}
}.getOrElse(IO.unit)
}
14 changes: 13 additions & 1 deletion src/test/scala/configuration/ConfigurationUtilsSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,24 @@ class ConfigurationUtilsSpec extends AnyFlatSpec with Matchers with MockFactory
an[IllegalArgumentException] should be thrownBy ConfigurationUtils.create(mockConfigReader, mockHttpClient).unsafeRunSync()
}

it should "allow an optional plex token to be passed in" in {

val mockConfigReader = createMockConfigReader(plexToken = Some("test-token"))
val mockHttpClient = createMockHttpClient()

val config = ConfigurationUtils.create(mockConfigReader, mockHttpClient).unsafeRunSync()
noException should be thrownBy config
config.plexToken shouldBe Some("test-token")
}

private def createMockConfigReader(
sonarrApiKey: Option[String] = Some("sonarr-api-key"),
sonarrRootFolder: Option[String] = None,
radarrRootFolder: Option[String] = None,
radarrApiKey: Option[String] = Some("radarr-api-key"),
plexWatchlist1: Option[String] = Some(s"https://rss.plex.tv/1"),
plexWatchlist2: Option[String] = None
plexWatchlist2: Option[String] = None,
plexToken: Option[String] = None
): ConfigurationReader = {
val unset = None

Expand All @@ -175,6 +186,7 @@ class ConfigurationUtilsSpec extends AnyFlatSpec with Matchers with MockFactory
(mockConfigReader.getConfigOption _).expects(Keys.plexWatchlist1).returning(plexWatchlist1).anyNumberOfTimes()
(mockConfigReader.getConfigOption _).expects(Keys.plexWatchlist2).returning(plexWatchlist2).anyNumberOfTimes()
(mockConfigReader.getConfigOption _).expects(Keys.sonarrSeasonMonitoring).returning(unset).anyNumberOfTimes()
(mockConfigReader.getConfigOption _).expects(Keys.plexToken).returning(plexToken).anyNumberOfTimes()
mockConfigReader
}

Expand Down

0 comments on commit 6c0e41a

Please sign in to comment.