├── test ├── test_helper.exs ├── text_2_sign_test.exs └── text_2_sign │ ├── .fetcher_test.exs.swp │ ├── fetcher_test.exs │ └── translator_test.exs ├── lib ├── text_2_sign.ex └── text_2_sign │ ├── fetcher.ex │ ├── translator.ex │ └── utils.ex ├── .formatter.exs ├── .travis.yml ├── .gitignore ├── mix.exs ├── LICENSE ├── README.md ├── config └── config.exs └── mix.lock /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /lib/text_2_sign.ex: -------------------------------------------------------------------------------- 1 | defmodule Text2Sign do 2 | end 3 | -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] 3 | ] 4 | -------------------------------------------------------------------------------- /test/text_2_sign_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Text2SignTest do 2 | use ExUnit.Case 3 | end 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: elixir 2 | elixir: 3 | - 1.6 4 | otp_release: 5 | - 18.0 6 | sudo: false 7 | -------------------------------------------------------------------------------- /test/text_2_sign/.fetcher_test.exs.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Waasi/text_2_sign/HEAD/test/text_2_sign/.fetcher_test.exs.swp -------------------------------------------------------------------------------- /test/text_2_sign/fetcher_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Text2Sign.FetcherTest do 2 | use ExUnit.Case 3 | 4 | @url "https://www.signingsavvy.com/" 5 | 6 | alias Text2Sign.Fetcher 7 | 8 | test "fetch/2 fetches the words endpoints" do 9 | assert {:ok, "sign/YAM/8544/1"} = Fetcher.fetch("YAM", @url) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc 12 | 13 | # If the VM crashes, it generates a dump, let's ignore it too. 14 | erl_crash.dump 15 | 16 | # Also ignore archive artifacts (built via "mix archive.build"). 17 | *.ez 18 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Text2Sign.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :text_2_sign, 7 | version: "0.1.1", 8 | elixir: "~> 1.6", 9 | build_embedded: Mix.env() == :prod, 10 | start_permanent: Mix.env() == :prod, 11 | deps: deps() 12 | ] 13 | end 14 | 15 | def application do 16 | [applications: [:logger, :httpoison]] 17 | end 18 | 19 | defp deps do 20 | [{:httpoison, "~> 0.9.0"}, {:floki, "~> 0.11.0"}, {:mock, "~> 0.2.0", only: :test}] 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /test/text_2_sign/translator_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Text2Sign.TranslatorTest do 2 | use ExUnit.Case 3 | 4 | @url "https://www.signingsavvy.com/" 5 | 6 | alias Text2Sign.{Translator, Fetcher} 7 | 8 | import Mock 9 | 10 | test ".translate a single word" do 11 | with_mock Fetcher, fetch: fn _word, _url -> {:ok, "sign/YAM/8544/1"} end do 12 | assert Translator.translate(["YAM"]) == ["#{@url}signs/mp4/9/9055.mp4"] 13 | end 14 | end 15 | 16 | test ".translate an empty list of words" do 17 | with_mock Fetcher, fetch: fn _word, _url -> {:ok, ""} end do 18 | Translator.translate(["WAKAKAA"]) == [] 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/text_2_sign/fetcher.ex: -------------------------------------------------------------------------------- 1 | defmodule Text2Sign.Fetcher do 2 | @moduledoc """ 3 | The Text2Sign.Fetcher module allows the fetching 4 | of the url for the given word. 5 | """ 6 | 7 | @doc """ 8 | The fetch/1 function takes a string type 9 | word as an argument and returns a tuple 10 | with the status and the url for the given 11 | word. 12 | 13 | ### Example: 14 | iex> Text2Sign.Fetcher.fetch("Word")\n 15 | {:ok, some_url} 16 | """ 17 | 18 | def fetch(word, url) do 19 | response = HTTPoison.get!("#{url}search/#{word}") 20 | 21 | endpoint = 22 | response.body 23 | |> Floki.find("a[href*=#{String.upcase(word)}]") 24 | |> Floki.attribute("href") 25 | |> Enum.at(0) 26 | 27 | {:ok, endpoint} 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/text_2_sign/translator.ex: -------------------------------------------------------------------------------- 1 | defmodule Text2Sign.Translator do 2 | @moduledoc """ 3 | The Text2Sign.Translator module allows the fetching 4 | of the video url for a given list of words. 5 | """ 6 | 7 | alias Text2Sign.Utils 8 | 9 | @doc """ 10 | The translate/1 function takes a list of 11 | string type words as argument and returns 12 | a list with the video urls for the given 13 | list of words. 14 | 15 | ### Example: 16 | iex> Text2Sign.Translator.translate(["Word", "World"])\n 17 | ["some_url", "other_url"] 18 | """ 19 | 20 | def translate([]), do: {:ok, []} 21 | 22 | def translate(words) do 23 | words 24 | |> Task.async_stream(&Utils.translate_word/1) 25 | |> Stream.map(&Utils.extract_response/1) 26 | |> Enum.to_list() 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/text_2_sign/utils.ex: -------------------------------------------------------------------------------- 1 | defmodule Text2Sign.Utils do 2 | @moduledoc false 3 | @url "https://www.signingsavvy.com/" 4 | 5 | alias Text2Sign.Fetcher 6 | 7 | def extract_response({:ok, {_word, response}}), do: response 8 | def extract_response(response), do: response 9 | 10 | def translate_word(""), do: {:ok, nil} 11 | 12 | def translate_word(word) do 13 | case Fetcher.fetch(word, @url) do 14 | {:ok, url} -> 15 | endpoint = 16 | url 17 | |> fetch_script() 18 | |> parse_script() 19 | 20 | {word, "#{@url}#{endpoint}"} 21 | 22 | _ -> 23 | [] 24 | end 25 | end 26 | 27 | def parse_script([]), do: [] 28 | 29 | def parse_script(script) do 30 | script 31 | |> Floki.DeepText.get() 32 | |> String.split("player1VideoList=new Array(") 33 | |> Enum.at(1) 34 | |> String.split("\"") 35 | |> Enum.at(1) 36 | end 37 | 38 | def fetch_script(url) do 39 | %{body: body} = HTTPoison.get!("https://www.signingsavvy.com/#{url}") 40 | Floki.find(body, ".signing_body script") 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Eric Santos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Text2Sign 2 | [![Build Status](https://travis-ci.org/Waasi/text_2_sign.svg?branch=master)](https://travis-ci.org/Waasi/text_2_sign) 3 | 4 | Text to Sign Language Translator Elixir Library. It uses the resources in 5 | https://www.signingsavvy.com/ to translate a list of words to sign language. 6 | 7 | ### Installation 8 | 9 | 1. Add `text_2_sign` to your list of dependencies in `mix.exs`: 10 | 11 | ```elixir 12 | def deps do 13 | [{:text_2_sign, git: "https://github.com/Waasi/text_2_sign"}] 14 | end 15 | ``` 16 | 17 | ### Usage 18 | 19 | ##### Text2Sign.Translator Module translate/1 20 | 21 | ```elixir 22 | Text2Sign.Translator.translate ["Hello", "World"] # => [hello_sign_video_url, world_sign_video_url] 23 | ``` 24 | ## Contributing 25 | 26 | 1. Fork it ( https://github.com/[my-github-username]/text_2_sign/fork ) 27 | 2. Create your feature branch (`git checkout -b feature/my_new_feature`) 28 | 3. Commit your changes (`git commit -am 'Add some feature'`) 29 | 4. Push to the branch (`git push origin my-new-feature`) 30 | 5. Create a new Pull Request 31 | 32 | ## Special Thanks To: 33 | 34 | The people @ [Signing Savvy](https://www.signingsavvy.com/) for all 35 | the resources that made this library possible. 36 | 37 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for 9 | # 3rd-party users, it should be done in your "mix.exs" file. 10 | 11 | # You can configure for your application as: 12 | # 13 | # config :text_2_sign, key: :value 14 | # 15 | # And access this configuration in your application as: 16 | # 17 | # Application.get_env(:text_2_sign, :key) 18 | # 19 | # Or configure a 3rd-party app: 20 | # 21 | # config :logger, level: :info 22 | # 23 | 24 | # It is also possible to import configuration files, relative to this 25 | # directory. For example, you can emulate configuration per environment 26 | # by uncommenting the line below and defining dev.exs, test.exs and such. 27 | # Configuration from the imported file will override the ones defined 28 | # here (which is why it is important to import them last). 29 | # 30 | # import_config "#{Mix.env}.exs" 31 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{"certifi": {:hex, :certifi, "0.7.0", "861a57f3808f7eb0c2d1802afeaae0fa5de813b0df0979153cbafcd853ababaf", [:rebar3], []}, 2 | "floki": {:hex, :floki, "0.11.0", "b4532ab64d67225f13f5626e4ba1b8cf3ee9d5bd48017075bb975e1522efd32d", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, optional: false]}]}, 3 | "hackney": {:hex, :hackney, "1.6.2", "96a0a5e7e65b7acad8031d231965718cc70a9b4131a8b033b7543bbd673b8210", [:rebar3, :mix], [{:certifi, "0.7.0", [hex: :certifi, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]}, 4 | "httpoison": {:hex, :httpoison, "0.9.2", "a211a8e87403a043c41218e64df250d321f236ac57f786c6a0ccf3e9e817c819", [:mix], [{:hackney, "~> 1.6.0", [hex: :hackney, optional: false]}]}, 5 | "idna": {:hex, :idna, "1.2.0", "ac62ee99da068f43c50dc69acf700e03a62a348360126260e87f2b54eced86b2", [:rebar3], []}, 6 | "meck": {:hex, :meck, "0.8.4", "59ca1cd971372aa223138efcf9b29475bde299e1953046a0c727184790ab1520", [:rebar, :make], []}, 7 | "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, 8 | "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, 9 | "mochiweb": {:hex, :mochiweb, "2.15.0", "e1daac474df07651e5d17cc1e642c4069c7850dc4508d3db7263a0651330aacc", [:rebar3], []}, 10 | "mock": {:hex, :mock, "0.2.0", "5991877be6bb514b647dbd6f4869bc12bd7f2829df16e86c98d6108f966d34d7", [:mix], [{:meck, "~> 0.8.2", [hex: :meck, optional: false]}]}, 11 | "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:rebar, :make], []}} 12 | --------------------------------------------------------------------------------