defmodule Pinchflat.Profiles.MediaProfile do @moduledoc """ A media profile is a set of configuration options that can be applied to many media sources """ use Ecto.Schema import Ecto.Changeset alias __MODULE__ alias Pinchflat.Sources.Source @allowed_fields ~w( name output_path_template download_subs download_auto_subs embed_subs sub_langs download_thumbnail embed_thumbnail download_source_images download_metadata embed_metadata download_nfo sponsorblock_behaviour sponsorblock_categories shorts_behaviour livestream_behaviour audio_track preferred_resolution media_container redownload_delay_days marked_for_deletion_at )a @required_fields ~w(name output_path_template)a schema "media_profiles" do field :name, :string field :redownload_delay_days, :integer field :output_path_template, :string, default: "/{{ source_custom_name }}/{{ upload_yyyy_mm_dd }} {{ title }}/{{ title }} [{{ id }}].{{ ext }}" field :download_subs, :boolean, default: false field :download_auto_subs, :boolean, default: false field :embed_subs, :boolean, default: false field :sub_langs, :string, default: "en" field :download_thumbnail, :boolean, default: false field :embed_thumbnail, :boolean, default: false field :download_source_images, :boolean, default: false field :download_metadata, :boolean, default: false field :embed_metadata, :boolean, default: false field :download_nfo, :boolean, default: false field :sponsorblock_behaviour, Ecto.Enum, values: [:disabled, :mark, :remove], default: :disabled field :sponsorblock_categories, {:array, :string}, default: [] # NOTE: these do NOT speed up indexing - the indexer still has to go # through the entire collection to determine if a media is a short or # a livestream. # NOTE: these can BOTH be set to :only which will download shorts and # livestreams _only_ and ignore regular media. The redundant case # is when one is set to :only and the other is set to :exclude. # See `build_format_clauses` in the Media context for more. field :shorts_behaviour, Ecto.Enum, values: ~w(include exclude only)a, default: :include field :livestream_behaviour, Ecto.Enum, values: ~w(include exclude only)a, default: :include field :audio_track, :string field :preferred_resolution, Ecto.Enum, values: ~w(4320p 2160p 1440p 1080p 720p 480p 360p audio)a, default: :"1080p" field :media_container, :string, default: nil field :marked_for_deletion_at, :utc_datetime has_many :sources, Source timestamps(type: :utc_datetime) end @doc false def changeset(media_profile, attrs) do media_profile |> cast(attrs, @allowed_fields) |> validate_required(@required_fields) # Ensures it ends with `.{{ ext }}` or `.%(ext)s` or similar (with a little wiggle room) |> validate_format(:output_path_template, ext_regex(), message: "must end with .{{ ext }}") |> validate_number(:redownload_delay_days, greater_than_or_equal_to: 0) |> unique_constraint(:name) end @doc false def ext_regex do ~r/\.({{ ?ext ?}}|%\( ?ext ?\)[sS])$/ end @doc false def json_exluded_fields do ~w(__meta__ __struct__ sources)a end defimpl Jason.Encoder, for: MediaProfile do def encode(value, opts) do value |> Map.drop(MediaProfile.json_exluded_fields()) |> Jason.Encode.map(opts) end end end