Skip to content

Commit

Permalink
iterate
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
Betree committed Feb 23, 2025
1 parent 5d8b0c5 commit b52eb68
Show file tree
Hide file tree
Showing 29 changed files with 190 additions and 156 deletions.
25 changes: 1 addition & 24 deletions apps/cf/lib/videos/captions_fetcher_youtube.ex
Original file line number Diff line number Diff line change
Expand Up @@ -69,30 +69,7 @@ defmodule CF.Videos.CaptionsFetcherYoutube do
end

defp process_transcript(transcript) do
transcript
|> SweetXml.xpath(
~x"//transcript/text"l,
text: ~x"./text()"s |> transform_by(&clean_text/1),
start: ~x"./@start"s |> transform_by(&parse_float/1),
duration: ~x"./@dur"os |> transform_by(&parse_float/1)
)
|> Enum.filter(fn %{text: text, start: start} ->
start != nil and text != nil and text != ""
end)
end

defp clean_text(text) do
text
|> String.replace("&", "&")
|> HtmlEntities.decode()
|> String.trim()
end

defp parse_float(val) do
case Float.parse(val) do
{num, _} -> num
_ -> nil
end
CF.Videos.CaptionsSrv1Parser.parse_file(transcript)
end

# Below is an implementation using the official YouTube API, but it requires OAuth2 authentication.
Expand Down
35 changes: 35 additions & 0 deletions apps/cf/lib/videos/captions_srv1_parser.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
defmodule CF.Videos.CaptionsSrv1Parser do
@moduledoc """
A captions parser for the srv1 format.
"""

require Logger
import SweetXml

def parse_file(content) do
content
|> SweetXml.xpath(
~x"//transcript/text"l,
text: ~x"./text()"s |> transform_by(&clean_text/1),
start: ~x"./@start"s |> transform_by(&parse_float/1),
duration: ~x"./@dur"os |> transform_by(&parse_float/1)
)
|> Enum.filter(fn %{text: text, start: start} ->
start != nil and text != nil and text != ""
end)
end

defp clean_text(text) do
text
|> String.replace("&", "&")
|> HtmlEntities.decode()
|> String.trim()
end

defp parse_float(val) do
case Float.parse(val) do
{num, _} -> num
_ -> nil
end
end
end
4 changes: 2 additions & 2 deletions apps/cf_graphql/lib/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ defmodule CF.GraphQLWeb.Endpoint do

plug(
Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
parsers: [:urlencoded, :multipart, :json, Absinthe.Plug.Parser],
pass: ["*/*"],
json_decoder: Poison
json_decoder: Jason
)

plug(Plug.MethodOverride)
Expand Down
68 changes: 42 additions & 26 deletions apps/cf_graphql/lib/resolvers/videos.ex
Original file line number Diff line number Diff line change
Expand Up @@ -115,31 +115,47 @@ defmodule CF.Graphql.Resolvers.Videos do
{:ok, video}
end

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

Multi.new()
|> Multi.insert(
:caption,
VideoCaption.changeset(%VideoCaption{
video_id: video.id,
raw: captions_input,
parsed: captions_input,
format: "user-provided"
})
)
|> Multi.run(
:action,
fn _repo, %{caption: caption} ->
CF.Actions.ActionCreator.action_upload_video_captions(user.id, video.id, caption)
|> DB.Repo.insert!()

{:ok, caption}
end
)

{:ok, video}
def set_captions(
_root,
%{video_id: video_id, captions: %{path: path, content_type: content_type}},
%{
context: %{user: user}
}
) do
cond do
content_type != "text/xml" ->
{:error, "Unsupported content type: #{content_type}"}

File.stat!(path).size > 10_000_000 ->
{:error, "File must be < 10MB"}

true ->
video = DB.Repo.get!(DB.Schema.Video, video_id)
captions_file_content = File.read!(path)
parsed = CF.Videos.CaptionsSrv1Parser.parse_file(captions_file_content)

