[Enhancement] Start tracking a media item's duration (#146)
* Added duration field, started importing it during indexing and download * Added duration to RSS feed
This commit is contained in:
parent
bdd97bde01
commit
fae7ba3304
8 changed files with 56 additions and 5 deletions
|
|
@ -22,6 +22,7 @@ defmodule Pinchflat.Media.MediaItem do
|
||||||
:source_id,
|
:source_id,
|
||||||
:short_form_content,
|
:short_form_content,
|
||||||
:upload_date,
|
:upload_date,
|
||||||
|
:duration_seconds,
|
||||||
# these fields are captured only on download
|
# these fields are captured only on download
|
||||||
:media_downloaded_at,
|
:media_downloaded_at,
|
||||||
:media_filepath,
|
:media_filepath,
|
||||||
|
|
@ -57,6 +58,7 @@ defmodule Pinchflat.Media.MediaItem do
|
||||||
field :short_form_content, :boolean, default: false
|
field :short_form_content, :boolean, default: false
|
||||||
field :media_downloaded_at, :utc_datetime
|
field :media_downloaded_at, :utc_datetime
|
||||||
field :upload_date, :date
|
field :upload_date, :date
|
||||||
|
field :duration_seconds, :integer
|
||||||
|
|
||||||
field :media_filepath, :string
|
field :media_filepath, :string
|
||||||
field :media_size_bytes, :integer
|
field :media_size_bytes, :integer
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,8 @@ defmodule Pinchflat.Metadata.MetadataParser do
|
||||||
original_url: metadata["original_url"],
|
original_url: metadata["original_url"],
|
||||||
description: metadata["description"],
|
description: metadata["description"],
|
||||||
media_filepath: metadata["filepath"],
|
media_filepath: metadata["filepath"],
|
||||||
livestream: metadata["was_live"]
|
livestream: metadata["was_live"],
|
||||||
|
duration_seconds: metadata["duration"] && round(metadata["duration"])
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ defmodule Pinchflat.Podcasts.RssFeedBuilder do
|
||||||
<link>#{media_item.original_url}</link>
|
<link>#{media_item.original_url}</link>
|
||||||
<description>#{safe(media_item.description)}</description>
|
<description>#{safe(media_item.description)}</description>
|
||||||
<pubDate>#{generate_upload_date(media_item)}</pubDate>
|
<pubDate>#{generate_upload_date(media_item)}</pubDate>
|
||||||
|
<itunes:duration>#{media_item.duration_seconds}</itunes:duration>
|
||||||
<enclosure
|
<enclosure
|
||||||
url="#{media_stream_path(url_base, media_item)}"
|
url="#{media_stream_path(url_base, media_item)}"
|
||||||
length="#{media_item.media_size_bytes}"
|
length="#{media_item.media_size_bytes}"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ defmodule Pinchflat.YtDlp.Media do
|
||||||
:original_url,
|
:original_url,
|
||||||
:livestream,
|
:livestream,
|
||||||
:short_form_content,
|
:short_form_content,
|
||||||
:upload_date
|
:upload_date,
|
||||||
|
:duration_seconds
|
||||||
]
|
]
|
||||||
|
|
||||||
defstruct [
|
defstruct [
|
||||||
|
|
@ -20,7 +21,8 @@ defmodule Pinchflat.YtDlp.Media do
|
||||||
:original_url,
|
:original_url,
|
||||||
:livestream,
|
:livestream,
|
||||||
:short_form_content,
|
:short_form_content,
|
||||||
:upload_date
|
:upload_date,
|
||||||
|
:duration_seconds
|
||||||
]
|
]
|
||||||
|
|
||||||
alias __MODULE__
|
alias __MODULE__
|
||||||
|
|
@ -86,6 +88,7 @@ defmodule Pinchflat.YtDlp.Media do
|
||||||
description: response["description"],
|
description: response["description"],
|
||||||
original_url: response["webpage_url"],
|
original_url: response["webpage_url"],
|
||||||
livestream: response["was_live"],
|
livestream: response["was_live"],
|
||||||
|
duration_seconds: response["duration"] && round(response["duration"]),
|
||||||
short_form_content: response["webpage_url"] && short_form_content?(response),
|
short_form_content: response["webpage_url"] && short_form_content?(response),
|
||||||
upload_date: response["upload_date"] && MetadataFileHelpers.parse_upload_date(response["upload_date"])
|
upload_date: response["upload_date"] && MetadataFileHelpers.parse_upload_date(response["upload_date"])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pinchflat.Repo.Migrations.AddDurationToMediaItems do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:media_items) do
|
||||||
|
add :duration_seconds, :integer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -35,7 +35,7 @@ defmodule Pinchflat.Downloading.MediaDownloaderTest do
|
||||||
assert {:ok, _} = MediaDownloader.download_for_media_item(media_item)
|
assert {:ok, _} = MediaDownloader.download_for_media_item(media_item)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it saves the metadata filepatha to the database", %{media_item: media_item} do
|
test "it saves the metadata filepath to the database", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
||||||
{:ok, render_metadata(:media_metadata)}
|
{:ok, render_metadata(:media_metadata)}
|
||||||
end)
|
end)
|
||||||
|
|
@ -93,6 +93,12 @@ defmodule Pinchflat.Downloading.MediaDownloaderTest do
|
||||||
assert [["de", _], ["en", _] | _rest] = updated_media_item.subtitle_filepaths
|
assert [["de", _], ["en", _] | _rest] = updated_media_item.subtitle_filepaths
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it extracts the duration_seconds", %{media_item: media_item} do
|
||||||
|
assert media_item.duration_seconds == nil
|
||||||
|
assert {:ok, updated_media_item} = MediaDownloader.download_for_media_item(media_item)
|
||||||
|
assert is_integer(updated_media_item.duration_seconds)
|
||||||
|
end
|
||||||
|
|
||||||
test "it extracts the thumbnail_filepath", %{media_item: media_item} do
|
test "it extracts the thumbnail_filepath", %{media_item: media_item} do
|
||||||
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
expect(YtDlpRunnerMock, :run, fn _url, _opts, _ot ->
|
||||||
metadata = render_parsed_metadata(:media_metadata)
|
metadata = render_parsed_metadata(:media_metadata)
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,12 @@ defmodule Pinchflat.Metadata.MetadataParserTest do
|
||||||
|
|
||||||
assert result.livestream == metadata["was_live"]
|
assert result.livestream == metadata["was_live"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it extracts the duration in seconds", %{metadata: metadata} do
|
||||||
|
result = Parser.parse_for_media_item(metadata)
|
||||||
|
|
||||||
|
assert result.duration_seconds == round(metadata["duration"])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "parse_for_media_item/1 when testing subtitle metadata" do
|
describe "parse_for_media_item/1 when testing subtitle metadata" do
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,8 @@ defmodule Pinchflat.YtDlp.MediaTest do
|
||||||
original_url: "https://www.youtube.com/watch?v=TiZPUDkDYbk",
|
original_url: "https://www.youtube.com/watch?v=TiZPUDkDYbk",
|
||||||
livestream: false,
|
livestream: false,
|
||||||
short_form_content: false,
|
short_form_content: false,
|
||||||
upload_date: Date.from_iso8601!("2021-01-01")
|
upload_date: Date.from_iso8601!("2021-01-01"),
|
||||||
|
duration_seconds: 60
|
||||||
} == Media.response_to_struct(response)
|
} == Media.response_to_struct(response)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -174,5 +175,27 @@ defmodule Pinchflat.YtDlp.MediaTest do
|
||||||
|
|
||||||
assert %Media{upload_date: nil} = Media.response_to_struct(response)
|
assert %Media{upload_date: nil} = Media.response_to_struct(response)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "parses the duration" do
|
||||||
|
response = %{
|
||||||
|
"webpage_url" => "https://www.youtube.com/watch?v=TiZPUDkDYbk",
|
||||||
|
"aspect_ratio" => 1.0,
|
||||||
|
"duration" => 60.4,
|
||||||
|
"upload_date" => "20210101"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %Media{duration_seconds: 60} = Media.response_to_struct(response)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't blow up if duration is missing" do
|
||||||
|
response = %{
|
||||||
|
"webpage_url" => "https://www.youtube.com/watch?v=TiZPUDkDYbk",
|
||||||
|
"aspect_ratio" => 1.0,
|
||||||
|
"duration" => nil,
|
||||||
|
"upload_date" => "20210101"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %Media{duration_seconds: nil} = Media.response_to_struct(response)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue