Skip to content

Commit

Permalink
Add contributor convenience files (#126)
Browse files Browse the repository at this point in the history
  • Loading branch information
nylonee authored Apr 9, 2024
1 parent 03df0c5 commit 80ce086
Show file tree
Hide file tree
Showing 32 changed files with 1,101 additions and 688 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@nylonee
7 changes: 7 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Description
Enter a meaningful description here of the changes, including any relevant links

## Checklist
- [ ] Documentation Updated
- [ ] `sbt scalafmtAll` Run (and optionally `sbt scalafmtSbt`)
- [ ] At least one approval from a codeowner
2 changes: 1 addition & 1 deletion .github/workflows/release-java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
distribution: 'temurin'
cache: 'sbt'
- name: sbt validation
run: sbt "dependencyUpdatesFailBuild; unusedCompileDependenciesTest; undeclaredCompileDependenciesTest; test"
run: sbt "scalafmtSbtCheck; scalafmtCheckAll; dependencyUpdatesFailBuild; unusedCompileDependenciesTest; undeclaredCompileDependenciesTest; test"
- name: Build fat JAR
run: sbt assembly
- name: Rename fat JAR
Expand Down
8 changes: 8 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version=3.7.13

runner.dialect = scala213
preset = defaultWithAlign
project.git = true
maxColumn = 120

rewrite.rules = [ RedundantBraces, SortImports ]
78 changes: 39 additions & 39 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,55 @@ ThisBuild / scalaVersion := "2.13.12"

lazy val root = (project in file("."))
.settings(
name := "watchlistarr",
assembly / mainClass := Some("Server"),
name := "watchlistarr",
assembly / mainClass := Some("Server")
)

val caseInsensitiveVersion = "1.4.0"
val catsCoreVersion = "2.9.0"
val catsEffectVersion = "3.5.0"
val catsEffectKernelVersion = "3.5.1"
val caseInsensitiveVersion = "1.4.0"
val catsCoreVersion = "2.9.0"
val catsEffectVersion = "3.5.0"
val catsEffectKernelVersion = "3.5.1"
val circeGenericExtrasVersion = "0.14.3"
val circeVersion = "0.14.6"
val fs2Version = "3.7.0"
val http4sVersion = "0.23.23"
val logbackVersion = "1.4.11"
val scalamockVersion = "5.2.0"
val scalatestVersion = "3.2.17"
val shapelessVersion = "2.3.10"
val slf4jVersion = "2.0.12"
val snakeYamlVersion = "2.2"
val vaultVersion = "3.5.0"
val circeVersion = "0.14.6"
val fs2Version = "3.7.0"
val http4sVersion = "0.23.23"
val logbackVersion = "1.4.11"
val scalamockVersion = "5.2.0"
val scalatestVersion = "3.2.17"
val shapelessVersion = "2.3.10"
val slf4jVersion = "2.0.12"
val snakeYamlVersion = "2.2"
val vaultVersion = "3.5.0"

libraryDependencies ++= Seq(
"org.scala-lang" % "scala-library" % scalaVersion.value % "provided",
"ch.qos.logback" % "logback-classic" % logbackVersion % Runtime,
"org.slf4j" % "slf4j-api" % slf4jVersion,
"org.http4s" %% "http4s-ember-client" % http4sVersion,
"org.http4s" %% "http4s-circe" % http4sVersion,
"org.http4s" %% "http4s-client" % http4sVersion,
"org.http4s" %% "http4s-core" % http4sVersion,
"co.fs2" %% "fs2-core" % fs2Version,
"co.fs2" %% "fs2-io" % fs2Version,
"com.chuusai" %% "shapeless" % shapelessVersion,
"io.circe" %% "circe-core" % circeVersion,
"org.typelevel" %% "case-insensitive" % caseInsensitiveVersion,
"org.typelevel" %% "cats-core" % catsCoreVersion,
"org.typelevel" %% "cats-effect" % catsEffectVersion,
"org.typelevel" %% "cats-effect-kernel" % catsEffectKernelVersion,
"org.typelevel" %% "vault" % vaultVersion,
"io.circe" %% "circe-generic" % circeVersion,
"io.circe" %% "circe-generic-extras" % circeGenericExtrasVersion,
"org.yaml" % "snakeyaml" % snakeYamlVersion,
"io.circe" %% "circe-parser" % circeVersion % Test,
"org.scalamock" %% "scalamock" % scalamockVersion % Test,
"org.scalatest" %% "scalatest" % scalatestVersion % Test
"org.scala-lang" % "scala-library" % scalaVersion.value % "provided",
"ch.qos.logback" % "logback-classic" % logbackVersion % Runtime,
"org.slf4j" % "slf4j-api" % slf4jVersion,
"org.http4s" %% "http4s-ember-client" % http4sVersion,
"org.http4s" %% "http4s-circe" % http4sVersion,
"org.http4s" %% "http4s-client" % http4sVersion,
"org.http4s" %% "http4s-core" % http4sVersion,
"co.fs2" %% "fs2-core" % fs2Version,
"co.fs2" %% "fs2-io" % fs2Version,
"com.chuusai" %% "shapeless" % shapelessVersion,
"io.circe" %% "circe-core" % circeVersion,
"org.typelevel" %% "case-insensitive" % caseInsensitiveVersion,
"org.typelevel" %% "cats-core" % catsCoreVersion,
"org.typelevel" %% "cats-effect" % catsEffectVersion,
"org.typelevel" %% "cats-effect-kernel" % catsEffectKernelVersion,
"org.typelevel" %% "vault" % vaultVersion,
"io.circe" %% "circe-generic" % circeVersion,
"io.circe" %% "circe-generic-extras" % circeGenericExtrasVersion,
"org.yaml" % "snakeyaml" % snakeYamlVersion,
"io.circe" %% "circe-parser" % circeVersion % Test,
"org.scalamock" %% "scalamock" % scalamockVersion % Test,
"org.scalatest" %% "scalatest" % scalatestVersion % Test
)

enablePlugins(JavaAppPackaging)

ThisBuild / assemblyMergeStrategy := {
case "module-info.class" => MergeStrategy.discard
case "module-info.class" => MergeStrategy.discard
case PathList("META-INF", _*) => MergeStrategy.discard
case x =>
val oldStrategy = (ThisBuild / assemblyMergeStrategy).value
Expand Down
7 changes: 4 additions & 3 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.11")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.1")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.3")
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.11")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.1")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.3")
addSbtPlugin("com.github.cb372" % "sbt-explicit-dependencies" % "0.3.1")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2")
37 changes: 26 additions & 11 deletions src/main/scala/PlexTokenDeleteSync.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,39 @@ object PlexTokenDeleteSync extends PlexUtils with SonarrUtils with RadarrUtils {
def run(config: Configuration, client: HttpClient): IO[Unit] = {
val result = for {
selfWatchlist <- getSelfWatchlist(config.plexConfiguration, client)
othersWatchlist <- if (config.plexConfiguration.skipFriendSync) 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(_)))
othersWatchlist <-
if (config.plexConfiguration.skipFriendSync) 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
moviesWithoutExclusions <- fetchMovies(client)(config.radarrConfiguration.radarrApiKey, config.radarrConfiguration.radarrBaseUrl, bypass = true)
seriesWithoutExclusions <- fetchSeries(client)(config.sonarrConfiguration.sonarrApiKey, config.sonarrConfiguration.sonarrBaseUrl, bypass = true)
moviesWithoutExclusions <- fetchMovies(client)(
config.radarrConfiguration.radarrApiKey,
config.radarrConfiguration.radarrBaseUrl,
bypass = true
)
seriesWithoutExclusions <- fetchSeries(client)(
config.sonarrConfiguration.sonarrApiKey,
config.sonarrConfiguration.sonarrBaseUrl,
bypass = true
)
allIdsWithoutExclusions = moviesWithoutExclusions ++ seriesWithoutExclusions
_ <- missingIdsOnPlex(client)(config)(allIdsWithoutExclusions, selfWatchlist ++ othersWatchlist ++ watchlistData)
} yield ()

