* Add media streaming (#108) * [WIP] set up streaming route * Added UUID to sources and media items * Added media preview to MI show page * Added plug to strip file extensions * [VERY WIP] basic podcast RSS setup * [WIP] got basic podcast RSS working * [WIP] more expanding on RSS * Comment * [WIP] Working on refactoring feed * Added UUID backfill to a migration * [WIP] Moar refactoring * [WIP] Adding UI for getting RSS feed * Many tests * Added conditional routing for feed URLs * Removed the need for url_base to be set * Updated preset name * Rendered certain fields HTML-safe; Added logging to confirm range request support * Fixed incorrect scheme issue * Updated env var * Updated other UI to use dropdown * removed commented code * Generate rss feeds (#123) * Added plug to strip file extensions * [VERY WIP] basic podcast RSS setup * [WIP] got basic podcast RSS working * [WIP] more expanding on RSS * [WIP] Working on refactoring feed * Added UUID backfill to a migration * [WIP] Moar refactoring * [WIP] Adding UI for getting RSS feed * Many tests * Added conditional routing for feed URLs * Removed the need for url_base to be set * Updated preset name * Rendered certain fields HTML-safe; Added logging to confirm range request support * Fixed incorrect scheme issue * Updated env var * Updated other UI to use dropdown * removed commented code * docs * Added unique index to UUID fields
128 lines
3.6 KiB
Elixir
128 lines
3.6 KiB
Elixir
defmodule Pinchflat.Sources.Source do
|
|
@moduledoc """
|
|
The Source schema.
|
|
"""
|
|
|
|
use Ecto.Schema
|
|
import Ecto.Changeset
|
|
import Pinchflat.Utils.ChangesetUtils
|
|
|
|
alias Pinchflat.Tasks.Task
|
|
alias Pinchflat.Media.MediaItem
|
|
alias Pinchflat.Profiles.MediaProfile
|
|
alias Pinchflat.Metadata.SourceMetadata
|
|
|
|
@allowed_fields ~w(
|
|
collection_name
|
|
collection_id
|
|
collection_type
|
|
custom_name
|
|
description
|
|
nfo_filepath
|
|
poster_filepath
|
|
fanart_filepath
|
|
banner_filepath
|
|
series_directory
|
|
index_frequency_minutes
|
|
fast_index
|
|
download_media
|
|
last_indexed_at
|
|
original_url
|
|
download_cutoff_date
|
|
title_filter_regex
|
|
media_profile_id
|
|
)a
|
|
|
|
# Expensive API calls are made when a source is inserted/updated so
|
|
# we want to ensure that the source is valid before making the call.
|
|
# This way, we check that the other attributes are valid before ensuring
|
|
# that all fields are valid. This is still only one DB insert but it's
|
|
# a two-stage validation process to fail fast before the API call.
|
|
@initially_required_fields ~w(
|
|
index_frequency_minutes
|
|
fast_index
|
|
download_media
|
|
original_url
|
|
media_profile_id
|
|
)a
|
|
|
|
@pre_insert_required_fields @initially_required_fields ++
|
|
~w(
|
|
uuid
|
|
custom_name
|
|
collection_name
|
|
collection_id
|
|
collection_type
|
|
)a
|
|
|
|
schema "sources" do
|
|
# This is _not_ used as the primary key or internally in the database
|
|
# relations. This is only used to prevent an enumeration attack on the streaming
|
|
# and RSS feed endpoints since those _must_ be public (ie: no basic auth)
|
|
field :uuid, Ecto.UUID
|
|
|
|
field :custom_name, :string
|
|
field :description, :string
|
|
field :collection_name, :string
|
|
field :collection_id, :string
|
|
field :collection_type, Ecto.Enum, values: [:channel, :playlist]
|
|
field :index_frequency_minutes, :integer, default: 60 * 24
|
|
field :fast_index, :boolean, default: false
|
|
field :download_media, :boolean, default: true
|
|
field :last_indexed_at, :utc_datetime
|
|
# Only download media items that were published after this date
|
|
field :download_cutoff_date, :date
|
|
field :original_url, :string
|
|
field :title_filter_regex, :string
|
|
|
|
field :series_directory, :string
|
|
field :nfo_filepath, :string
|
|
field :poster_filepath, :string
|
|
field :fanart_filepath, :string
|
|
field :banner_filepath, :string
|
|
|
|
belongs_to :media_profile, MediaProfile
|
|
|
|
has_one :metadata, SourceMetadata, on_replace: :update
|
|
|
|
has_many :tasks, Task
|
|
has_many :media_items, MediaItem, foreign_key: :source_id
|
|
|
|
timestamps(type: :utc_datetime)
|
|
end
|
|
|
|
@doc false
|
|
def changeset(source, attrs, validation_stage) do
|
|
# See above for rationale
|
|
required_fields =
|
|
if validation_stage == :initial do
|
|
@initially_required_fields
|
|
else
|
|
@pre_insert_required_fields
|
|
end
|
|
|
|
source
|
|
|> cast(attrs, @allowed_fields)
|
|
|> dynamic_default(:custom_name, fn cs -> get_field(cs, :collection_name) end)
|
|
|> dynamic_default(:uuid, fn _ -> Ecto.UUID.generate() end)
|
|
|> validate_required(required_fields)
|
|
|> cast_assoc(:metadata, with: &SourceMetadata.changeset/2, required: false)
|
|
end
|
|
|
|
@doc false
|
|
def index_frequency_when_fast_indexing do
|
|
# 30 days in minutes
|
|
60 * 24 * 30
|
|
end
|
|
|
|
@doc false
|
|
def fast_index_frequency do
|
|
# minutes
|
|
15
|
|
end
|
|
|
|
@doc false
|
|
def filepath_attributes do
|
|
~w(nfo_filepath fanart_filepath poster_filepath banner_filepath)a
|
|
end
|
|
end
|