168 lines
5.4 KiB
Elixir
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
|