Improves UI for media items, associated tasks

This commit is contained in:
Kieran Eglin 2024-02-29 15:24:59 -08:00
parent 6ba9b2f28e
commit 738e61a6c0
No known key found for this signature in database
GPG key ID: 193984967FCF432D
7 changed files with 139 additions and 99 deletions

View file

@ -7,6 +7,7 @@ defmodule Pinchflat.Sources.Source do
import Ecto.Changeset import Ecto.Changeset
import Pinchflat.Utils.ChangesetUtils import Pinchflat.Utils.ChangesetUtils
alias Pinchflat.Tasks.Task
alias Pinchflat.Media.MediaItem alias Pinchflat.Media.MediaItem
alias Pinchflat.Profiles.MediaProfile alias Pinchflat.Profiles.MediaProfile
@ -47,6 +48,7 @@ defmodule Pinchflat.Sources.Source do
belongs_to :media_profile, MediaProfile belongs_to :media_profile, MediaProfile
has_many :tasks, Task
has_many :media_items, MediaItem, foreign_key: :source_id has_many :media_items, MediaItem, foreign_key: :source_id
timestamps(type: :utc_datetime) timestamps(type: :utc_datetime)

View file

@ -23,7 +23,7 @@ defmodule PinchflatWeb.CustomComponents.TextComponents do
attr :href, :string, required: true attr :href, :string, required: true
slot :inner_block slot :inner_block
def reference_link(assigns) do def inline_link(assigns) do
~H""" ~H"""
<.link href={@href} target="_blank" class="text-blue-500 hover:text-blue-300"> <.link href={@href} target="_blank" class="text-blue-500 hover:text-blue-300">
<%= render_slot(@inner_block) %> <%= render_slot(@inner_block) %>

View file

@ -1,10 +1,14 @@
defmodule PinchflatWeb.MediaItems.MediaItemController do defmodule PinchflatWeb.MediaItems.MediaItemController do
use PinchflatWeb, :controller use PinchflatWeb, :controller
alias Pinchflat.Repo
alias Pinchflat.Media alias Pinchflat.Media
def show(conn, %{"id" => id}) do def show(conn, %{"id" => id}) do
media_item = Media.get_media_item!(id) media_item =
id
|> Media.get_media_item!()
|> Repo.preload([:source, tasks: [:job]])
render(conn, :show, media_item: media_item) render(conn, :show, media_item: media_item)
end end

View file

@ -8,23 +8,51 @@
</h2> </h2>
</div> </div>
</div> </div>
<div class="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1"> <div class="rounded-sm border border-stroke bg-white py-5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5">
<div class="max-w-full overflow-x-auto"> <div class="max-w-full overflow-x-auto">
<div class="flex flex-col gap-10 dark:text-white"> <.tabbed_layout>
<h3 class="font-bold text-xl">Attributes</h3> <:tab title="Attributes">
<.list_items_from_map map={Map.from_struct(@media_item)} /> <div class="flex flex-col gap-10 dark:text-white">
</div> <h3 class="font-bold text-xl">Attributes</h3>
<section>
<strong>Source:</strong>
<.inline_link href={~p"/sources/#{@media_item.source_id}"}>
<%= @media_item.source.friendly_name %>
</.inline_link>
</section>
<section class="flex justify-center my-10"> <.list_items_from_map map={Map.from_struct(@media_item)} />
<.link </div>
href={~p"/sources/#{@media_item.source_id}/media/#{@media_item}?delete_files=true"}
method="delete" <section class="flex justify-center my-10">
data-confirm="Are you sure you want to delete this record and all associated files on disk? This cannot be undone." <.link
> href={~p"/sources/#{@media_item.source_id}/media/#{@media_item}?delete_files=true"}
<.button color="bg-meta-1" rounding="rounded-full"> method="delete"
Delete Files data-confirm="Are you sure you want to delete this record and all associated files on disk? This cannot be undone."
</.button> >
</.link> <.button color="bg-meta-1" rounding="rounded-full">
</section> Delete Files
</.button>
</.link>
</section>
</:tab>
<:tab title="Tasks">
<%= if match?([_|_], @media_item.tasks) do %>
<.table rows={@media_item.tasks} table_class="text-black dark:text-white">
<:col :let={task} label="Worker">
<%= task.job.worker %>
</:col>
<:col :let={task} label="State">
<%= task.job.state %>
</:col>
<:col :let={task} label="Scheduled At">
<%= Calendar.strftime(task.job.scheduled_at, "%y-%m-%d %I:%M:%S %p %Z") %>
</:col>
</.table>
<% else %>
<p class="text-black dark:text-white">Nothing Here!</p>
<% end %>
</:tab>
</.tabbed_layout>
</div> </div>
</div> </div>

View file

@ -10,9 +10,9 @@
</li> </li>
<li> <li>
<code class="text-sm">yt-dlp</code>-style <code class="text-sm">yt-dlp</code>-style
<.reference_link href="https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#output-template"> <.inline_link href="https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#output-template">
<.icon name="hero-arrow-top-right-on-square" class="h-4 w-4" /> <.icon name="hero-arrow-top-right-on-square" class="h-4 w-4" />
</.reference_link>: </.inline_link>:
<.inline_code>/%(channel)s/%(duration>%H-%M-%S)s-%(id)s.%(ext)s</.inline_code> <.inline_code>/%(channel)s/%(duration>%H-%M-%S)s-%(id)s.%(ext)s</.inline_code>
</li> </li>
<li> <li>
@ -50,9 +50,9 @@
<p> <p>
Any single-word <code class="text-sm">yt-dlp</code> Any single-word <code class="text-sm">yt-dlp</code>
option option
<.reference_link href="https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#output-template"> <.inline_link href="https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#output-template">
<.icon name="hero-arrow-top-right-on-square" class="h-4 w-4" /> <.icon name="hero-arrow-top-right-on-square" class="h-4 w-4" />
</.reference_link> </.inline_link>
can be used with the curly braced liquid-style syntax. can be used with the curly braced liquid-style syntax.
This is just a list of the most common options as well as some custom aliases This is just a list of the most common options as well as some custom aliases
</p> </p>

View file

@ -54,10 +54,10 @@ defmodule PinchflatWeb.Sources.SourceController do
source = source =
id id
|> Sources.get_source!() |> Sources.get_source!()
|> Repo.preload(:media_profile) |> Repo.preload([:media_profile, tasks: [:job]])
pending_media = Media.list_pending_media_items_for(source) pending_media = Media.list_pending_media_items_for(source, limit: 100)
downloaded_media = Media.list_downloaded_media_items_for(source) 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_media: pending_media, downloaded_media: downloaded_media)
end end

View file

