Merge pull request #312 from philomena-dev/channel-extraction

Channel automatic update move
This commit is contained in:
liamwhite 2024-07-01 09:31:48 -04:00 committed by GitHub
commit d799c9d1bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 94 additions and 50 deletions

View file

@ -6,49 +6,15 @@ defmodule Philomena.Channels do
import Ecto.Query, warn: false import Ecto.Query, warn: false
alias Philomena.Repo alias Philomena.Repo
alias Philomena.Channels.AutomaticUpdater
alias Philomena.Channels.Channel alias Philomena.Channels.Channel
alias Philomena.Channels.PicartoChannel
alias Philomena.Channels.PiczelChannel
alias Philomena.Notifications alias Philomena.Notifications
@doc """ @doc """
Updates all the tracked channels for which an update Updates all the tracked channels for which an update scheme is known.
scheme is known.
""" """
def update_tracked_channels! do def update_tracked_channels! do
now = DateTime.utc_now() |> DateTime.truncate(:second) AutomaticUpdater.update_tracked_channels!()
picarto_channels = PicartoChannel.live_channels(now)
live_picarto_channels = Map.keys(picarto_channels)
piczel_channels = PiczelChannel.live_channels(now)
live_piczel_channels = Map.keys(piczel_channels)
# Update all channels which are offline to reflect offline status
offline_query =
from c in Channel,
where: c.type == "PicartoChannel" and c.short_name not in ^live_picarto_channels,
or_where: c.type == "PiczelChannel" and c.short_name not in ^live_piczel_channels
Repo.update_all(offline_query, set: [is_live: false, updated_at: now])
# Update all channels which are online to reflect online status using
# changeset functions
online_query =
from c in Channel,
where: c.type == "PicartoChannel" and c.short_name in ^live_picarto_channels,
or_where: c.type == "PiczelChannel" and c.short_name in ^live_picarto_channels
online_query
|> Repo.all()
|> Enum.map(fn
%{type: "PicartoChannel", short_name: name} = channel ->
Channel.update_changeset(channel, Map.get(picarto_channels, name, []))
%{type: "PiczelChannel", short_name: name} = channel ->
Channel.update_changeset(channel, Map.get(piczel_channels, name, []))
end)
|> Enum.map(&Repo.update!/1)
end end
@doc """ @doc """
@ -103,6 +69,24 @@ defmodule Philomena.Channels do
|> Repo.update() |> Repo.update()
end end
@doc """
Updates a channel's state when it goes live.
## Examples
iex> update_channel_state(channel, %{field: new_value})
{:ok, %Channel{}}
iex> update_channel_state(channel, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_channel_state(%Channel{} = channel, attrs) do
channel
|> Channel.update_changeset(attrs)
|> Repo.update()
end
@doc """ @doc """
Deletes a Channel. Deletes a Channel.

View file

@ -0,0 +1,64 @@
defmodule Philomena.Channels.AutomaticUpdater do
@moduledoc """
Automatic update routine for streams.
Calls APIs for each stream provider to remove channels which are no longer online,
and to restore channels which are currently online.
"""
import Ecto.Query, warn: false
alias Philomena.Repo
alias Philomena.Channels
alias Philomena.Channels.Channel
alias Philomena.Channels.PicartoChannel
alias Philomena.Channels.PiczelChannel
@doc """
Updates all the tracked channels for which an update scheme is known.
"""
def update_tracked_channels! do
now = DateTime.utc_now(:second)
Enum.each(providers(), &update_provider(&1, now))
end
defp providers do
[
{"PicartoChannel", PicartoChannel.live_channels()},
{"PiczelChannel", PiczelChannel.live_channels()}
]
end
defp update_provider({provider_name, live_channels}, now) do
channel_names = Map.keys(live_channels)
provider_name
|> update_offline_query(channel_names, now)
|> Repo.update_all([])
provider_name
|> online_query(channel_names)
|> Repo.all()
|> Enum.each(&update_online_channel(&1, live_channels, now))
end
defp update_offline_query(provider_name, channel_names, now) do
from c in Channel,
where: c.type == ^provider_name and c.short_name not in ^channel_names,
update: [set: [is_live: false, updated_at: ^now]]
end
defp online_query(provider_name, channel_names) do
from c in Channel,
where: c.type == ^provider_name and c.short_name in ^channel_names
end
defp update_online_channel(channel, live_channels, now) do
attrs =
live_channels
|> Map.get(channel.short_name, %{})
|> Map.merge(%{last_live_at: now, last_fetched_at: now})
Channels.update_channel_state(channel, attrs)
end
end

View file

@ -1,30 +1,28 @@
defmodule Philomena.Channels.PicartoChannel do defmodule Philomena.Channels.PicartoChannel do
@api_online "https://api.picarto.tv/api/v1/online?adult=true&gaming=true" @api_online "https://api.picarto.tv/api/v1/online?adult=true&gaming=true"
@spec live_channels(DateTime.t()) :: map() @spec live_channels() :: map()
def live_channels(now) do def live_channels do
@api_online @api_online
|> PhilomenaProxy.Http.get() |> PhilomenaProxy.Http.get()
|> case do |> case do
{:ok, %{body: body, status: 200}} -> {:ok, %{body: body, status: 200}} ->
body body
|> Jason.decode!() |> Jason.decode!()
|> Map.new(&{&1["name"], fetch(&1, now)}) |> Map.new(&{&1["name"], fetch(&1)})
_error -> _error ->
%{} %{}
end end
end end
defp fetch(api, now) do defp fetch(api) do
%{ %{
title: api["title"], title: api["title"],
is_live: true, is_live: true,
nsfw: api["adult"], nsfw: api["adult"],
viewers: api["viewers"], viewers: api["viewers"],
thumbnail_url: api["thumbnails"]["web"], thumbnail_url: api["thumbnails"]["web"],
last_fetched_at: now,
last_live_at: now,
description: nil description: nil
} }
end end

View file

@ -1,30 +1,28 @@
defmodule Philomena.Channels.PiczelChannel do defmodule Philomena.Channels.PiczelChannel do
@api_online "https://api.piczel.tv/api/streams" @api_online "https://api.piczel.tv/api/streams"
@spec live_channels(DateTime.t()) :: map() @spec live_channels() :: map()
def live_channels(now) do def live_channels do
@api_online @api_online
|> PhilomenaProxy.Http.get() |> PhilomenaProxy.Http.get()
|> case do |> case do
{:ok, %{body: body, status: 200}} -> {:ok, %{body: body, status: 200}} ->
body body
|> Jason.decode!() |> Jason.decode!()
|> Map.new(&{&1["slug"], fetch(&1, now)}) |> Map.new(&{&1["slug"], fetch(&1)})
_error -> _error ->
%{} %{}
end end
end end
defp fetch(api, now) do defp fetch(api) do
%{ %{
title: api["title"], title: api["title"],
is_live: api["live"], is_live: api["live"],
nsfw: api["adult"], nsfw: api["adult"],
viewers: api["viewers"], viewers: api["viewers"],
thumbnail_url: api["user"]["avatar"]["avatar"]["url"], thumbnail_url: api["user"]["avatar"]["avatar"]["url"]
last_fetched_at: now,
last_live_at: now
} }
end end
end end