Skip to content

Commit f0748b8

Browse files
committed
iterate
- Trying to fetch directly from the API is too dangerous at this point, since the API is blocked - I've chosen to iterate on a GraphQL subtitles upload API - Trying the GraphQL mutation with Absinthe, I got a "No query document supplied" error - ```gql mutation SetVideoCaptions($captions: Upload!) { setVideoCaptions(videoId: 1, captions: $captions) { captions { text } } } ``` - Decided to upgrade Absinthe to see how that goes - [absinthe_ecto](https://github.com/absinthe-graphql/absinthe_ecto) is deprecated, so I'm looking to move to [dataloader](https://github.com/absinthe-graphql/dataloader) - Updated the dependencies, now looking to migrate the actual `assoc` calls
1 parent a413df4 commit f0748b8

31 files changed

+202
-154
lines changed

apps/cf/lib/videos/captions_fetcher_youtube.ex

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -69,30 +69,7 @@ defmodule CF.Videos.CaptionsFetcherYoutube do
6969
end
7070

7171
defp process_transcript(transcript) do
72-
transcript
73-
|> SweetXml.xpath(
74-
~x"//transcript/text"l,
75-
text: ~x"./text()"s |> transform_by(&clean_text/1),
76-
start: ~x"./@start"s |> transform_by(&parse_float/1),
77-
duration: ~x"./@dur"os |> transform_by(&parse_float/1)
78-
)
79-
|> Enum.filter(fn %{text: text, start: start} ->
80-
start != nil and text != nil and text != ""
81-
end)
82-
end
83-
84-
defp clean_text(text) do
85-
text
86-
|> String.replace("&", "&")
87-
|> HtmlEntities.decode()
88-
|> String.trim()
89-
end
90-
91-
defp parse_float(val) do
92-
case Float.parse(val) do
93-
{num, _} -> num
94-
_ -> nil
95-
end
72+
CF.Videos.CaptionsSrv1Parser.parse_file(transcript)
9673
end
9774

9875
# Below is an implementation using the official YouTube API, but it requires OAuth2 authentication.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
defmodule CF.Videos.CaptionsSrv1Parser do
2+
@moduledoc """
3+
A captions parser for the srv1 format.
4+
"""
5+
6+
require Logger
7+
import SweetXml
8+
9+
def parse_file(content) do
10+
content
11+
|> SweetXml.xpath(
12+
~x"//transcript/text"l,
13+
text: ~x"./text()"s |> transform_by(&clean_text/1),
14+
start: ~x"./@start"s |> transform_by(&parse_float/1),
15+
duration: ~x"./@dur"os |> transform_by(&parse_float/1)
16+
)
17+
|> Enum.filter(fn %{text: text, start: start} ->
18+
# Filter out text in brackets, like "[Music]"
19+
start != nil and text != nil and text != "" and
20+
String.match?(text, ~r/^\[.*\]$/) == false
21+
end)
22+
end
23+
24+
defp clean_text(text) do
25+
text
26+
|> String.replace("&", "&")
27+
|> HtmlEntities.decode()
28+
|> String.trim()
29+
end
30+
31+
defp parse_float(val) do
32+
case Float.parse(val) do
33+
{num, _} -> num
34+
_ -> nil
35+
end
36+
end
37+
end

apps/cf_graphql/lib/endpoint.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ defmodule CF.GraphQLWeb.Endpoint do
1313

1414
plug(
1515
Plug.Parsers,
16-
parsers: [:urlencoded, :multipart, :json],
16+
parsers: [:urlencoded, :multipart, :json, Absinthe.Plug.Parser],
1717
pass: ["*/*"],
18-
json_decoder: Poison
18+
json_decoder: Jason
1919
)
2020

2121
plug(Plug.MethodOverride)

apps/cf_graphql/lib/resolvers/videos.ex

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ defmodule CF.Graphql.Resolvers.Videos do
88
import Ecto.Query
99
import Absinthe.Resolution.Helpers, only: [batch: 3]
1010

11+
alias Ecto.Multi
1112
alias DB.Repo
1213
alias DB.Schema.Video
1314
alias DB.Schema.VideoCaption
@@ -115,7 +116,6 @@ defmodule CF.Graphql.Resolvers.Videos do
115116
{:ok, video}
116117
end
117118

118-
119119
def edit(_root, %{id: id, unlisted: unlisted}, %{
120120
context: %{user: user}
121121
}) do
@@ -137,32 +137,55 @@ defmodule CF.Graphql.Resolvers.Videos do
137137
{:error, _} ->
138138
{:error, "Failed to update video"}
139139
end
140+
end
140141

141142
def set_captions(_root, %{video_id: video_id, captions: captions_input}, %{
142143
context: %{user: user}
143144
}) do
144145
video = DB.Repo.get!(DB.Schema.Video, video_id)
145146

146-
Multi.new()
147-
|> Multi.insert(
148-
:caption,
149-
VideoCaption.changeset(%VideoCaption{
150-
video_id: video.id,
151-
raw: captions_input,
152-
parsed: captions_input,
153-
format: "user-provided"
154-
})
155-
)
156-
|> Multi.run(
157-
:action,
158-
fn _repo, %{caption: caption} ->
159-
CF.Actions.ActionCreator.action_upload_video_captions(user.id, video.id, caption)
160-
|> DB.Repo.insert!()
161-
162-
{:ok, caption}
147+
def set_captions(
148+
_root,
149+
%{video_id: video_id, captions: %{path: path, content_type: content_type}},
150+
%{
151+
context: %{user: user}
152+
}
153+
) do
154+
cond do
155+
content_type != "text/xml" ->
156+
{:error, "Unsupported content type: #{content_type}"}
157+
158+
File.stat!(path).size > 10_000_000 ->
159+
{:error, "File must be < 10MB"}
160+
161+
true ->
162+
video = DB.Repo.get!(DB.Schema.Video, video_id)
163+
captions_file_content = File.read!(path)
164+
parsed = CF.Videos.CaptionsSrv1Parser.parse_file(captions_file_content)
165+
166+
Multi.new()
167+
|> Multi.insert(
168+
:caption,
169+
VideoCaption.changeset(%VideoCaption{
170+
video_id: video.id,
171+
raw: captions_file_content,
172+
parsed: parsed,
173+
format: "xml"
174+
})
175+
)
176+
|> Multi.run(
177+
:action,
178+
fn _repo, %{caption: caption} ->
179+
CF.Actions.ActionCreator.action_upload_video_captions(user.id, video.id, caption)
180+
|> DB.Repo.insert!()
181+
182+
{:ok, caption}
183+
end
184+
)
185+
|> DB.Repo.transaction()
186+
187+
{:ok, video}
163188
end
164-
)
165-
166-
{:ok, video}
189+
end
167190
end
168191
end

apps/cf_graphql/lib/schema/input_objects.ex

Lines changed: 0 additions & 9 deletions
This file was deleted.

apps/cf_graphql/lib/schema/input_objects/statement_filter.ex

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ defmodule CF.Graphql.Schema.InputObjects.StatementFilter do
44
"""
55

66
use Absinthe.Schema.Notation
7-
use Absinthe.Ecto, repo: DB.Repo
87

98
@desc "Props to filter statements on"
109
input_object :statement_filter do

apps/cf_graphql/lib/schema/input_objects/video_caption.ex

Lines changed: 0 additions & 10 deletions
This file was deleted.

apps/cf_graphql/lib/schema/input_objects/video_filter.ex

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ defmodule CF.Graphql.Schema.InputObjects.VideoFilter do
44
"""
55

66
use Absinthe.Schema.Notation
7-
use Absinthe.Ecto, repo: DB.Repo
87

98
@desc "Props to filter videos on"
109
input_object :video_filter do

apps/cf_graphql/lib/schema/schema.ex

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,42 @@ defmodule CF.Graphql.Schema do
22
use Absinthe.Schema
33
alias CF.Graphql.Resolvers
44
alias CF.Graphql.Schema.Middleware
5+
import Absinthe.Resolution.Helpers, only: [dataloader: 1]
6+
7+
import_types(Absinthe.Plug.Types)
8+
9+
import_types(CF.Graphql.Schema.Types.{
10+
AppInfo,
11+
Comment,
12+
Notification,
13+
Paginated,
14+
Source,
15+
Speaker,
16+
Statement,
17+
Statistics,
18+
Subscription,
19+
UserAction,
20+
User,
21+
Video,
22+
VideoCaption
23+
})
24+
25+
import_types(CF.Graphql.Schema.InputObjects.{
26+
VideoFilter,
27+
StatementFilter
28+
})
29+
30+
def context(ctx) do
31+
loader =
32+
Dataloader.new()
33+
|> Dataloader.add_source(DB.Repo, Dataloader.Ecto.new(DB.Repo))
34+
35+
Map.put(ctx, :loader, loader)
36+
end
537

6-
import_types(CF.Graphql.Schema.Types)
7-
import_types(CF.Graphql.Schema.InputObjects)
38+
def plugins do
39+
[Absinthe.Middleware.Dataloader | Absinthe.Plugin.defaults()]
40+
end
841

942
# Query API
1043

@@ -109,16 +142,16 @@ defmodule CF.Graphql.Schema do
109142
arg(:unlisted, non_null(:boolean))
110143

111144
resolve(&Resolvers.Videos.edit/3)
112-
end
145+
end
113146

114147
field :set_video_captions, :video do
115148
middleware(Middleware.RequireAuthentication)
116149
middleware(Middleware.RequireReputation, 450)
117150

118151
arg(:video_id, non_null(:id))
119-
arg(:captions, non_null(list_of(:video_caption_input)))
152+
arg(:captions, non_null(:upload))
120153

121154
resolve(&Resolvers.Videos.set_captions/3)
122-
155+
end
123156
end
124157
end

apps/cf_graphql/lib/schema/types.ex

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)