diff --git a/fixture/vcr_cassettes/tiktok_invalid.json b/fixture/vcr_cassettes/tiktok_invalid.json
new file mode 100644
index 0000000..ef5ef23
--- /dev/null
+++ b/fixture/vcr_cassettes/tiktok_invalid.json
@@ -0,0 +1,25 @@
+[
+ {
+ "request": {
+ "body": "",
+ "headers": [],
+ "method": "get",
+ "options": {
+ "follow_redirect": "true",
+ "ssl_options": {
+ "versions": {
+ "tlsv1.2": true
+ }
+ }
+ },
+ "request_body": "",
+ "url": "https://www.tiktok.com/@scout2015/video/invalid_url"
+ },
+ "response": {
+ "binary": false,
+ "message": "Something went wrong",
+ "status_code": 400,
+ "type": "ok"
+ }
+ }
+]
diff --git a/fixture/vcr_cassettes/tiktok_valid.json b/fixture/vcr_cassettes/tiktok_valid.json
new file mode 100644
index 0000000..621ddae
--- /dev/null
+++ b/fixture/vcr_cassettes/tiktok_valid.json
@@ -0,0 +1,50 @@
+[
+ {
+ "request": {
+ "body": "",
+ "headers": [],
+ "method": "get",
+ "options": {
+ "follow_redirect": "true",
+ "ssl_options": {
+ "versions": {
+ "tlsv1.2": true
+ }
+ }
+ },
+ "request_body": "",
+ "url": "https://www.tiktok.com/oembed?url=https%3A%2F%2Fwww.tiktok.com%2F%40scout2015%2Fvideo%2F6718335390845095173"
+ },
+ "response": {
+ "binary": false,
+ "body": "{\"version\":\"1.0\",\"type\":\"video\",\"title\":\"Scramble up ur name & I’ll try to guess it😍❤️ #foryoupage #petsoftiktok #aesthetic\",\"author_url\":\"https://www.tiktok.com/@scout2015\",\"author_name\":\"Scout, Suki & Stella\",\"width\":\"100%\",\"height\":\"100%\",\"html\":\"
\",\"thumbnail_width\":720,\"thumbnail_height\":1280,\"thumbnail_url\":\"https://p16-sign-va.tiktokcdn.com/obj/tos-maliva-p-0068/2367c7d45cf54a1397abd0e72bf22eac?x-expires=1665003600&x-signature=Fpv8iDJCk%2Bt8PO%2F8%2FjE1MNLY4Lc%3D\",\"provider_url\":\"https://www.tiktok.com\",\"provider_name\":\"TikTok\"}",
+ "headers": {
+ "Server": "nginx",
+ "Content-Type": "application/json; charset=utf-8",
+ "X-Tt-Logid": "20221005154816F14388128A319830E5F4",
+ "X-Xss-Protection": "1; mode=block",
+ "X-Frame-Options": "SAMEORIGIN",
+ "X-Content-Type-Options": "nosniff",
+ "X-Download-Options": "noopen",
+ "x-tt-trace-host": "0140a3cbc36dbd2901a952d68819c5a5cbe21382bcb1cf4df83480642d2901718f581ee66fc93afe022de4bdec10ba453b9895718999ec854d41ac681115f19628fda5204bc135e5cd882aae960fc06ba6eb7736d92dcf9c9abb3c3f30b7634ecb",
+ "Access-Control-Allow-Origin": "*",
+ "X-Origin-Response-Time": "64,23.45.233.7",
+ "X-Akamai-Request-ID": "1b69d3c8.7a5f8eb",
+ "Expires": "Wed, 05 Oct 2022 15:48:16 GMT",
+ "Cache-Control": "max-age=0, no-cache, no-store",
+ "Pragma": "no-cache",
+ "Date": "Wed, 05 Oct 2022 15:48:16 GMT",
+ "Content-Length": "1563",
+ "X-Cache": "TCP_MISS from a23-45-12-173.deploy.akamaitechnologies.com (AkamaiGHost/10.9.5-44379351) (-)",
+ "Connection": "keep-alive",
+ "X-Cache-Remote": "TCP_MISS from a23-45-233-7.deploy.akamaitechnologies.com (AkamaiGHost/10.9.5-44379351) (-)",
+ "x-tt-trace-tag": "id=16;cdn-cache=miss;type=dyn",
+ "Server-Timing": "cdn-cache; desc=MISS, edge; dur=97, origin; dur=64",
+ "X-Parent-Response-Time": "161,23.45.12.173",
+ "Set-Cookie": "_abck=BD0C68FA825306B5EA8113BA8184691B~-1~YAAQrQwtF94iPp+DAQAAylLVqAj8iBkIcUKELOZskzFbnO2pr8sYgc7UJCSSj+NWtQrYR49yiwQKnodkVSFDk7QckimIYrRRaFb9hxSciev1yfK9+CG+TncxsggVkloUI7OoLKr0718kCZjX0AncP8n87LiO/UGbqPE/Bi6P2L5oJGT6bgXQec5XsokrJxHCc3DXGYtjztay1OS3TEZBBogksGKtw/ilawqe2TEiemyt0wT1hHECaOXEaZSaKwm44HGggaFUju3J+mK4igrWYW/Zij1nQkW5+GWu3C/7HCjyY22yYmMKwJMzFgYUp068IBbtzBgq5LXA2anRiizh2P/40KS1Qottmq0hFeB9Sm67E4c8tw8aubg=~-1~-1~-1; Domain=.tiktok.com; Path=/; Expires=Thu, 05 Oct 2023 15:48:16 GMT; Max-Age=31536000; Secure"
+ },
+ "status_code": 200,
+ "type": "ok"
+ }
+ }
+]
\ No newline at end of file
diff --git a/fixture/vcr_cassettes/twitter_status_invalid.json b/fixture/vcr_cassettes/twitter_status_invalid.json
new file mode 100644
index 0000000..36fb91e
--- /dev/null
+++ b/fixture/vcr_cassettes/twitter_status_invalid.json
@@ -0,0 +1,45 @@
+[
+ {
+ "request": {
+ "body": "",
+ "headers": [],
+ "method": "get",
+ "options": {
+ "follow_redirect": "true",
+ "ssl_options": {
+ "versions": {
+ "tlsv1.2": true
+ }
+ }
+ },
+ "request_body": "",
+ "url": "https://www.twitter.com/jack/status/invalid_url/"
+ },
+ "response": {
+ "binary": false,
+ "body": "\n\n\n\n\n\n\n\n\n ",
+ "headers": {
+ "date": "Fri, 09 Jul 2021 02:36:34 GMT",
+ "expiry": "Tue, 31 Mar 1981 05:00:00 GMT",
+ "pragma": "no-cache",
+ "server": "tsa_a",
+ "set-cookie": "personalization_id=\"v1_5d1/CU/p/G2H28GB5lnkOw==\"; Max-Age=63072000; Expires=Sun, 09 Jul 2023 02:36:34 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None",
+ "content-type": "text/html; charset=utf-8",
+ "x-powered-by": "Express",
+ "cache-control": "no-cache, no-store, must-revalidate, pre-check=0, post-check=0",
+ "last-modified": "Fri, 09 Jul 2021 02:36:34 GMT",
+ "x-frame-options": "DENY",
+ "x-xss-protection": "0",
+ "x-content-type-options": "nosniff",
+ "content-security-policy": "connect-src 'self' blob: https://*.giphy.com https://*.pscp.tv https://*.video.pscp.tv https://*.twimg.com https://api.twitter.com https://api-stream.twitter.com https://ads-api.twitter.com https://aa.twitter.com https://caps.twitter.com https://media.riffsy.com https://pay.twitter.com https://sentry.io https://ton.twitter.com https://twitter.com https://upload.twitter.com https://www.google-analytics.com https://app.link https://api2.branch.io https://bnc.lt wss://*.pscp.tv https://vmap.snappytv.com https://vmapstage.snappytv.com https://vmaprel.snappytv.com https://vmap.grabyo.com https://dhdsnappytv-vh.akamaihd.net https://pdhdsnappytv-vh.akamaihd.net https://mdhdsnappytv-vh.akamaihd.net https://mdhdsnappytv-vh.akamaihd.net https://mpdhdsnappytv-vh.akamaihd.net https://mmdhdsnappytv-vh.akamaihd.net https://mdhdsnappytv-vh.akamaihd.net https://mpdhdsnappytv-vh.akamaihd.net https://mmdhdsnappytv-vh.akamaihd.net https://dwo3ckksxlb0v.cloudfront.net ; default-src 'self'; form-action 'self' https://twitter.com https://*.twitter.com; font-src 'self' https://*.twimg.com; frame-src 'self' https://twitter.com https://mobile.twitter.com https://pay.twitter.com https://cards-frame.twitter.com https://accounts.google.com/; img-src 'self' blob: data: https://*.cdn.twitter.com https://ton.twitter.com https://*.twimg.com https://analytics.twitter.com https://cm.g.doubleclick.net https://www.google-analytics.com https://www.periscope.tv https://www.pscp.tv https://media.riffsy.com https://*.giphy.com https://*.pscp.tv https://*.periscope.tv https://prod-periscope-profile.s3-us-west-2.amazonaws.com https://platform-lookaside.fbsbx.com https://scontent.xx.fbcdn.net https://scontent-sea1-1.xx.fbcdn.net https://*.googleusercontent.com https://imgix.revue.co; manifest-src 'self'; media-src 'self' blob: https://twitter.com https://*.twimg.com https://*.vine.co https://*.pscp.tv https://*.video.pscp.tv https://*.giphy.com https://media.riffsy.com https://dhdsnappytv-vh.akamaihd.net https://pdhdsnappytv-vh.akamaihd.net https://mdhdsnappytv-vh.akamaihd.net https://mdhdsnappytv-vh.akamaihd.net https://mpdhdsnappytv-vh.akamaihd.net https://mmdhdsnappytv-vh.akamaihd.net https://mdhdsnappytv-vh.akamaihd.net https://mpdhdsnappytv-vh.akamaihd.net https://mmdhdsnappytv-vh.akamaihd.net https://dwo3ckksxlb0v.cloudfront.net; object-src 'none'; script-src 'self' 'unsafe-inline' https://*.twimg.com https://www.google-analytics.com https://twitter.com https://app.link https://apis.google.com/js/platform.js https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js 'nonce-MzkyMGVmYzctODQyYS00ODY5LTgzMmMtN2UzNGE1NTJjNjU4'; style-src 'self' 'unsafe-inline' https://*.twimg.com; worker-src 'self' blob:; report-uri https://twitter.com/i/csp_report?a=O5RXE%3D%3D%3D&ro=false",
+ "strict-transport-security": "max-age=631138519",
+ "cross-origin-opener-policy": "same-origin-allow-popups",
+ "cross-origin-embedder-policy": "unsafe-none",
+ "x-connection-hash": "1ec5111a334e8a88337de721bbf717d812108f1a6559f4569e4d2c3aeca7ec3b",
+ "transfer-encoding": "chunked"
+ },
+ "status_code": 200,
+ "type": "ok"
+ }
+ }
+]
\ No newline at end of file
diff --git a/fixture/vcr_cassettes/twitter_status_valid.json b/fixture/vcr_cassettes/twitter_status_valid.json
new file mode 100644
index 0000000..5de1972
--- /dev/null
+++ b/fixture/vcr_cassettes/twitter_status_valid.json
@@ -0,0 +1,41 @@
+[
+ {
+ "request": {
+ "body": "",
+ "headers": [],
+ "method": "get",
+ "options": {
+ "follow_redirect": "true",
+ "ssl_options": {
+ "versions": {
+ "tlsv1.2": true
+ }
+ }
+ },
+ "request_body": "",
+ "url": "https://publish.twitter.com/oembed?url=https%3A%2F%2Ftwitter.com%2Fjack%2Fstatus%2F20"
+ },
+ "response": {
+ "binary": false,
+ "body": "{\"url\":\"https:\\/\\/twitter.com\\/jack\\/status\\/20\",\"author_name\":\"jack\",\"author_url\":\"https:\\/\\/twitter.com\\/jack\",\"html\":\"\\u003Cblockquote class=\\\"twitter-tweet\\\"\\u003E\\u003Cp lang=\\\"en\\\" dir=\\\"ltr\\\"\\u003Ejust setting up my twttr\\u003C\\/p\\u003E— jack (@jack) \\u003Ca href=\\\"https:\\/\\/twitter.com\\/jack\\/status\\/20?ref_src=twsrc%5Etfw\\\"\\u003EMarch 21, 2006\\u003C\\/a\\u003E\\u003C\\/blockquote\\u003E\\n\\u003Cscript async src=\\\"https:\\/\\/platform.twitter.com\\/widgets.js\\\" charset=\\\"utf-8\\\"\\u003E\\u003C\\/script\\u003E\\n\",\"width\":550,\"height\":null,\"type\":\"rich\",\"cache_age\":\"3153600000\",\"provider_name\":\"Twitter\",\"provider_url\":\"https:\\/\\/twitter.com\",\"version\":\"1.0\"}",
+ "headers": {
+ "date": "Fri, 09 Jul 2021 02:36:28 GMT",
+ "server": "tsa_a",
+ "expires": "Sun, 15 Jun 2121 02:36:29 GMT",
+ "set-cookie": "personalization_id=\"v1_1VCHb8EcTSSour3kTA1gmg==\"; Max-Age=63072000; Expires=Sun, 09 Jul 2023 02:36:29 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None",
+ "content-type": "application/json; charset=utf-8",
+ "cache-control": "must-revalidate, max-age=3153600000",
+ "last-modified": "Fri, 09 Jul 2021 02:36:29 GMT",
+ "content-length": "664",
+ "x-frame-options": "SAMEORIGIN",
+ "x-xss-protection": "0",
+ "content-disposition": "attachment; filename=json.json",
+ "x-content-type-options": "nosniff",
+ "strict-transport-security": "max-age=631138519",
+ "x-connection-hash": "9a68eee7d5f5b51f5057f60e2c65b1d36fcec3d6f702658b8fa7d58c29000dc9"
+ },
+ "status_code": 200,
+ "type": "ok"
+ }
+ }
+]
\ No newline at end of file
diff --git a/lib/oembed.ex b/lib/oembed.ex
index d37d9e1..48e8db8 100644
--- a/lib/oembed.ex
+++ b/lib/oembed.ex
@@ -7,7 +7,9 @@ defmodule OEmbed do
OEmbed.InstagramProvider,
OEmbed.PinterestProvider,
OEmbed.VimeoProvider,
- OEmbed.YoutubeProvider
+ OEmbed.YoutubeProvider,
+ OEmbed.TwitterProvider,
+ OEmbed.TikTokProvider
]
@fallback_providers [OEmbed.DiscoverableProvider]
diff --git a/lib/oembed/providers/tiktok_provider.ex b/lib/oembed/providers/tiktok_provider.ex
new file mode 100644
index 0000000..4dbd2f4
--- /dev/null
+++ b/lib/oembed/providers/tiktok_provider.ex
@@ -0,0 +1,25 @@
+defmodule OEmbed.TikTokProvider do
+ @moduledoc """
+ oEmbed provider for Tiktok URLs.
+ """
+ use OEmbed.Provider
+
+ @oembed_endpoint "https://www.tiktok.com/oembed?url="
+
+ @doc """
+ Check if this provider supports given URL.
+ """
+ def provides?(url) do
+ Regex.match?(
+ ~r/(^|[^'"])(https?:\/\/(www\.)?tiktok\.com\/\@?(\w+)\/video?\/(\d+))/i,
+ url
+ )
+ end
+
+ @doc """
+ Get oEmbed result for given URL.
+ """
+ def get(url) do
+ get_oembed(@oembed_endpoint <> URI.encode(url, &URI.char_unreserved?/1))
+ end
+end
diff --git a/lib/oembed/providers/twitter_provider.ex b/lib/oembed/providers/twitter_provider.ex
new file mode 100644
index 0000000..1c26965
--- /dev/null
+++ b/lib/oembed/providers/twitter_provider.ex
@@ -0,0 +1,25 @@
+defmodule OEmbed.TwitterProvider do
+ @moduledoc """
+ oEmbed provider for Twitter URLs.
+ """
+ use OEmbed.Provider
+
+ @oembed_endpoint "https://publish.twitter.com/oembed?url="
+
+ @doc """
+ Check if this provider supports given URL.
+ """
+ def provides?(url) do
+ Regex.match?(
+ ~r/(^|[^'"])(https?:\/\/twitter\.com\/(?:#!\/)?(\w+)\/status(?:es)?\/(\d+))/i,
+ url
+ )
+ end
+
+ @doc """
+ Get oEmbed result for given URL.
+ """
+ def get(url) do
+ get_oembed(@oembed_endpoint <> URI.encode(url, &URI.char_unreserved?/1))
+ end
+end
diff --git a/test/oembed_test.exs b/test/oembed_test.exs
index eb6a75d..1ae1500 100644
--- a/test/oembed_test.exs
+++ b/test/oembed_test.exs
@@ -87,6 +87,19 @@ defmodule OEmbedTest do
end
end
+ test "gets rich oembed for valid twitter status url" do
+ use_cassette "twitter_status_valid" do
+ {:ok, %Rich{} = oembed} = OEmbed.for("https://twitter.com/jack/status/20")
+ assert oembed.html =~ "just setting up my twttr"
+ end
+ end
+
+ test "gets error response for invalid twitter url" do
+ use_cassette "twitter_status_invalid" do
+ {:error, _} = OEmbed.for("https://www.twitter.com/jack/status/invalid_url/")
+ end
+ end
+
test "gets rich oembed for valid soundcloud url" do
use_cassette "soundcloud_valid" do
{:ok, %Rich{} = oembed} = OEmbed.for("https://soundcloud.com/forss/flickermood")
@@ -129,4 +142,19 @@ defmodule OEmbedTest do
assert oembed.html =~ "vimeo"
end
end
+
+ test "gets video oembed for valid tiktok video url" do
+ use_cassette "tiktok_valid" do
+ {:ok, %Video{} = oembed} =
+ OEmbed.for("https://www.tiktok.com/@scout2015/video/6718335390845095173")
+
+ assert oembed.html =~ "Scramble up ur name"
+ end
+ end
+
+ test "gets error response for invalid tiktok url" do
+ use_cassette "tiktok_invalid" do
+ {:error, _} = OEmbed.for("https://www.tiktok.com/@scout2015/video/invalid_url")
+ end
+ end
end