result.leftMap {
err =>
result
.leftMap { err =>
logger.warn(s"An error occurred: $err")
err
}.value.map(_.getOrElse(()))
}
.value
.map(_.getOrElse(()))
}

private def missingIdsOnPlex(client: HttpClient)(config: Configuration)(existingItems: Set[Item], watchlist: Set[Item]): EitherT[IO, Throwable, Set[Unit]] = {
private def missingIdsOnPlex(
client: HttpClient
)(config: Configuration)(existingItems: Set[Item], watchlist: Set[Item]): EitherT[IO, Throwable, Set[Unit]] = {
for {
item <- existingItems
maybeExistingItem = watchlist.exists(_.matches(item))
Expand All @@ -53,13 +69,13 @@ object PlexTokenDeleteSync extends PlexUtils with SonarrUtils with RadarrUtils {

private def deleteMovie(client: HttpClient, config: Configuration)(movie: Item): EitherT[IO, Throwable, Unit] =
if (config.deleteConfiguration.movieDeleting) {
deleteFromRadarr(client, config.radarrConfiguration)(movie)
deleteFromRadarr(client, config.radarrConfiguration)(movie)
} else {
logger.info(s"Found movie \"${movie.title}\" which is not watchlisted on Plex")
EitherT.pure[IO, Throwable](())
}

private def deleteSeries(client: HttpClient, config: Configuration)(show: Item): EitherT[IO, Throwable, Unit] = {
private def deleteSeries(client: HttpClient, config: Configuration)(show: Item): EitherT[IO, Throwable, Unit] =
if (show.ended.contains(true) && config.deleteConfiguration.endedShowDeleting) {
deleteFromSonarr(client, config.sonarrConfiguration)(show)
} else if (show.ended.contains(false) && config.deleteConfiguration.continuingShowDeleting) {
Expand All @@ -68,6 +84,5 @@ object PlexTokenDeleteSync extends PlexUtils with SonarrUtils with RadarrUtils {
logger.info(s"Found show \"${show.title}\" which is not watchlisted on Plex")
EitherT[IO, Throwable, Unit](IO.pure(Right(())))
}
}

}
59 changes: 40 additions & 19 deletions src/main/scala/PlexTokenSync.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,54 @@ object PlexTokenSync extends PlexUtils with SonarrUtils with RadarrUtils {
val runTokenSync = firstRun || !config.plexConfiguration.hasPlexPass

val result = for {
selfWatchlist <- if (runTokenSync)
getSelfWatchlist(config.plexConfiguration, client)
else
EitherT.pure[IO, Throwable](Set.empty[Item])
selfWatchlist <-
if (runTokenSync)
getSelfWatchlist(config.plexConfiguration, client)
else
EitherT.pure[IO, Throwable](Set.empty[Item])
_ = if (runTokenSync)
logger.info(s"Found ${selfWatchlist.size} items on user's watchlist using the plex token")
othersWatchlist <- if (config.plexConfiguration.skipFriendSync || !runTokenSync)
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(_)))
othersWatchlist <-
if (config.plexConfiguration.skipFriendSync || !runTokenSync)
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) logger.info(s"Found ${othersWatchlist.size} items on other available watchlists using the plex token")
movies <- fetchMovies(client)(config.radarrConfiguration.radarrApiKey, config.radarrConfiguration.radarrBaseUrl, config.radarrConfiguration.radarrBypassIgnored)
series <- fetchSeries(client)(config.sonarrConfiguration.sonarrApiKey, config.sonarrConfiguration.sonarrBaseUrl, config.sonarrConfiguration.sonarrBypassIgnored)
_ = if (runTokenSync)
logger.info(s"Found ${othersWatchlist.size} items on other available watchlists using the plex token")
movies <- fetchMovies(client)(
config.radarrConfiguration.radarrApiKey,
config.radarrConfiguration.radarrBaseUrl,
config.radarrConfiguration.radarrBypassIgnored
)
series <- fetchSeries(client)(
config.sonarrConfiguration.sonarrApiKey,
config.sonarrConfiguration.sonarrBaseUrl,
config.sonarrConfiguration.sonarrBypassIgnored
)
allIds = movies ++ series
_ <- missingIds(client)(config)(allIds, selfWatchlist ++ othersWatchlist ++ watchlistData)
} yield ()

result.leftMap {
err =>
result
.leftMap { err =>
logger.warn(s"An error occurred: $err")
err
}.value.map(_.getOrElse(()))
}
.value
.map(_.getOrElse(()))
}

private def missingIds(client: HttpClient)(config: Configuration)(existingItems: Set[Item], watchlist: Set[Item]): EitherT[IO, Throwable, Set[Unit]] = {
private def missingIds(
client: HttpClient
)(config: Configuration)(existingItems: Set[Item], watchlist: Set[Item]): EitherT[IO, Throwable, Set[Unit]] = {
for {
watchlistedItem <- watchlist
maybeExistingItem = existingItems.exists(_.matches(watchlistedItem))
category = watchlistedItem.category
category = watchlistedItem.category
task = EitherT.fromEither[IO]((maybeExistingItem, category) match {
case (true, c) =>
logger.debug(s"$c \"${watchlistedItem.title}\" already exists in Sonarr/Radarr")
Expand All @@ -58,15 +75,19 @@ object PlexTokenSync extends PlexUtils with SonarrUtils with RadarrUtils {
logger.debug(s"Found show \"${watchlistedItem.title}\" which does not exist yet in Sonarr")
Right(addToSonarr(client)(config.sonarrConfiguration)(watchlistedItem))
} else {
logger.debug(s"Found show \"${watchlistedItem.title}\" which does not exist yet in Sonarr, but we do not have the tvdb ID so will skip adding")
logger.debug(
s"Found show \"${watchlistedItem.title}\" which does not exist yet in Sonarr, but we do not have the tvdb ID so will skip adding"
)
Right(IO.unit)
}
case (false, "movie") =>
if (watchlistedItem.getTmdbId.isDefined) {
logger.debug(s"Found movie \"${watchlistedItem.title}\" which does not exist yet in Radarr")
Right(addToRadarr(client)(config.radarrConfiguration)(watchlistedItem))
} else {
logger.debug(s"Found movie \"${watchlistedItem.title}\" which does not exist yet in Radarr, but we do not have the tmdb ID so will skip adding")
logger.debug(
s"Found movie \"${watchlistedItem.title}\" which does not exist yet in Radarr, but we do not have the tmdb ID so will skip adding"
)
Right(IO.unit)
}

Expand Down
31 changes: 17 additions & 14 deletions src/main/scala/Server.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import cats.effect._
import cats.implicits.catsSyntaxTuple3Parallel
import configuration.{Configuration, ConfigurationUtils, FileAndSystemPropertyReader, SystemPropertyReader}
Expand All @@ -14,16 +13,16 @@ object Server extends IOApp {

override protected def reportFailure(err: Throwable): IO[Unit] = err match {
case _: ClosedChannelException => IO.pure(logger.debug("Suppressing ClosedChannelException error", err))
case _ => IO.pure(logger.error("Failure caught and handled by IOApp", err))
case _ => IO.pure(logger.error("Failure caught and handled by IOApp", err))
}

def run(args: List[String]): IO[ExitCode] = {
val configReader = FileAndSystemPropertyReader
val httpClient = new HttpClient
val httpClient = new HttpClient

for {
initialConfig <- ConfigurationUtils.create(configReader, httpClient)
configRef <- Ref.of[IO, Configuration](initialConfig)
configRef <- Ref.of[IO, Configuration](initialConfig)
result <- (
pingTokenSync(configRef, httpClient),
plexTokenSync(configRef, httpClient),
Expand All @@ -38,24 +37,28 @@ object Server extends IOApp {
private def pingTokenSync(configRef: Ref[IO, Configuration], httpClient: HttpClient): IO[Unit] =
for {
config <- fetchLatestConfig(configRef)
_ <- PingTokenSync.run(config, httpClient)
_ <- IO.sleep(24.hours)
_ <- pingTokenSync(configRef, httpClient)
_ <- PingTokenSync.run(config, httpClient)
_ <- IO.sleep(24.hours)
_ <- pingTokenSync(configRef, httpClient)
} yield ()

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

private def plexTokenDeleteSync(configRef: Ref[IO, Configuration], httpClient: HttpClient): IO[Unit] =
for {
config <- fetchLatestConfig(configRef)
_ <- PlexTokenDeleteSync.run(config, httpClient)
_ <- IO.sleep(config.deleteConfiguration.deleteInterval)
_ <- plexTokenDeleteSync(configRef, httpClient)
_ <- PlexTokenDeleteSync.run(config, httpClient)
_ <- IO.sleep(config.deleteConfiguration.deleteInterval)
_ <- plexTokenDeleteSync(configRef, httpClient)
} yield ()
}
Loading

0 comments on commit 80ce086

Please sign in to comment.