Multi.new()
|> Multi.insert(
:caption,
VideoCaption.changeset(%VideoCaption{
video_id: video.id,
raw: captions_file_content,
parsed: parsed,
format: "xml"
})
)
|> Multi.run(
:action,
fn _repo, %{caption: caption} ->
CF.Actions.ActionCreator.action_upload_video_captions(user.id, video.id, caption)
|> DB.Repo.insert!()

{:ok, caption}
end
)
|> DB.Repo.transaction()

{:ok, video}
end
end
end
9 changes: 0 additions & 9 deletions apps/cf_graphql/lib/schema/input_objects.ex

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ defmodule CF.Graphql.Schema.InputObjects.StatementFilter do
"""

use Absinthe.Schema.Notation
use Absinthe.Ecto, repo: DB.Repo

@desc "Props to filter statements on"
input_object :statement_filter do
Expand Down
10 changes: 0 additions & 10 deletions apps/cf_graphql/lib/schema/input_objects/video_caption.ex

This file was deleted.

1 change: 0 additions & 1 deletion apps/cf_graphql/lib/schema/input_objects/video_filter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ defmodule CF.Graphql.Schema.InputObjects.VideoFilter do
"""

use Absinthe.Schema.Notation
use Absinthe.Ecto, repo: DB.Repo

@desc "Props to filter videos on"
input_object :video_filter do
Expand Down
37 changes: 34 additions & 3 deletions apps/cf_graphql/lib/schema/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,40 @@ defmodule CF.Graphql.Schema do
use Absinthe.Schema
alias CF.Graphql.Resolvers
alias CF.Graphql.Schema.Middleware
import Absinthe.Resolution.Helpers, only: [dataloader: 1]

import_types(Absinthe.Plug.Types)
import_types(CF.Graphql.Schema.Types.{
AppInfo,
Comment,
Notification,
Paginated,
Source,
Speaker,
Statement,
Statistics,
Subscription,
UserAction,
User,
Video,
VideoCaption
})
import_types(CF.Graphql.Schema.InputObjects.{
VideoFilter,
StatementFilter
})

def context(ctx) do
loader =
Dataloader.new()
|> Dataloader.add_source(DB.Repo, Dataloader.Ecto.new(DB.Repo))

Map.put(ctx, :loader, loader)
end

import_types(CF.Graphql.Schema.Types)
import_types(CF.Graphql.Schema.InputObjects)
def plugins do
[Absinthe.Middleware.Dataloader | Absinthe.Plugin.defaults()]
end

# Query API

Expand Down Expand Up @@ -105,7 +136,7 @@ defmodule CF.Graphql.Schema do
middleware(Middleware.RequireReputation, 450)

arg(:video_id, non_null(:id))
arg(:captions, non_null(list_of(:video_caption_input)))
arg(:captions, non_null(:upload))

resolve(&Resolvers.Videos.set_captions/3)
end
Expand Down
19 changes: 0 additions & 19 deletions apps/cf_graphql/lib/schema/types.ex

This file was deleted.

1 change: 0 additions & 1 deletion apps/cf_graphql/lib/schema/types/app_info.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ defmodule CF.Graphql.Schema.Types.AppInfo do
"""

use Absinthe.Schema.Notation
use Absinthe.Ecto, repo: DB.Repo

@desc "Information about the application"
object :app_info do
Expand Down
7 changes: 4 additions & 3 deletions apps/cf_graphql/lib/schema/types/comment.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ defmodule CF.Graphql.Schema.Types.Comment do
"""

use Absinthe.Schema.Notation
use Absinthe.Ecto, repo: DB.Repo

import Absinthe.Resolution.Helpers, only: [dataloader: 1]
import CF.Graphql.Schema.Utils
alias CF.Graphql.Resolvers

Expand All @@ -13,7 +14,7 @@ defmodule CF.Graphql.Schema.Types.Comment do
field(:id, non_null(:id))
@desc "User who made the comment"
field :user, :user do
resolve(assoc(:user))
resolve(dataloader(DB.Repo))
complexity(join_complexity())
end

