Adds options + option builder + metadata parsing for media thumbnails
This commit is contained in:
parent
ca01f17b58
commit
3e5faa0503
9 changed files with 281 additions and 48 deletions
|
|
@ -11,12 +11,13 @@ defmodule Pinchflat.Media.MediaItem do
|
|||
alias Pinchflat.Media.MediaMetadata
|
||||
|
||||
@required_fields ~w(media_id source_id)a
|
||||
@allowed_fields ~w(title media_id media_filepath source_id subtitle_filepaths)a
|
||||
@allowed_fields ~w(title media_id media_filepath source_id subtitle_filepaths thumbnail_filepath)a
|
||||
|
||||
schema "media_items" do
|
||||
field :title, :string
|
||||
field :media_id, :string
|
||||
field :media_filepath, :string
|
||||
field :thumbnail_filepath, :string
|
||||
# This is an array of [iso-2 language, filepath] pairs. Probably could
|
||||
# be an associated record, but I don't see the benefit right now.
|
||||
# Will very likely revisit because I can't leave well-enough alone.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.MetadataParser do
|
|||
metadata_attrs
|
||||
|> Map.merge(parse_media_metadata(metadata))
|
||||
|> Map.merge(parse_subtitle_metadata(metadata))
|
||||
|> Map.merge(parse_thumbnail_metadata(metadata))
|
||||
end
|
||||
|
||||
defp parse_media_metadata(metadata) do
|
||||
|
|
@ -35,10 +36,9 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.MetadataParser do
|
|||
end
|
||||
|
||||
defp parse_subtitle_metadata(metadata) do
|
||||
subtitle_map = metadata["requested_subtitles"] || %{}
|
||||
# IDEA: if needed, consider filtering out subtitles that don't exist on-disk
|
||||
subtitle_filepaths =
|
||||
subtitle_map
|
||||
(metadata["requested_subtitles"] || %{})
|
||||
|> Enum.map(fn {lang, attrs} -> [lang, attrs["filepath"]] end)
|
||||
|> Enum.sort(fn [lang_a, _], [lang_b, _] -> lang_a < lang_b end)
|
||||
|
||||
|
|
@ -46,4 +46,18 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.MetadataParser do
|
|||
subtitle_filepaths: subtitle_filepaths
|
||||
}
|
||||
end
|
||||
|
||||
defp parse_thumbnail_metadata(metadata) do
|
||||
thumbnail_filepath =
|
||||
(metadata["thumbnails"] || %{})
|
||||
# Reverse so that higher resolution thumbnails come first.
|
||||
# This _shouldn't_ matter yet, but I'd rather default to the best
|
||||
# in case I'm wrong.
|
||||
|> Enum.reverse()
|
||||
|> Enum.find_value(fn attrs -> attrs["filepath"] end)
|
||||
|
||||
%{
|
||||
thumbnail_filepath: thumbnail_filepath
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ defmodule Pinchflat.Profiles.MediaProfile do
|
|||
download_auto_subs
|
||||
embed_subs
|
||||
sub_langs
|
||||
download_thumbnail
|
||||
embed_thumbnail
|
||||
)a
|
||||
|
||||
@required_fields ~w(name output_path_template)a
|
||||
|
|
@ -22,11 +24,15 @@ defmodule Pinchflat.Profiles.MediaProfile do
|
|||
schema "media_profiles" do
|
||||
field :name, :string
|
||||
field :output_path_template, :string
|
||||
|
||||
field :download_subs, :boolean, default: true
|
||||
field :download_auto_subs, :boolean, default: true
|
||||
field :embed_subs, :boolean, default: true
|
||||
field :sub_langs, :string, default: "en"
|
||||
|
||||
field :download_thumbnail, :boolean, default: true
|
||||
field :embed_thumbnail, :boolean, default: true
|
||||
|
||||
has_many :sources, Source
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ defmodule Pinchflat.Profiles.Options.YtDlp.OptionBuilder do
|
|||
built_options =
|
||||
default_options() ++
|
||||
subtitle_options(media_profile) ++
|
||||
thumbnail_options(media_profile) ++
|
||||
output_options(media_profile)
|
||||
|
||||
{:ok, built_options}
|
||||
|
|
@ -33,7 +34,6 @@ defmodule Pinchflat.Profiles.Options.YtDlp.OptionBuilder do
|
|||
defp default_options do
|
||||
[
|
||||
:embed_metadata,
|
||||
:embed_thumbnail,
|
||||
:no_progress
|
||||
]
|
||||
end
|
||||
|
|
@ -65,6 +65,18 @@ defmodule Pinchflat.Profiles.Options.YtDlp.OptionBuilder do
|
|||
end)
|
||||
end
|
||||
|
||||
defp thumbnail_options(media_profile) do
|
||||
mapped_struct = Map.from_struct(media_profile)
|
||||
|
||||
Enum.reduce(mapped_struct, [], fn attr, acc ->
|
||||
case attr do
|
||||
{:download_thumbnail, true} -> acc ++ [:write_thumbnail]
|
||||
{:embed_thumbnail, true} -> acc ++ [:embed_thumbnail]
|
||||
_ -> acc
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp output_options(media_profile) do
|
||||
{:ok, output_path} = OutputPathBuilder.build(media_profile.output_path_template)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
defmodule Pinchflat.Repo.Migrations.AddThumbnailOptionsToMediaProfiles do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:media_profiles) do
|
||||
add :download_thumbnail, :boolean, default: true, null: false
|
||||
add :embed_thumbnail, :boolean, default: true, null: false
|
||||
end
|
||||
|
||||
alter table(:media_items) do
|
||||
add :thumbnail_filepath, :string
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -83,4 +83,28 @@ defmodule Pinchflat.MediaClient.Backends.YtDlp.MediaParserTest do
|
|||
assert result.subtitle_filepaths == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "parse_for_media_item/1 when testing thumbnail metadata" do
|
||||
test "extracts the thumbnail filepath", %{metadata: metadata} do
|
||||
result = Parser.parse_for_media_item(metadata)
|
||||
|
||||
assert String.ends_with?(result.thumbnail_filepath, ".webp")
|
||||
end
|
||||
|
||||
test "doesn't freak out if the video has no thumbnails", %{metadata: metadata} do
|
||||
metadata = Map.put(metadata, "thumbnails", %{})
|
||||
|
||||
result = Parser.parse_for_media_item(metadata)
|
||||
|
||||
assert result.thumbnail_filepath == nil
|
||||
end
|
||||
|
||||
test "doesn't freak out if the thumbnails key is missing", %{metadata: metadata} do
|
||||
metadata = Map.delete(metadata, "thumbnails")
|
||||
|
||||
result = Parser.parse_for_media_item(metadata)
|
||||
|
||||
assert result.thumbnail_filepath == nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -33,11 +33,18 @@ defmodule Pinchflat.MediaClient.VideoDownloaderTest do
|
|||
{:ok, render_metadata(:media_metadata)}
|
||||
end)
|
||||
|
||||
assert %{media_filepath: nil, title: nil, subtitle_filepaths: []} = media_item
|
||||
assert %{
|
||||
media_filepath: nil,
|
||||
title: nil,
|
||||
subtitle_filepaths: [],
|
||||
thumbnail_filepath: nil
|
||||
} = media_item
|
||||
|
||||
assert {:ok, updated_media_item} = VideoDownloader.download_for_media_item(media_item)
|
||||
assert updated_media_item.media_filepath
|
||||
assert updated_media_item.title
|
||||
assert length(updated_media_item.subtitle_filepaths) > 0
|
||||
assert updated_media_item.thumbnail_filepath
|
||||
end
|
||||
|
||||
test "it saves the metadata to the database", %{media_item: media_item} do
|
||||
|
|
|
|||
|
|
@ -94,4 +94,35 @@ defmodule Pinchflat.Profiles.Options.YtDlp.OptionBuilderTest do
|
|||
refute {:id, -1} in res
|
||||
end
|
||||
end
|
||||
|
||||
describe "build/1 when testing thumbnail options" do
|
||||
test "includes :write_thumbnail option when specified" do
|
||||
media_profile = %MediaProfile{@media_profile | download_thumbnail: true}
|
||||
|
||||
assert {:ok, res} = OptionBuilder.build(media_profile)
|
||||
|
||||
assert :write_thumbnail in res
|
||||
end
|
||||
|
||||
test "includes :embed_thumbnail option when specified" do
|
||||
media_profile = %MediaProfile{@media_profile | embed_thumbnail: true}
|
||||
|
||||
assert {:ok, res} = OptionBuilder.build(media_profile)
|
||||
|
||||
assert :embed_thumbnail in res
|
||||
end
|
||||
|
||||
test "doesn't include these options when not specified" do
|
||||
media_profile = %MediaProfile{
|
||||
@media_profile
|
||||
| embed_thumbnail: false,
|
||||
download_thumbnail: false
|
||||
}
|
||||
|
||||
assert {:ok, res} = OptionBuilder.build(media_profile)
|
||||
|
||||
refute :write_thumbnail in res
|
||||
refute :embed_thumbnail in res
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1389,42 +1389,138 @@
|
|||
}
|
||||
],
|
||||
"thumbnails": [
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/3.jpg", "preference": -37, "id": "0" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/3.webp", "preference": -36, "id": "1" },
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/2.jpg", "preference": -35, "id": "2" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/2.webp", "preference": -34, "id": "3" },
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/1.jpg", "preference": -33, "id": "4" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/1.webp", "preference": -32, "id": "5" },
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/mq3.jpg", "preference": -31, "id": "6" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/mq3.webp", "preference": -30, "id": "7" },
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/mq2.jpg", "preference": -29, "id": "8" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/mq2.webp", "preference": -28, "id": "9" },
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/mq1.jpg", "preference": -27, "id": "10" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/mq1.webp", "preference": -26, "id": "11" },
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hq3.jpg", "preference": -25, "id": "12" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/hq3.webp", "preference": -24, "id": "13" },
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hq2.jpg", "preference": -23, "id": "14" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/hq2.webp", "preference": -22, "id": "15" },
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hq1.jpg", "preference": -21, "id": "16" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/hq1.webp", "preference": -20, "id": "17" },
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/sd3.jpg", "preference": -19, "id": "18" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/sd3.webp", "preference": -18, "id": "19" },
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/sd2.jpg", "preference": -17, "id": "20" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/sd2.webp", "preference": -16, "id": "21" },
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/sd1.jpg", "preference": -15, "id": "22" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/sd1.webp", "preference": -14, "id": "23" },
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/3.jpg",
|
||||
"preference": -37,
|
||||
"id": "0"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/3.webp",
|
||||
"preference": -36,
|
||||
"id": "1"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/2.jpg",
|
||||
"preference": -35,
|
||||
"id": "2"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/2.webp",
|
||||
"preference": -34,
|
||||
"id": "3"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/1.jpg",
|
||||
"preference": -33,
|
||||
"id": "4"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/1.webp",
|
||||
"preference": -32,
|
||||
"id": "5"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/mq3.jpg",
|
||||
"preference": -31,
|
||||
"id": "6"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/mq3.webp",
|
||||
"preference": -30,
|
||||
"id": "7"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/mq2.jpg",
|
||||
"preference": -29,
|
||||
"id": "8"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/mq2.webp",
|
||||
"preference": -28,
|
||||
"id": "9"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/mq1.jpg",
|
||||
"preference": -27,
|
||||
"id": "10"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/mq1.webp",
|
||||
"preference": -26,
|
||||
"id": "11"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hq3.jpg",
|
||||
"preference": -25,
|
||||
"id": "12"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/hq3.webp",
|
||||
"preference": -24,
|
||||
"id": "13"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hq2.jpg",
|
||||
"preference": -23,
|
||||
"id": "14"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/hq2.webp",
|
||||
"preference": -22,
|
||||
"id": "15"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hq1.jpg",
|
||||
"preference": -21,
|
||||
"id": "16"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/hq1.webp",
|
||||
"preference": -20,
|
||||
"id": "17"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/sd3.jpg",
|
||||
"preference": -19,
|
||||
"id": "18"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/sd3.webp",
|
||||
"preference": -18,
|
||||
"id": "19"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/sd2.jpg",
|
||||
"preference": -17,
|
||||
"id": "20"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/sd2.webp",
|
||||
"preference": -16,
|
||||
"id": "21"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/sd1.jpg",
|
||||
"preference": -15,
|
||||
"id": "22"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/sd1.webp",
|
||||
"preference": -14,
|
||||
"id": "23"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/default.jpg",
|
||||
"height": 90,
|
||||
"width": 120,
|
||||
"preference": -13,
|
||||
"id": "24",
|
||||
"resolution": "120x90"
|
||||
"id": "24"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/default.webp",
|
||||
"height": 90,
|
||||
"width": 120,
|
||||
"preference": -12,
|
||||
"id": "25"
|
||||
"id": "25",
|
||||
"resolution": "120x90"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/mqdefault.jpg",
|
||||
|
|
@ -1436,13 +1532,24 @@
|
|||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/mqdefault.webp",
|
||||
"height": 180,
|
||||
"width": 320,
|
||||
"preference": -10,
|
||||
"id": "27"
|
||||
"id": "27",
|
||||
"resolution": "320x180"
|
||||
},
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/0.jpg", "preference": -9, "id": "28" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/0.webp", "preference": -8, "id": "29" },
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLCfnqZqzf3SpMWSBlr5eEkYrRbeOw",
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/0.jpg",
|
||||
"preference": -9,
|
||||
"id": "28"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/0.webp",
|
||||
"preference": -8,
|
||||
"id": "29"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLDmfPp_bFp43fLeDiG7JmFAldoFLA",
|
||||
"height": 94,
|
||||
"width": 168,
|
||||
"preference": -7,
|
||||
|
|
@ -1450,7 +1557,7 @@
|
|||
"resolution": "168x94"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLAnj5wtnbYGzZX9gerMKQ-FS5sSeA",
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLCGAiCuOy3Tmz9U9iGo3YF5dM57bQ",
|
||||
"height": 110,
|
||||
"width": 196,
|
||||
"preference": -7,
|
||||
|
|
@ -1458,7 +1565,7 @@
|
|||
"resolution": "196x110"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLC5fmbFoduOVcifLAMu006pXlcicQ",
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDcSQOIb-t1Aug8y0A-_M2jNehcRw",
|
||||
"height": 138,
|
||||
"width": 246,
|
||||
"preference": -7,
|
||||
|
|
@ -1466,7 +1573,7 @@
|
|||
"resolution": "246x138"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDISoJqnSXFGgqsSCMRd9NCiV5Dyg",
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAT65DjLaxTpghDUrIAQEclNUUJog",
|
||||
"height": 188,
|
||||
"width": 336,
|
||||
"preference": -7,
|
||||
|
|
@ -1483,8 +1590,11 @@
|
|||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/hqdefault.webp",
|
||||
"height": 360,
|
||||
"width": 480,
|
||||
"preference": -6,
|
||||
"id": "35"
|
||||
"id": "35",
|
||||
"resolution": "480x360"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/sddefault.jpg",
|
||||
|
|
@ -1496,23 +1606,37 @@
|
|||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/sddefault.webp",
|
||||
"height": 480,
|
||||
"width": 640,
|
||||
"preference": -4,
|
||||
"id": "37"
|
||||
"id": "37",
|
||||
"resolution": "640x480"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hq720.jpg",
|
||||
"preference": -3,
|
||||
"id": "38"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/hq720.webp",
|
||||
"preference": -2,
|
||||
"id": "39"
|
||||
},
|
||||
{ "url": "https://i.ytimg.com/vi/bwRHIkYqYJo/hq720.jpg", "preference": -3, "id": "38" },
|
||||
{ "url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/hq720.webp", "preference": -2, "id": "39" },
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi/bwRHIkYqYJo/maxresdefault.jpg",
|
||||
"height": 1080,
|
||||
"width": 1920,
|
||||
"height": 720,
|
||||
"width": 1280,
|
||||
"preference": -1,
|
||||
"id": "40",
|
||||
"resolution": "1920x1080"
|
||||
"resolution": "1280x720"
|
||||
},
|
||||
{
|
||||
"url": "https://i.ytimg.com/vi_webp/bwRHIkYqYJo/maxresdefault.webp",
|
||||
"height": 1080,
|
||||
"width": 1920,
|
||||
"preference": 0,
|
||||
"id": "41",
|
||||
"resolution": "1920x1080",
|
||||
"filepath": "/app/tmp/videos/bwRHIkYqYJo/Trying to Wheelie Without the Rear Brake-bwRHIkYqYJo.webp"
|
||||
}
|
||||
],
|
||||
|
|
|
|||
Loading…
Reference in a new issue