Fast indexing (#58)

* Made method to getting singular media details; Renamed other related method

* Takes a fun and flirty digression to remove abstractions around yt-dlp since I'm 100% committed to using it exclusively

* Removed commented test code

* Lays the groundwork for fast indexing

* Added module for working with youtube RSS feed

* Added methods to kick off indexing workers from RSS response

* Improve short detection (#59)

* Made media attribute-related yt-dlp calls return a struct

* Added shorts attribute to media items

* Added ability to discern a short from yt-dlp response

* Updated search to use new shorts attribute

* Fast index UI (#63)

* Added fast_index field and adds it to source form

* Added fast indexing to source changeset operations

* Added fast indexing worker and updated other modules to start using it

* Handled fast index worker on source update

* Add support modals (#65)

* Added fast indexing upgrade modal

* Improved modal on smaller screens

* Updated links to work again

* Added donation modal

* Reverted source fast index to 15 minutes

* Removed unneeded HTML attributes from old alpine approach
This commit is contained in:
Kieran
2024-03-10 14:36:34 -07:00
committed by GitHub
parent 128485c30a
commit e1f8e94686
72 changed files with 1698 additions and 626 deletions
+30 -20
View File
@@ -40,6 +40,7 @@ defmodule PinchflatWeb.CoreComponents do
"""
attr :id, :string, required: true
attr :show, :boolean, default: false
attr :allow_close, :boolean, default: true
attr :on_cancel, JS, default: %JS{}
slot :inner_block, required: true
@@ -50,9 +51,9 @@ defmodule PinchflatWeb.CoreComponents do
phx-mounted={@show && show_modal(@id)}
phx-remove={hide_modal(@id)}
data-cancel={JS.exec(@on_cancel, "phx-remove")}
class="relative z-50 hidden"
class="relative z-99999 hidden"
>
<div id={"#{@id}-bg"} class="bg-zinc-50/90 fixed inset-0 transition-opacity" aria-hidden="true" />
<div id={"#{@id}-bg"} class="bg-black-2/80 fixed inset-0 transition-opacity" aria-hidden="true" />
<div
class="fixed inset-0 overflow-y-auto"
aria-labelledby={"#{@id}-title"}
@@ -62,28 +63,28 @@ defmodule PinchflatWeb.CoreComponents do
tabindex="0"
>
<div class="flex min-h-full items-center justify-center">
<div class="w-full max-w-3xl p-4 sm:p-6 lg:py-8">
<.focus_wrap
<div class="w-full max-w-3xl p-2 sm:p-6 lg:py-8">
<div
id={"#{@id}-container"}
phx-window-keydown={JS.exec("data-cancel", to: "##{@id}")}
phx-window-keydown={@allow_close && JS.exec("data-cancel", to: "##{@id}")}
phx-key="escape"
phx-click-away={JS.exec("data-cancel", to: "##{@id}")}
class="shadow-zinc-700/10 ring-zinc-700/10 relative hidden rounded-2xl bg-white p-14 shadow-lg ring-1 transition"
phx-click-away={@allow_close && JS.exec("data-cancel", to: "##{@id}")}
class="shadow-zinc-700/10 ring-zinc-700/10 relative hidden rounded-2xl bg-graydark p-8 sm:p-14 shadow-lg ring-1 transition"
>
<div class="absolute top-6 right-5">
<div :if={@allow_close} class="absolute top-6 right-5">
<button
phx-click={JS.exec("data-cancel", to: "##{@id}")}
type="button"
class="-m-3 flex-none p-3 opacity-20 hover:opacity-40"
class="-m-3 flex-none p-3 opacity-60 hover:opacity-80"
aria-label={gettext("close")}
>
<.icon name="hero-x-mark-solid" class="h-5 w-5" />
<.icon name="hero-x-mark-solid" class="h-5 w-5 text-white" />
</button>
</div>
<div id={"#{@id}-content"}>
<%= render_slot(@inner_block) %>
</div>
</.focus_wrap>
</div>
</div>
</div>
</div>
@@ -243,6 +244,7 @@ defmodule PinchflatWeb.CoreComponents do
attr :id, :any, default: nil
attr :name, :any
attr :label, :string, default: nil
attr :label_suffix, :string, default: nil
attr :value, :any
attr :help, :string, default: nil
@@ -294,6 +296,7 @@ defmodule PinchflatWeb.CoreComponents do
{@rest}
/>
<%= @label %>
<span :if={@label_suffix} class="text-xs text-bodydark"><%= @label_suffix %></span>
</label>
<.help :if={@help}><%= @help %></.help>
<.error :for={msg <- @errors}><%= msg %></.error>
@@ -309,7 +312,10 @@ defmodule PinchflatWeb.CoreComponents do
~H"""
<div x-data={"{ enabled: #{@checked}}"}>
<.label for={@id}><%= @label %></.label>
<.label for={@id}>
<%= @label %>
<span :if={@label_suffix} class="text-xs text-bodydark"><%= @label_suffix %></span>
</.label>
<div class="relative">
<input type="hidden" name={@name} value="false" />
<input
@@ -343,7 +349,9 @@ defmodule PinchflatWeb.CoreComponents do
def input(%{type: "select"} = assigns) do
~H"""
<div phx-feedback-for={@name}>
<.label for={@id}><%= @label %></.label>
<.label for={@id}>
<%= @label %><span :if={@label_suffix} class="text-xs text-bodydark"><%= @label_suffix %></span>
</.label>
<select
id={@id}
name={@name}
@@ -367,7 +375,9 @@ defmodule PinchflatWeb.CoreComponents do
def input(%{type: "textarea"} = assigns) do
~H"""
<div phx-feedback-for={@name}>
<.label for={@id}><%= @label %></.label>
<.label for={@id}>
<%= @label %><span :if={@label_suffix} class="text-xs text-bodydark"><%= @label_suffix %></span>
</.label>
<textarea
id={@id}
name={@name}
@@ -390,7 +400,9 @@ defmodule PinchflatWeb.CoreComponents do
def input(assigns) do
~H"""
<div phx-feedback-for={@name}>
<.label for={@id}><%= @label %></.label>
<.label for={@id}>
<%= @label %><span :if={@label_suffix} class="text-xs text-bodydark"><%= @label_suffix %></span>
</.label>
<input
type={@type}
name={@name}
@@ -608,15 +620,15 @@ defmodule PinchflatWeb.CoreComponents do
## Examples
<.back navigate={~p"/posts"}>Back to posts</.back>
<.back href={~p"/posts"}>Back to posts</.back>
"""
attr :navigate, :any, required: true
attr :href, :any, required: true
slot :inner_block, required: true
def back(assigns) do
~H"""
<div class="mt-16">
<.link navigate={@navigate} class="text-sm font-semibold leading-6 text-zinc-900 hover:text-zinc-700">
<.link href={@href} class="text-sm font-semibold leading-6 text-zinc-900 hover:text-zinc-700">
<.icon name="hero-arrow-left-solid" class="h-3 w-3" />
<%= render_slot(@inner_block) %>
</.link>
@@ -685,7 +697,6 @@ defmodule PinchflatWeb.CoreComponents do
)
|> show("##{id}-container")
|> JS.add_class("overflow-hidden", to: "body")
|> JS.focus_first(to: "##{id}-content")
end
def hide_modal(js \\ %JS{}, id) do
@@ -697,7 +708,6 @@ defmodule PinchflatWeb.CoreComponents do
|> hide("##{id}-container")
|> JS.hide(to: "##{id}", transition: {"block", "block", "hidden"})
|> JS.remove_class("overflow-hidden", to: "body")
|> JS.pop_focus()
end
@doc """
@@ -14,7 +14,9 @@ defmodule PinchflatWeb.CustomComponents.ButtonComponents do
attr :color, :string, default: "bg-primary"
attr :rounding, :string, default: "rounded-sm"
attr :class, :string, default: ""
attr :type, :string, default: "submit"
attr :disabled, :boolean, default: false
attr :rest, :global
slot :inner_block, required: true
@@ -26,9 +28,11 @@ defmodule PinchflatWeb.CustomComponents.ButtonComponents do
"#{@rounding} inline-flex items-center justify-center px-8 py-4",
"#{@color}",
"hover:bg-opacity-90 lg:px-8 xl:px-10",
"disabled:bg-opacity-50 disabled:cursor-not-allowed disabled:text-gray-2",
@class
]}
disabled={@disabled}
{@rest}
>
<%= render_slot(@inner_block) %>
</button>
+4 -2
View File
@@ -6,14 +6,16 @@ defmodule PinchflatWeb.Layouts do
attr :icon, :string, required: true
attr :text, :string, required: true
attr :navigate, :any, required: true
attr :href, :any, required: true
attr :target, :any, default: "_self"
def sidebar_item(assigns) do
# I'm testing out grouping classes here. Tentative order: font, layout, color, animation, state-modifiers
~H"""
<li>
<.link
navigate={@navigate}
href={@href}
target={@target}
class={[
"font-medium text-bodydark1",
"group relative flex items-center gap-2.5 rounded-sm px-4 py-2 duration-300 ease-in-out",
@@ -0,0 +1,26 @@
<.modal id="donate-modal" allow_close={true}>
<section x-data="{ inputValue: '' }">
<h3 class="text-2xl text-white">Donate</h3>
<p class="text-sm">Thank you for your support :&rpar;</p>
<p class="mt-4">
If you find the project valuable and want to support its development, a
<.inline_link href="https://www.paypal.me/kieraneglin">donation</.inline_link>
would be greatly appreciated.
</p>
<p class="mt-4">
Plus, $5 USD from any donation over $10 USD will be donated to
<.inline_link href="https://www.eff.org/">The Electronic Frontier Foundation</.inline_link>
who defend your online liberties and backed
<.inline_code>youtube-dl</.inline_code>
when Google took them down<.inline_link href="https://github.com/github/dmca/blob/9a85e0f021f7967af80e186b890776a50443f06c/2020/11/2020-11-16-RIAA-reversal-effletter.pdf">
<.icon name="hero-arrow-top-right-on-square" class="h-3 w-3" />
</.inline_link>.
</p>
<.link href="https://www.paypal.me/kieraneglin" target="_blank">
<.button color="bg-primary" class="w-full mt-8">
Donate
</.button>
</.link>
</section>
</.modal>
@@ -1,31 +1,58 @@
<aside
x-bind:class="sidebarVisible ? 'translate-x-0' : '-translate-x-full'"
class={[
"-translate-x-full absolute left-0 top-0 z-9999 flex h-screen w-60 flex-col overflow-y-hidden",
"-translate-x-full absolute left-0 top-0 z-9999 flex h-screen w-60 flex-col overflow-y-hidden justify-between",
"bg-black duration-300 ease-linear shadow-lg sm:shadow-none dark:bg-boxdark lg:static lg:translate-x-0"
]}
@click.outside="sidebarVisible = false"
>
<div class="flex items-center justify-between gap-2 px-6 py-5.5 lg:py-6.5">
<a href="/" class="flex items-center">
<img src={~p"/images/logo.png?cachebust=2024-02-29"} alt="Pinchflat" class="w-9 h-9" />
<h2 class="text-xl font-bold text-white pl-2">Pinchflat</h2>
</a>
<section>
<div class="flex items-center justify-between gap-2 px-6 py-5.5 lg:py-6.5">
<a href="/" class="flex items-center">
<img src={~p"/images/logo.png?cachebust=2024-02-29"} alt="Pinchflat" class="w-9 h-9" />
<h2 class="text-xl font-bold text-white pl-2">Pinchflat</h2>
</a>
<button class="block lg:hidden" @click.stop="sidebarVisible = !sidebarVisible">
<.icon name="hero-arrow-left" class="fill-current" />
</button>
</div>
<div class="no-scrollbar flex flex-col overflow-y-auto duration-300 ease-linear">
<nav class="mt-5 px-4 py-4 lg:mt-9 lg:px-6">
<div>
<button class="block lg:hidden" @click.stop="sidebarVisible = !sidebarVisible">
<.icon name="hero-arrow-left" class="fill-current" />
</button>
</div>
<div class="no-scrollbar flex flex-col overflow-y-auto duration-300 ease-linear">
<nav class="mt-5 px-4 py-4 lg:mt-9 lg:px-6">
<h3 class="mb-4 ml-4 text-sm font-medium text-bodydark2">MENU</h3>
<ul class="mb-6 flex flex-col gap-1.5">
<.sidebar_item icon="hero-home" text="Home" navigate={~p"/"} />
<.sidebar_item icon="hero-tv" text="Sources" navigate={~p"/sources"} />
<.sidebar_item icon="hero-adjustments-vertical" text="Media Profiles" navigate={~p"/media_profiles"} />
</ul>
</div>
<div class="flex flex-col justify-between">
<ul class="mb-6 flex flex-col gap-1.5">
<.sidebar_item icon="hero-home" text="Home" href={~p"/"} />
<.sidebar_item icon="hero-tv" text="Sources" href={~p"/sources"} />
<.sidebar_item icon="hero-adjustments-vertical" text="Media Profiles" href={~p"/media_profiles"} />
</ul>
</div>
</nav>
</div>
</section>
<section>
<nav class="mt-5 px-4 py-4 lg:mt-9 lg:px-6">
<ul class="mb-6 flex flex-col gap-1.5">
<.sidebar_item
icon="hero-code-bracket"
text="Github"
target="_blank"
href="https://github.com/kieraneglin/pinchflat"
/>
<li>
<span
class={[
"font-medium text-bodydark1",
"group relative flex items-center gap-2.5 rounded-sm px-4 py-2 duration-300 ease-in-out",
"duration-300 ease-in-out cursor-pointer",
"hover:bg-graydark dark:hover:bg-meta-4"
]}
phx-click={show_modal("donate-modal")}
>
<.icon name="hero-currency-dollar" /> Donate
</span>
</li>
</ul>
</nav>
</div>
</section>
</aside>
@@ -0,0 +1,40 @@
defmodule Pinchflat.UpgradeButtonLive do
use PinchflatWeb, :live_view
def render(assigns) do
~H"""
<form phx-change="check_matching_text">
<.input type="text" name="unlock-pro-textbox" value="" />
</form>
<.button
class="w-full mt-4"
type="button"
disabled={@button_disabled}
phx-click={hide_modal("upgrade-modal")}
x-on:click="setTimeout(() => { proEnabled = true }, 200)"
>
Unlock Pro
</.button>
"""
end
def mount(_params, _session, socket) do
{:ok, assign(socket, :button_disabled, true)}
end
def handle_event("check_matching_text", %{"unlock-pro-textbox" => text}, socket) do
normalized_text =
text
|> String.trim()
|> String.downcase()
if normalized_text == "got it!" do
Settings.set!(:pro_enabled, true)
{:noreply, update(socket, :button_disabled, fn _ -> false end)}
else
{:noreply, update(socket, :button_disabled, fn _ -> true end)}
end
end
end
@@ -0,0 +1,28 @@
<.modal id="upgrade-modal" allow_close={false}>
<section>
<h3 class="text-2xl text-white">Pro Mode</h3>
<p class="text-sm">Don't worry - Pinchflat is completely free :&rpar;</p>
<p class="mt-4">
If you find the project valuable and want to support its development, a
<.inline_link href="https://www.paypal.me/kieraneglin">donation</.inline_link>
would be greatly appreciated.
</p>
<p class="mt-4">
Plus, $5 USD from any donation over $10 USD will be donated to
<.inline_link href="https://www.eff.org/">The Electronic Frontier Foundation</.inline_link>
who defend your online liberties and backed
<.inline_code>youtube-dl</.inline_code>
when Google took them down<.inline_link href="https://github.com/github/dmca/blob/9a85e0f021f7967af80e186b890776a50443f06c/2020/11/2020-11-16-RIAA-reversal-effletter.pdf">
<.icon name="hero-arrow-top-right-on-square" class="h-3 w-3" />
</.inline_link>. <strong>You do not need to donate to unlock Pro</strong>. It's just a way to say thanks!
</p>
<p class="mt-4">
To unlock Pro, simply type
<.inline_code>got it!</.inline_code>
into the text box and press the button.
</p>
<%= live_render(@conn, Pinchflat.UpgradeButtonLive) %>
</section>
</.modal>
@@ -12,7 +12,15 @@
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
</script>
</head>
<body x-data="{ sidebarVisible: false }" class="dark text-bodydark bg-boxdark-2">
<body
x-data={"{ sidebarVisible: false, proEnabled: #{Settings.get!(:pro_enabled)} }"}
class="dark text-bodydark bg-boxdark-2"
>
<%= @inner_content %>
<.donate_modal conn={@conn} />
<template x-if="!proEnabled">
<.upgrade_modal conn={@conn} />
</template>
</body>
</html>
@@ -1,6 +1,6 @@
<div class="mb-6 flex gap-3 flex-row items-center justify-between">
<div class="flex gap-3 items-center">
<.link navigate={~p"/sources/#{@media_item.source_id}"}>
<.link href={~p"/sources/#{@media_item.source_id}"}>
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
</.link>
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">
@@ -1,5 +1,5 @@
<div class="mb-6 flex gap-3 flex-row items-center">
<.link navigate={~p"/media_profiles"}>
<.link href={~p"/media_profiles"}>
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
</.link>
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">Edit Media Profile</h2>
@@ -3,7 +3,7 @@
Media Profiles
</h2>
<nav>
<.link navigate={~p"/media_profiles/new"}>
<.link href={~p"/media_profiles/new"}>
<.button color="bg-primary" rounding="rounded-full">
<span class="font-bold text-xl mx-2">+</span> New <span class="hidden sm:inline pl-1">Media Profile</span>
</.button>
@@ -1,5 +1,5 @@
<div class="mb-6 flex gap-3 flex-row items-center">
<.link :if={!Settings.get!(:onboarding)} navigate={~p"/media_profiles"}>
<.link :if={!Settings.get!(:onboarding)} href={~p"/media_profiles"}>
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
</.link>
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">New Media Profile</h2>
@@ -1,6 +1,6 @@
<div class="mb-6 flex gap-3 flex-row items-center justify-between">
<div class="flex items-center">
<.link navigate={~p"/media_profiles"}>
<.link href={~p"/media_profiles"}>
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
</.link>
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-2">
@@ -9,7 +9,7 @@
</div>
<nav>
<.link navigate={~p"/media_profiles/#{@media_profile}/edit"}>
<.link href={~p"/media_profiles/#{@media_profile}/edit"}>
<.button color="bg-primary" rounding="rounded-full">
<.icon name="hero-pencil-square" class="mr-2" />Edit <span class="hidden sm:inline pl-1">Media Profile</span>
</.button>
@@ -10,7 +10,7 @@
<p class="text-md text-bodydark">Media Profiles set your preferences for fetching and downloading media.</p>
<p class="text-md text-bodydark">Don't worry, you can create more Media Profiles later!</p>
<div class="mt-8">
<.link navigate={~p"/media_profiles/new"}>
<.link href={~p"/media_profiles/new"}>
<.button color="bg-primary" rounding="rounded-full" disabled={@media_profiles_exist}>
<span class="font-bold mx-2">+</span> New Media Profile
</.button>
@@ -24,7 +24,7 @@
Each Media Profile can control many Sources so it's easy to add more content!
</p>
<div class="mt-8">
<.link navigate={~p"/sources/new"}>
<.link href={~p"/sources/new"}>
<.button color="bg-primary" rounding="rounded-full" disabled={not @media_profiles_exist}>
<span class="font-bold mx-2">+</span> New Source
</.button>
@@ -39,7 +39,7 @@
</p>
<p class="text-md text-bodydark">Feel free to add more Media Profiles or Sources in the meantime!</p>
<div class="mt-8">
<.link navigate={~p"/"}>
<.link href={~p"/"}>
<.button color="bg-primary" rounding="rounded-full" disabled={not @sources_exist}>
Let's Go <span class="font-bold mx-2">🚀</span>
</.button>
@@ -17,7 +17,7 @@
</:col>
<:col :let={result} label="" class="flex place-content-evenly">
<.link
navigate={~p"/sources/#{result.source_id}/media/#{result.id}"}
href={~p"/sources/#{result.source_id}/media/#{result.id}"}
class="hover:text-secondary duration-200 ease-in-out mx-0.5"
>
<.icon name="hero-eye" />
@@ -3,8 +3,9 @@ defmodule PinchflatWeb.Sources.SourceController do
alias Pinchflat.Repo
alias Pinchflat.Media
alias Pinchflat.Profiles
alias Pinchflat.Tasks
alias Pinchflat.Sources
alias Pinchflat.Profiles
alias Pinchflat.Sources.Source
def index(conn, _params) do
@@ -43,15 +44,18 @@ defmodule PinchflatWeb.Sources.SourceController do
end
def show(conn, %{"id" => id}) do
source =
id
|> Sources.get_source!()
|> Repo.preload([:media_profile, tasks: [:job]])
source = Repo.preload(Sources.get_source!(id), :media_profile)
pending_tasks = Repo.preload(Tasks.list_pending_tasks_for(:source_id, source.id), :job)
pending_media = Media.list_pending_media_items_for(source, limit: 100)
downloaded_media = Media.list_downloaded_media_items_for(source, limit: 100)
render(conn, :show, source: source, pending_media: pending_media, downloaded_media: downloaded_media)
render(conn, :show,
source: source,
pending_tasks: pending_tasks,
pending_media: pending_media,
downloaded_media: downloaded_media
)
end
def edit(conn, %{"id" => id}) do
@@ -1,5 +1,5 @@
<div class="mb-6 flex gap-3 flex-row items-center">
<.link navigate={~p"/sources"}>
<.link href={~p"/sources"}>
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
</.link>
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">Edit Source</h2>
@@ -0,0 +1,21 @@
<aside>
<h2 class="text-xl font-bold mb-2">What is fast indexing (experimental)?</h2>
<section class="ml-2 md:ml-4 mb-4 max-w-prose">
<p>
Indexing is the act of scanning a channel or playlist (aka: source) for new media.
</p>
<p class="mt-2">
Normal indexing uses <code class="text-sm">yt-dlp</code>
to scan the entire source on your specified frequency, but it's very slow for large sources. This is the most accurate way to find uploaded media with the tradeoff being that pairing a large source with a low index frequency will result in you spending most of your time indexing. Only so many indexing operations can be running at the same time, so this can impact your other source's ability to index.
</p>
<p class="mt-2">
Fast indexing takes a different approach. It still does an initial scan the slow way but after that it uses an RSS feed to frequently check for new videos. This has the potential to be hundreds of times faster, but it can miss videos if the uploader un-privates an old video or uploads dozens of videos in the space of a few minutes. It works well for most channels or playlists but it's not perfect.
</p>
<p class="mt-2">
To make up for this limitation, a normal index is still run monthly to catch any videos that were missed by fast indexing. Fast indexing overrides the normal index frequency.
</p>
<p class="mt-2">
Fast indexing is experimental so please report any issues on GitHub. It's only recommended for sources with over 200-ish videos and that upload frequently. Not recommended for small or inactive sources.
</p>
</section>
</aside>
@@ -1,7 +1,7 @@
<div class="mb-6 flex gap-3 flex-row items-center justify-between">
<h2 class="text-title-md2 font-bold text-black dark:text-white">Sources</h2>
<nav>
<.link navigate={~p"/sources/new"}>
<.link href={~p"/sources/new"}>
<.button color="bg-primary" rounding="rounded-full">
<span class="font-bold mx-2">+</span> New <span class="hidden sm:inline pl-1">Source</span>
</.button>
@@ -22,7 +22,7 @@
</:col>
<:col :let={source} label="Media Profile">
<.link
navigate={~p"/media_profiles/#{source.media_profile_id}"}
href={~p"/media_profiles/#{source.media_profile_id}"}
class="hover:text-secondary duration-200 ease-in-out"
>
<%= source.media_profile.name %>
@@ -1,5 +1,5 @@
<div class="mb-6 flex gap-3 flex-row items-center">
<.link :if={!Settings.get!(:onboarding)} navigate={~p"/sources"}>
<.link :if={!Settings.get!(:onboarding)} href={~p"/sources"}>
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
</.link>
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">New Source</h2>
@@ -1,6 +1,6 @@
<div class="mb-6 flex gap-3 flex-row items-center justify-between">
<div class="flex gap-3 items-center">
<.link navigate={~p"/sources"}>
<.link href={~p"/sources"}>
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
</.link>
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">
@@ -9,7 +9,7 @@
</div>
<nav>
<.link navigate={~p"/sources/#{@source}/edit"}>
<.link href={~p"/sources/#{@source}/edit"}>
<.button color="bg-primary" rounding="rounded-full">
<.icon name="hero-pencil-square" class="mr-2" /> Edit <span class="hidden sm:inline pl-1">Source</span>
</.button>
@@ -84,9 +84,9 @@
<p class="text-black dark:text-white">Nothing Here!</p>
<% end %>
</:tab>
<:tab title="Tasks">
<%= if match?([_|_], @source.tasks) do %>
<.table rows={@source.tasks} table_class="text-black dark:text-white">
<:tab title="Pending Tasks">
<%= if match?([_|_], @pending_tasks) do %>
<.table rows={@pending_tasks} table_class="text-black dark:text-white">
<:col :let={task} label="Worker">
<%= task.job.worker %>
</:col>
@@ -3,6 +3,10 @@
Oops, something went wrong! Please check the errors below.
</.error>
<h3 class="mt-8 text-2xl text-black dark:text-white">
General Options
</h3>
<.input
field={f[:custom_name]}
type="text"
@@ -19,6 +23,10 @@
label="Media Profile"
/>
<h3 class="mt-8 text-2xl text-black dark:text-white">
Indexing Options
</h3>
<.input
field={f[:index_frequency_minutes]}
options={friendly_index_frequencies()}
@@ -27,6 +35,21 @@
help="Time between one index of this source finishing and the next one starting. Setting to 'On Create' will still run an initial index but no subsequent ones"
/>
<%!-- TODO: use Alpine to disable the index frequency when fast indexing is enabled --%>
<div phx-click={show_modal("upgrade-modal")}>
<.input
field={f[:fast_index]}
type="toggle"
label="Use Fast Indexing?"
label_suffix="(pro)"
help="Experimental. Ignores 'Index Frequency'. Recommended for large channels that upload frequently. See below for more info"
/>
</div>
<h3 class="mt-8 text-2xl text-black dark:text-white">
Downloading Options
</h3>
<.input
field={f[:download_media]}
type="toggle"
@@ -34,7 +57,9 @@
help="Unchecking still indexes media but it won't be downloaded until you enable this option"
/>
<:actions>
<.button class="my-10 sm:mb-7.5 w-full sm:w-auto">Save Source</.button>
</:actions>
<.button class="my-10 sm:mb-7.5 w-full sm:w-auto">Save Source</.button>
<div class="rounded-sm dark:bg-meta-4 p-4 md:p-6 mb-5">
<.fast_indexing_help />
</div>
</.simple_form>