diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 698abc1c..ec2abd89 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -30,6 +30,10 @@ env:
SONARR_BASE_URL: http://localhost:8989
SONARR_API_KEY: 1accda4476394bfcaddefe8c4fd77d4a
+ VAPID_PUBLIC_KEY: BDntLA3k5K1tsrFOXXAuS_9Ey30jxy-R2CAosC2DOQnTs8LpQGxpTEx3AcPXinVYFFpJI6tT_RJC8pHgUsdbhOk
+ VAPID_PRIVATE_KEY: RVPPDBVNmJtSLoZ28jE1SumpG4HyhhCPfcix3bvxbLw
+ VAPID_SUBJECT: mailto:admin@email.com
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
@@ -40,7 +44,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- otp: [ '25' ]
+ otp: [ '24' ]
elixir: [ '1.15' ]
services:
diff --git a/README.md b/README.md
index 97af4473..18343b6a 100644
--- a/README.md
+++ b/README.md
@@ -204,7 +204,7 @@ services:
To keep your media in sync, webhook urls are required in your integrations. Midarr accepts a
POST request from your integrations with your unique API Token (found on the Midarr Settings page).
-Add your webhook urls to `Radarr / Sonarr -> Settings -> Connect`:
+Add these webhook urls to Radarr / Sonarr under `Settings -> Connect -> Webhook`:
#### Radarr example
```
diff --git a/lib/media_server/application.ex b/lib/media_server/application.ex
index 4c6975d7..e5e13a83 100644
--- a/lib/media_server/application.ex
+++ b/lib/media_server/application.ex
@@ -20,9 +20,11 @@ defmodule MediaServer.Application do
# {MediaServer.Worker, arg}
MediaServerWeb.Presence,
{DynamicSupervisor, name: MediaServer.DynamicSupervisor},
- MediaServer.UserActions,
MediaServer.MoviesIndex,
- MediaServer.SeriesIndex
+ MediaServer.SeriesIndex,
+ MediaServer.MovieActions,
+ MediaServer.SeriesActions,
+ MediaServer.UserActions
]
# See https://hexdocs.pm/elixir/Supervisor.html
diff --git a/lib/media_server/media_actions.ex b/lib/media_server/media_actions.ex
index 9a591ea3..fc2f4628 100644
--- a/lib/media_server/media_actions.ex
+++ b/lib/media_server/media_actions.ex
@@ -1,6 +1,7 @@
defmodule MediaServer.MediaActions do
use Ecto.Schema
import Ecto.Changeset
+ import Ecto.Query
alias MediaServer.Repo
@@ -59,6 +60,25 @@ defmodule MediaServer.MediaActions do
def delete(attrs) do
Repo.get_by(__MODULE__, attrs)
- |> Repo.delete
+ |> Repo.delete()
+ end
+
+ def movie(id) do
+ from this in __MODULE__,
+ where: this.media_type_id == ^MediaServer.MediaTypes.get_movie_id() and this.media_id == ^id
+ end
+
+ def series(id) do
+ from this in __MODULE__,
+ where:
+ this.media_type_id == ^MediaServer.MediaTypes.get_series_id() and this.media_id == ^id
+ end
+
+ def followers(query) do
+ MediaServer.Repo.all(
+ from this in query,
+ where: this.action_id == ^MediaServer.Actions.get_followed_id(),
+ preload: [user: [:push_subscriptions]]
+ )
end
end
diff --git a/lib/media_server/media_types.ex b/lib/media_server/media_types.ex
index 3b73b2c7..f3ad3cf8 100644
--- a/lib/media_server/media_types.ex
+++ b/lib/media_server/media_types.ex
@@ -25,6 +25,10 @@ defmodule MediaServer.MediaTypes do
Repo.get_by!(__MODULE__, type: "movie").id
end
+ def get_series_id() do
+ Repo.get_by!(__MODULE__, type: "series").id
+ end
+
def get_episode_id() do
Repo.get_by!(__MODULE__, type: "episode").id
end
diff --git a/lib/media_server/movie_actions.ex b/lib/media_server/movie_actions.ex
new file mode 100644
index 00000000..00231b04
--- /dev/null
+++ b/lib/media_server/movie_actions.ex
@@ -0,0 +1,34 @@
+defmodule MediaServer.MovieActions do
+ use Task
+
+ def start_link(arg) do
+ Task.start_link(__MODULE__, :run, [arg])
+ end
+
+ def run(_arg) do
+ Phoenix.PubSub.subscribe(MediaServer.PubSub, "movie")
+ end
+
+ def handle_info({:added, %{"movie" => %{"id" => id, "title" => title}}}) do
+ MediaServer.MoviesIndex.reset()
+
+ followers = MediaServer.MediaActions.movie(id) |> MediaServer.MediaActions.followers()
+
+ Enum.each(followers, fn media_action ->
+ Enum.each(media_action.user.push_subscriptions, fn push_subscription ->
+ WebPushElixir.send_notification(
+ push_subscription.push_subscription,
+ "#{title} is now available"
+ )
+ end)
+ end)
+ end
+
+ def handle_info({:deleted}) do
+ MediaServer.MoviesIndex.reset()
+ end
+
+ def handle_info({:deleted_file}) do
+ MediaServer.MoviesIndex.reset()
+ end
+end
diff --git a/lib/media_server/push_subscriptions.ex b/lib/media_server/push_subscriptions.ex
index 2ed44309..be3f567b 100644
--- a/lib/media_server/push_subscriptions.ex
+++ b/lib/media_server/push_subscriptions.ex
@@ -34,6 +34,6 @@ defmodule MediaServer.PushSubscriptions do
def delete(attrs) do
Repo.get_by(__MODULE__, attrs)
- |> Repo.delete
+ |> Repo.delete()
end
end
diff --git a/lib/media_server/series_actions.ex b/lib/media_server/series_actions.ex
new file mode 100644
index 00000000..a58b76e1
--- /dev/null
+++ b/lib/media_server/series_actions.ex
@@ -0,0 +1,36 @@
+defmodule MediaServer.SeriesActions do
+ use Task
+
+ require Logger
+
+ def start_link(arg) do
+ Task.start_link(__MODULE__, :run, [arg])
+ end
+
+ def run(_arg) do
+ Phoenix.PubSub.subscribe(MediaServer.PubSub, "series")
+ end
+
+ def handle_info({:added, %{"series" => %{"id" => id, "title" => title}}}) do
+ MediaServer.SeriesIndex.reset()
+
+ followers = MediaServer.MediaActions.series(id) |> MediaServer.MediaActions.followers()
+
+ Enum.each(followers, fn media_action ->
+ Enum.each(media_action.user.push_subscriptions, fn push_subscription ->
+ WebPushElixir.send_notification(
+ push_subscription.push_subscription,
+ "#{title} is now available"
+ )
+ end)
+ end)
+ end
+
+ def handle_info({:deleted}) do
+ MediaServer.SeriesIndex.reset()
+ end
+
+ def handle_info({:deleted_episode_file}) do
+ MediaServer.SeriesIndex.reset()
+ end
+end
diff --git a/lib/media_server/user_actions.ex b/lib/media_server/user_actions.ex
index b94a3cbd..41efef0e 100644
--- a/lib/media_server/user_actions.ex
+++ b/lib/media_server/user_actions.ex
@@ -24,7 +24,6 @@ defmodule MediaServer.UserActions do
end
def handle_info({:followed, params}, state) do
-
media_type_id = MediaServer.MediaTypes.get_type_id(params["media_type"])
MediaServer.MediaActions.create(%{
@@ -34,13 +33,14 @@ defmodule MediaServer.UserActions do
media_type_id: media_type_id
})
- Logger.info("user_id:#{ params["user_id"] }:followed:media_type_id:#{ media_type_id }:media_id:#{ params["media_id"] }")
+ Logger.info(
+ "user_id:#{params["user_id"]}:followed:media_type_id:#{media_type_id}:media_id:#{params["media_id"]}"
+ )
{:noreply, state}
end
def handle_info({:unfollowed, params}, state) do
-
media_type_id = MediaServer.MediaTypes.get_type_id(params["media_type"])
MediaServer.MediaActions.delete(%{
@@ -50,31 +50,35 @@ defmodule MediaServer.UserActions do
media_type_id: media_type_id
})
- Logger.info("user_id:#{ params["user_id"] }:unfollowed:media_type_id:#{ media_type_id }:media_id:#{ params["media_id"] }")
+ Logger.info(
+ "user_id:#{params["user_id"]}:unfollowed:media_type_id:#{media_type_id}:media_id:#{params["media_id"]}"
+ )
{:noreply, state}
end
def handle_info({:granted_push_notifications, params}, state) do
-
MediaServer.PushSubscriptions.create(%{
user_id: params["user_id"],
push_subscription: params["push_subscription"]
})
- Logger.info("user_id:#{ params["user_id"] }:granted_push_notifications:media_type_id:#{ params["media_type_id"] }:media_id:#{ params["media_id"] }")
+ Logger.info(
+ "user_id:#{params["user_id"]}:granted_push_notifications:media_type_id:#{params["media_type_id"]}:media_id:#{params["media_id"]}"
+ )
{:noreply, state}
end
def handle_info({:denied_push_notifications, params}, state) do
-
MediaServer.PushSubscriptions.create(%{
user_id: params["user_id"],
push_subscription: params["message"]
})
- Logger.info("user_id:#{ params["user_id"] }:denied_push_notifications:media_type_id:#{ params["media_type_id"] }:media_id:#{ params["media_id"] }")
+ Logger.info(
+ "user_id:#{params["user_id"]}:denied_push_notifications:media_type_id:#{params["media_type_id"]}:media_id:#{params["media_id"]}"
+ )
{:noreply, state}
end
diff --git a/lib/media_server_web/components/follow_component.ex b/lib/media_server_web/components/follow_component.ex
index f8f5858b..4051f3c3 100644
--- a/lib/media_server_web/components/follow_component.ex
+++ b/lib/media_server_web/components/follow_component.ex
@@ -6,16 +6,20 @@ defmodule MediaServerWeb.Components.FollowComponent do
@impl true
def preload(list_of_assigns) do
media_id = Enum.find(list_of_assigns, fn assign -> Map.get(assign, :media_id) end).media_id
- media_type = Enum.find(list_of_assigns, fn assign -> Map.get(assign, :media_type) end).media_type
+
+ media_type =
+ Enum.find(list_of_assigns, fn assign -> Map.get(assign, :media_type) end).media_type
+
user_id = Enum.find(list_of_assigns, fn assign -> Map.get(assign, :user_id) end).user_id
media_type_id = MediaServer.MediaTypes.get_type_id(media_type)
query =
from media_actions in MediaServer.MediaActions,
- where:
- media_actions.media_type_id == ^media_type_id and
- media_actions.user_id == ^user_id and media_actions.media_id == ^media_id and media_actions.action_id == ^MediaServer.Actions.get_followed_id
+ where:
+ media_actions.media_type_id == ^media_type_id and
+ media_actions.user_id == ^user_id and media_actions.media_id == ^media_id and
+ media_actions.action_id == ^MediaServer.Actions.get_followed_id()
result = MediaServer.Repo.all(query)
@@ -30,7 +34,8 @@ defmodule MediaServerWeb.Components.FollowComponent do
state: "Following",
event: "unfollow"
}
- end |> Map.merge(%{
+ end
+ |> Map.merge(%{
media_id: assign.media_id,
media_type: assign.media_type,
user_id: assign.user_id,
diff --git a/lib/media_server_web/components/follow_component.html.heex b/lib/media_server_web/components/follow_component.html.heex
index 41296f8e..8c7f0500 100644
--- a/lib/media_server_web/components/follow_component.html.heex
+++ b/lib/media_server_web/components/follow_component.html.heex
@@ -10,4 +10,4 @@
class="inline-flex items-center gap-2 justify-center rounded-lg py-2 px-3 text-sm outline-offset-2 transition active:transition-none bg-zinc-800 font-semibold text-zinc-100 hover:bg-zinc-700 active:bg-zinc-800"
>
<%= @state %>
-
\ No newline at end of file
+
diff --git a/lib/media_server_web/components/footer_component.html.heex b/lib/media_server_web/components/footer_component.html.heex
index 014f09ab..fce9b0e7 100644
--- a/lib/media_server_web/components/footer_component.html.heex
+++ b/lib/media_server_web/components/footer_component.html.heex
@@ -3,7 +3,7 @@
Copyright © 2023 Midarr Labs
- v4.2.0-beta.1
+ v4.2.0-beta.2
diff --git a/lib/media_server_web/controllers/webhooks_controller.ex b/lib/media_server_web/controllers/webhooks_controller.ex
index 328095e7..ccae5b53 100644
--- a/lib/media_server_web/controllers/webhooks_controller.ex
+++ b/lib/media_server_web/controllers/webhooks_controller.ex
@@ -1,43 +1,43 @@
defmodule MediaServerWeb.WebhooksController do
use MediaServerWeb, :controller
- def create(conn, %{"id" => "movie", "eventType" => "Download"}) do
- MediaServer.MoviesIndex.reset()
+ def create(conn, %{"id" => "movie", "eventType" => "Download"} = params) do
+ Phoenix.PubSub.broadcast(MediaServer.PubSub, "movie", {:added, params})
conn
|> send_resp(201, "Ok")
end
def create(conn, %{"id" => "movie", "eventType" => "MovieDelete"}) do
- MediaServer.MoviesIndex.reset()
+ Phoenix.PubSub.broadcast(MediaServer.PubSub, "movie", {:deleted})
conn
|> send_resp(201, "Ok")
end
def create(conn, %{"id" => "movie", "eventType" => "MovieFileDelete"}) do
- MediaServer.MoviesIndex.reset()
+ Phoenix.PubSub.broadcast(MediaServer.PubSub, "movie", {:deleted_file})
conn
|> send_resp(201, "Ok")
end
- def create(conn, %{"id" => "series", "eventType" => "Download"}) do
- MediaServer.SeriesIndex.reset()
+ def create(conn, %{"id" => "series", "eventType" => "Download"} = params) do
+ Phoenix.PubSub.broadcast(MediaServer.PubSub, "series", {:added, params})
conn
|> send_resp(201, "Ok")
end
def create(conn, %{"id" => "series", "eventType" => "SeriesDelete"}) do
- MediaServer.SeriesIndex.reset()
+ Phoenix.PubSub.broadcast(MediaServer.PubSub, "series", {:deleted})
conn
|> send_resp(201, "Ok")
end
def create(conn, %{"id" => "series", "eventType" => "EpisodeFileDelete"}) do
- MediaServer.SeriesIndex.reset()
+ Phoenix.PubSub.broadcast(MediaServer.PubSub, "series", {:deleted_episode_file})
conn
|> send_resp(201, "Ok")
diff --git a/lib/media_server_web/live/movies_live/show.html.heex b/lib/media_server_web/live/movies_live/show.html.heex
index be376bff..7b21ac04 100644
--- a/lib/media_server_web/live/movies_live/show.html.heex
+++ b/lib/media_server_web/live/movies_live/show.html.heex
@@ -20,16 +20,16 @@
<%= if @movie["hasFile"] do %>
- <.link
- id={"play-#{ @movie["id"] }"}
- navigate={"/watch?movie=#{@movie["id"]}#{ if !is_nil(@continue) do "×tamp=#{@continue.current_time}" end }"}
- class="group relative flex h-16 w-16 flex-shrink-0 items-center justify-center rounded-full bg-zinc-800 hover:bg-zinc-700 focus:outline-none focus:ring focus:ring-slate-700 focus:ring-offset-4"
- >
-
-
+ <.link
+ id={"play-#{ @movie["id"] }"}
+ navigate={"/watch?movie=#{@movie["id"]}#{ if !is_nil(@continue) do "×tamp=#{@continue.current_time}" end }"}
+ class="group relative flex h-16 w-16 flex-shrink-0 items-center justify-center rounded-full bg-zinc-800 hover:bg-zinc-700 focus:outline-none focus:ring focus:ring-slate-700 focus:ring-offset-4"
+ >
+
+
<% end %>
<.live_component
@@ -39,7 +39,7 @@
media_type="movie"
user_id={@current_user.id}
return_to={~p"/movies/#{@movie["id"]}"}
- />
+ />
diff --git a/lib/media_server_web/live/series_live/show.html.heex b/lib/media_server_web/live/series_live/show.html.heex
index 97acf6f1..05e09650 100644
--- a/lib/media_server_web/live/series_live/show.html.heex
+++ b/lib/media_server_web/live/series_live/show.html.heex
@@ -26,7 +26,7 @@
media_type="series"
user_id={@current_user.id}
return_to={~p"/series/#{@serie["id"]}"}
- />
+ />
diff --git a/lib/media_server_web/templates/layout/live.html.heex b/lib/media_server_web/templates/layout/live.html.heex
index 81c5df13..65337f66 100644
--- a/lib/media_server_web/templates/layout/live.html.heex
+++ b/lib/media_server_web/templates/layout/live.html.heex
@@ -20,7 +20,7 @@
<.link
- navigate={"/movies"}
+ navigate="/movies"
class="inline-flex flex-col items-center justify-center p-4 hover:bg-gray-50 dark:hover:bg-gray-800 group"
>