fp/apps/bright/lib/bright/torrentfile.ex
CJ_Clippy 873c3e0fd8
Some checks failed
ci / Tests & Checks (push) Failing after 46s
ci / build (push) Failing after 4m34s
acceptance tests pass omg omg
2025-02-19 13:09:53 -08:00

168 lines
5.4 KiB
Elixir

defmodule Bright.Torrentfile do
@moduledoc """
Provides functions to work with [torrentfile](https://github.com/alexpdev/torrentfile) CLI program
"""
require Logger
alias Bright.Cache
alias Bright.Streams.Vod
@spec bittorrent_tracker_url() :: binary()
def bittorrent_tracker_url do
case Application.fetch_env!(:bright, :torrent)[:tracker_url] do
nil -> raise "tracker_url missing or empty in app config"
"" -> raise "tracker_url missing or empty in app config"
url -> url
end
end
@spec site_url() :: binary()
def site_url do
case Application.fetch_env!(:bright, :site_url) do
nil -> raise "site_url missing or empty in app config"
"" -> raise "site_url missing or empty in app config"
url -> url
end
end
# @spec execute(command :: Command.t) :: {:ok, binary()} | {:error, {Collectable.t, exit_status :: non_neg_integer}}
# def execute(%Command{} = command) do
# {executable, args} = prepare(command)
# Rambo.run(executable, args, log: false)
# |> format_output()
# end
# @spec prepare(command :: Command.t) :: {binary() | nil, list(binary)}
# def prepare(%Command{files: files, global_options: options}) do
# options = Enum.map(options, &arg_for_option/1)
# cmd_args = List.flatten([options, options_list(files)])
# {ffmpeg_path(), cmd_args}
# end
# $ torrentfile \
# create \
# --prog 0 \
# -o test-fixture.torrent \
# -a https://tracker.futureporn.net/announce \
# --source https://futureporn.net \
# --web-seed=https://futureporn-b2.b-cdn.net/test-fixture.ts \
# --meta-version 2 \
# /home/cj/Documents/futureporn-monorepo/services/bright/test/fixtures/test-fixture.ts
def version do
case Rambo.run(torrentfile_path(), ["-V"]) do
{:ok, %Rambo{status: 0, out: output}} ->
case Regex.run(~r/(v[\d.]+)/, output) do
[_, version] -> {:ok, version}
_ -> {:error, "Version not found"}
end
{:error, reason} ->
{:error, reason}
end
end
# def parse_output(output) do
# magnet = extract_last(Regex.run(~r/(magnet:\?[^\s]+)/, output))
# save_path = extract_last(Regex.run(~r/Torrent Save Path:\s+(.+)/, output))
# btih = extract_last(Regex.run(~r/\burn:btih:([A-F\d]+)\b/i, magnet))
# btmh = extract_last(Regex.run(~r/\burn:btmh:([A-F\d]+)\b/i, magnet))
# %{magnet: magnet, save_path: save_path, btih: btih, btmh: btmh}
# end
def parse_output(output) do
magnet = extract_last(Regex.run(~r/(magnet:\?[^\s]+)/, output))
save_path = extract_last(Regex.run(~r/Torrent Save Path:\s+(.+)/, output))
btih = if magnet, do: extract_last(Regex.run(~r/\burn:btih:([A-F\d]+)\b/i, magnet)), else: nil
btmh = if magnet, do: extract_last(Regex.run(~r/\burn:btmh:([A-F\d]+)\b/i, magnet)), else: nil
%{magnet: magnet, save_path: save_path, btih: btih, btmh: btmh}
end
defp extract_last(nil), do: nil
defp extract_last(list) when is_list(list), do: List.last(list)
def create(%Vod{} = vod, input_path) do
output_path = Cache.generate_filename("vod-#{vod.id}", "torrent")
# FYI for deterministic test purposes, tracker_url and site_url have no effect on the info_hash.
tracker_url = "udp://tracker.futureporn.net/"
site_url = "https://futureporn.net/"
comment = site_url
# Setting the source_url to https://futureporn.net/vods/n would be cool,
# but doing that means getting a different info_hash every time during testing.
# we want a consistent, deterministic info_hash for our integration tests.
# there is probably a way to have our cake and eat it too, but
# for now in order to have a consistent info_hash, we settle
# for site_url instead of a more specific URL
# @see https://stackoverflow.com/a/28601408/1004931
source_url = site_url
web_seed_url = vod.s3_cdn_url
meta_version = 3
Logger.debug(
"source_url=#{source_url}, output_path=#{output_path}, tracker_url=#{tracker_url}"
)
idk =
create(
input_path,
output_path,
tracker_url,
comment,
source_url,
web_seed_url,
meta_version
)
Logger.debug(inspect(idk))
idk
end
def create(
input_path,
output_path,
tracker_url,
source_url,
comment,
web_seed_url,
meta_version
) do
Logger.debug(
"Torrentfile.create (torrentfile_path()=#{inspect(torrentfile_path())}) called with args input_path=#{input_path}, output_path=#{output_path}, tracker_url=#{tracker_url}, source_url=#{source_url}, comment=#{comment}, web_seed_url=#{web_seed_url}, meta_version=#{meta_version}"
)
case Rambo.run(torrentfile_path(), [
"--magnet",
"--prog",
"0",
"--out",
output_path,
"--announce",
tracker_url,
"--source",
source_url,
"--comment",
comment,
"--web-seed",
web_seed_url,
"--meta-version",
to_string(meta_version),
input_path
]) do
{:error, reason} -> {:error, reason}
{:ok, %Rambo{status: 0, out: out}} -> {:ok, parse_output(out)}
end
end
def torrentfile_path do
case Application.get_env(:bright, :torrentfile_path, nil) do
nil -> System.find_executable("torrentfile")
path -> path
end
end
end