Expand All @@ -31,7 +32,7 @@ defmodule CF.Graphql.Schema.Types.Comment do

@desc "Source of the scomment. If null, a text must be set"
field :source, :source do
resolve(assoc(:source))
resolve(dataloader(DB.Repo))
complexity(join_complexity())
end

Expand Down
7 changes: 3 additions & 4 deletions apps/cf_graphql/lib/schema/types/notification.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ defmodule CF.Graphql.Schema.Types.Notification do
"""

use Absinthe.Schema.Notation
use Absinthe.Ecto, repo: DB.Repo
import CF.Graphql.Schema.Utils

import_types(CF.Graphql.Schema.Types.Paginated)
import Absinthe.Resolution.Helpers, only: [dataloader: 1]
import CF.Graphql.Schema.Utils

@desc "A user notification"
object :notification do
Expand All @@ -20,7 +19,7 @@ defmodule CF.Graphql.Schema.Types.Notification do
field(:seen_at, :string)
@desc "Action the notification is referencing"
field :action, :user_action do
resolve(assoc(:action))
resolve(dataloader(DB.Repo))
complexity(join_complexity())
end
end
Expand Down
1 change: 0 additions & 1 deletion apps/cf_graphql/lib/schema/types/paginated.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ defmodule CF.Graphql.Schema.Types.Paginated do
"""

use Absinthe.Schema.Notation
use Absinthe.Ecto, repo: DB.Repo

object :paginated do
field(:page_number, :integer)
Expand Down
1 change: 0 additions & 1 deletion apps/cf_graphql/lib/schema/types/source.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ defmodule CF.Graphql.Schema.Types.Source do
"""

use Absinthe.Schema.Notation
use Absinthe.Ecto, repo: DB.Repo

@desc "An URL pointing toward a source (article, video, pdf...)"
object :source do
Expand Down
6 changes: 4 additions & 2 deletions apps/cf_graphql/lib/schema/types/speaker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ defmodule CF.Graphql.Schema.Types.Speaker do
"""

use Absinthe.Schema.Notation
use Absinthe.Ecto, repo: DB.Repo

import Absinthe.Resolution.Helpers, only: [dataloader: 1]
import Absinthe.Resolution.Helpers, only: [dataloader: 1]
import CF.Graphql.Schema.Utils
alias CF.Graphql.Resolvers

Expand All @@ -26,7 +28,7 @@ defmodule CF.Graphql.Schema.Types.Speaker do
field(:picture, :string, do: resolve(&Resolvers.Speakers.picture/3))
@desc "List of speaker's videos"
field :videos, list_of(:video) do
resolve(assoc(:videos))
resolve(dataloader(DB.Repo))
complexity(join_complexity())
end
end
Expand Down
11 changes: 5 additions & 6 deletions apps/cf_graphql/lib/schema/types/statement.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ defmodule CF.Graphql.Schema.Types.Statement do
"""

use Absinthe.Schema.Notation
use Absinthe.Ecto, repo: DB.Repo
import CF.Graphql.Schema.Utils

import_types(CF.Graphql.Schema.Types.{Paginated, Speaker, Comment})
import Absinthe.Resolution.Helpers, only: [dataloader: 1]
import CF.Graphql.Schema.Utils

@desc "A transcript or a description of the picture"
object :statement do
Expand All @@ -21,19 +20,19 @@ defmodule CF.Graphql.Schema.Types.Statement do

@desc "Statement's speaker. Null if statement describes picture"
field :speaker, :speaker do
resolve(assoc(:speaker))
resolve(dataloader(DB.Repo))
complexity(join_complexity())
end

@desc "List of users comments and facts for this statement"
field :comments, list_of(:comment) do
resolve(assoc(:comments))
resolve(dataloader(DB.Repo))
complexity(join_complexity())
end

@desc "The video associated with this statement"
field :video, :video do
resolve(assoc(:video))
resolve(dataloader(DB.Repo))
complexity(join_complexity())
end
end
Expand Down
Loading

0 comments on commit b52eb68

Please sign in to comment.