Auto select frontend for links to "parent" service

Farside now supports redirecting based on a provided link to a "parent"
service, if such a parent service is supported.

For example, a link such as:

farside.link/https://www.youtube.com/watch?v=dQw4w9WgXcQ

will now redirect to any of the available YouTube related frontends.

This works by matching against a mapping of "parent" service domains
("youtube.com", "reddit.com", etc) to a list of their respective frontend
alternatives (["invidious", "piped"], ["libreddit", "teddit"], etc). A
random element is chosen from this list, and the remainder of Farside's
routing logic proceeds as if the user had chosen the service directly to
begin with.

Closes 
This commit is contained in:
Ben Busby 2022-06-09 13:08:01 -06:00
parent ff8d220e90
commit 5006b97dfa
No known key found for this signature in database
GPG key ID: B9B7231E01D924A1
4 changed files with 88 additions and 3 deletions

2
.gitignore vendored
View file

@ -13,3 +13,5 @@ erl_crash.dump
.update-result*
*.rdb
.idea/
*.iml

View file

@ -3,6 +3,21 @@ defmodule Farside do
@fallback_suffix Application.fetch_env!(:farside, :fallback_suffix)
@previous_suffix Application.fetch_env!(:farside, :previous_suffix)
# Define relation between available services and their parent service.
# This enables Farside to redirect with links such as:
# farside.link/https://www.youtube.com/watch?v=dQw4w9WgXcQ
@parent_services %{
"youtube.com" => ["invidious", "piped"],
"reddit.com" => ["libreddit", "teddit"],
"instagram.com" => ["bibliogram"],
"twitter.com" => ["nitter"],
"wikipedia.org" => ["wikiless"],
"medium.com" => ["scribe"],
"odysee.com" => ["librarian"],
"imgur.com" => ["rimgo"],
"translate.google.com" => ["lingva"]
}
def get_services_map do
{:ok, service_list} = Redix.command(:redix, ["KEYS", "#{@service_prefix}*"])
@ -26,7 +41,42 @@ defmodule Farside do
end)
end
def get_service(service) do
# Check if service has an entry in Redis, otherwise try to
# match against available parent services
service_name = cond do
!check_service(service) ->
Enum.find_value(
@parent_services,
fn {k, v} ->
service =~ k && Enum.random(v)
end)
true ->
service
end
service_name
end
def check_service(service) do
# Checks to see if a specific service has instances available
# in redis
{:ok, instances} =
Redix.command(
:redix,
[
"LRANGE",
"#{@service_prefix}#{service}",
"0",
"-1"
]
)
Enum.count(instances) > 0
end
def last_instance(service) do
# Fetches the last selected instance for a particular service
{:ok, previous} =
Redix.command(
:redix,

View file

@ -39,12 +39,27 @@ defmodule Farside.Router do
end
get "/:service/*glob" do
path = Enum.join(glob, "/")
service_name = cond do
service =~ "http" ->
List.first(glob)
true ->
service
end
path = cond do
service_name != service ->
Enum.join(Enum.slice(glob, 1..-1), "/")
true ->
Enum.join(glob, "/")
end
instance = cond do
conn.assigns[:throttle] != nil ->
Farside.last_instance(service)
Farside.get_service(service_name)
|> Farside.last_instance
true ->
Farside.pick_instance(service)
Farside.get_service(service_name)
|> Farside.pick_instance
end
params =

View file

@ -77,4 +77,22 @@ defmodule FarsideTest do
assert first_redirect != second_redirect
end)
end
test "/https://..." do
parent_service = "https://www.youtube.com"
parent_path = "watch?v=dQw4w9WgXcQ"
conn = test_conn("/#{parent_service}/#{parent_path}")
redirect = elem(List.last(conn.resp_headers), 1)
IO.puts("")
IO.puts(" /#{parent_service}/#{parent_path}")
IO.puts(" redirected to")
IO.puts(" #{redirect}")
assert conn.state == :set
assert conn.status == 302
assert redirect =~ parent_path
assert !(redirect =~ parent_service)
end
end