@ -18,83 +18,89 @@
</div> </div>
<div class="rounded-sm border border-stroke bg-white px-5 py-5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5"> <div class="rounded-sm border border-stroke bg-white px-5 py-5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5">
<div class="max-w-full overflow-x-auto"> <div class="max-w-full overflow-x-auto">
<div class="flex flex-col gap-10 dark:text-white"> <.tabbed_layout>
<h3 class="font-bold text-xl">Attributes for "<%= @source.friendly_name %>"</h3> <:tab title="Attributes">
<div class="flex flex-col gap-10 text-white">
<h3 class="font-bold text-lg">Attributes</h3>
<section>
<strong>Media Profile:</strong>
<.inline_link href={~p"/media_profiles/#{@source.media_profile_id}"}>
<%= @source.media_profile.name %>
</.inline_link>
</section>
<h3 class="font-bold text-lg">Relationships</h3> <.list_items_from_map map={Map.from_struct(@source)} />
<.list> </div>
<:item title="media_profile">
<section class="flex flex-col md:flex-row items-center md:justify-around mt-10">
<.link <.link
navigate={~p"/media_profiles/#{@source.media_profile_id}"} href={~p"/sources/#{@source}"}
class="hover:text-secondary duration-200 ease-in-out" method="delete"
data-confirm="Are you sure you want to delete this source (leaving files in place)? This cannot be undone."
> >
<%= @source.media_profile.name %> <.button color="bg-meta-1" rounding="rounded-full">
Delete Source
</.button>
</.link> </.link>
</:item> <.link
</.list> href={~p"/sources/#{@source}?delete_files=true"}
method="delete"
<h3 class="font-bold text-lg">Attributes</h3> data-confirm="Are you sure you want to delete this source and it's files on disk? This cannot be undone."
<.list_items_from_map map={Map.from_struct(@source)} /> class="mt-5 md:mt-0"
>
<h3 class="font-bold text-xl">Downloaded Media</h3> <.button color="bg-meta-1" rounding="rounded-full">
<%= if match?([_|_], @downloaded_media) do %> Delete Source and Files
<.table rows={@downloaded_media} table_class="text-black dark:text-white"> </.button>
<:col :let={media_item} label="Title"> </.link>
<%= StringUtils.truncate(media_item.title, 50) %> </section>
</:col> </:tab>
<:col :let={media_item} label="" class="flex place-content-evenly"> <:tab title="Pending Media">
<.link <%= if match?([_|_], @pending_media) do %>
navigate={~p"/sources/#{@source.id}/media/#{media_item.id}"} <h4 class="text-white text-lg mb-6">Shows a maximum of 100 media items</h4>
class="hover:text-secondary duration-200 ease-in-out mx-0.5" <.table rows={@pending_media} table_class="text-black dark:text-white">
> <:col :let={media_item} label="Title">
<.icon name="hero-eye" /> <%= StringUtils.truncate(media_item.title, 50) %>
</.link> </:col>
</:col> <:col :let={media_item} label="" class="flex place-content-evenly">
</.table> <.icon_link href={~p"/sources/#{@source.id}/media/#{media_item.id}"} icon="hero-eye" />
<% else %> </:col>
<p class="text-black dark:text-white">Nothing Here!</p> </.table>
<% end %> <% else %>
<p class="text-black dark:text-white">Nothing Here!</p>
<h3 class="font-bold text-xl">Pending Media</h3> <% end %>
<%= if match?([_|_], @pending_media) do %> </:tab>
<.table rows={@pending_media} table_class="text-black dark:text-white"> <:tab title="Downloaded Media">
<:col :let={media_item} label="Title"> <%= if match?([_|_], @downloaded_media) do %>
<%= StringUtils.truncate(media_item.title, 50) %> <h4 class="text-white text-lg mb-6">Shows a maximum of 100 media items</h4>
</:col> <.table rows={@downloaded_media} table_class="text-black dark:text-white">
<:col :let={media_item} label="" class="flex place-content-evenly"> <:col :let={media_item} label="Title">
<.link <%= StringUtils.truncate(media_item.title, 50) %>
navigate={~p"/sources/#{@source.id}/media/#{media_item.id}"} </:col>
class="hover:text-secondary duration-200 ease-in-out mx-0.5" <:col :let={media_item} label="" class="flex place-content-evenly">
> <.icon_link href={~p"/sources/#{@source.id}/media/#{media_item.id}"} icon="hero-eye" />
<.icon name="hero-eye" /> </:col>
</.link> </.table>
</:col> <% else %>
</.table> <p class="text-black dark:text-white">Nothing Here!</p>
<% else %> <% end %>
<p class="text-black dark:text-white">Nothing Here!</p> </:tab>
<% end %> <:tab title="Tasks">
</div> <%= if match?([_|_], @source.tasks) do %>
<.table rows={@source.tasks} table_class="text-black dark:text-white">
<section class="flex flex-col md:flex-row items-center md:justify-around mt-10"> <:col :let={task} label="Worker">
<.link <%= task.job.worker %>
href={~p"/sources/#{@source}"} </:col>
method="delete" <:col :let={task} label="State">
data-confirm="Are you sure you want to delete this source (leaving files in place)? This cannot be undone." <%= task.job.state %>
> </:col>
<.button color="bg-meta-1" rounding="rounded-full"> <:col :let={task} label="Scheduled At">
Delete Source <%= Calendar.strftime(task.job.scheduled_at, "%y-%m-%d %I:%M:%S %p %Z") %>
</.button> </:col>
</.link> </.table>
<.link <% else %>
href={~p"/sources/#{@source}?delete_files=true"} <p class="text-black dark:text-white">Nothing Here!</p>
method="delete" <% end %>
data-confirm="Are you sure you want to delete this source and it's files on disk? This cannot be undone." </:tab>
class="mt-5 md:mt-0" </.tabbed_layout>
>
<.button color="bg-meta-1" rounding="rounded-full">
Delete Source and Files
</.button>
</.link>
</section>
</div> </div>
</div> </div>