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>