mirror of
https://github.com/iv-org/invidious.git
synced 2025-03-13 09:26:35 -04:00
Merge b709941033a961bb2e5666f73635751fa6542bc4 into adcdb8cb92bbf61bac46102eff026593d0bc87b0
This commit is contained in:
commit
43fbb5e12c
@ -110,6 +110,16 @@ db:
|
|||||||
##
|
##
|
||||||
domain:
|
domain:
|
||||||
|
|
||||||
|
##
|
||||||
|
## Alternative domains. You can add other domains if you
|
||||||
|
## serve invidious on more than one domain, like Tor
|
||||||
|
## and I2P addresses.
|
||||||
|
##
|
||||||
|
## Accepted values: an array of fully qualified domain names (FQDN)
|
||||||
|
## Default: <none>
|
||||||
|
##
|
||||||
|
#alternative_domains: []
|
||||||
|
|
||||||
##
|
##
|
||||||
## Tell Invidious that it is behind a proxy that provides only
|
## Tell Invidious that it is behind a proxy that provides only
|
||||||
## HTTPS, so all links must use the https:// scheme. This
|
## HTTPS, so all links must use the https:// scheme. This
|
||||||
|
@ -105,6 +105,8 @@ class Config
|
|||||||
property hmac_key : String = ""
|
property hmac_key : String = ""
|
||||||
# Domain to be used for links to resources on the site where an absolute URL is required
|
# Domain to be used for links to resources on the site where an absolute URL is required
|
||||||
property domain : String?
|
property domain : String?
|
||||||
|
# Alternative domains. You can add other domains, like TOR and I2P addresses
|
||||||
|
property alternative_domains : Array(String) = [] of String
|
||||||
# Subscribe to channels using PubSubHubbub (requires domain, hmac_key)
|
# Subscribe to channels using PubSubHubbub (requires domain, hmac_key)
|
||||||
property use_pubsub_feeds : Bool | Int32 = false
|
property use_pubsub_feeds : Bool | Int32 = false
|
||||||
property popular_enabled : Bool = true
|
property popular_enabled : Bool = true
|
||||||
|
@ -209,7 +209,7 @@ module Invidious::Routes::API::Manifest
|
|||||||
|
|
||||||
raw_params["host"] = uri.host.not_nil!
|
raw_params["host"] = uri.host.not_nil!
|
||||||
|
|
||||||
"#{HOST_URL}/videoplayback?#{raw_params}"
|
"#{env.request.headers["Host"]}/videoplayback?#{raw_params}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ module Invidious::Routes::API::V1::Authenticated
|
|||||||
end
|
end
|
||||||
|
|
||||||
playlist = create_playlist(title, privacy, user)
|
playlist = create_playlist(title, privacy, user)
|
||||||
env.response.headers["Location"] = "#{HOST_URL}/api/v1/auth/playlists/#{playlist.id}"
|
env.response.headers["Location"] = "#{env.request.headers["Host"]}/api/v1/auth/playlists/#{playlist.id}"
|
||||||
env.response.status_code = 201
|
env.response.status_code = 201
|
||||||
{
|
{
|
||||||
"title" => title,
|
"title" => title,
|
||||||
@ -336,7 +336,7 @@ module Invidious::Routes::API::V1::Authenticated
|
|||||||
Invidious::Database::PlaylistVideos.insert(playlist_video)
|
Invidious::Database::PlaylistVideos.insert(playlist_video)
|
||||||
Invidious::Database::Playlists.update_video_added(plid, playlist_video.index)
|
Invidious::Database::Playlists.update_video_added(plid, playlist_video.index)
|
||||||
|
|
||||||
env.response.headers["Location"] = "#{HOST_URL}/api/v1/auth/playlists/#{plid}/videos/#{playlist_video.index.to_u64.to_s(16).upcase}"
|
env.response.headers["Location"] = "#{env.request.headers["Host"]}/api/v1/auth/playlists/#{plid}/videos/#{playlist_video.index.to_u64.to_s(16).upcase}"
|
||||||
env.response.status_code = 201
|
env.response.status_code = 201
|
||||||
|
|
||||||
JSON.build do |json|
|
JSON.build do |json|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
module Invidious::Routes::ErrorRoutes
|
module Invidious::Routes::ErrorRoutes
|
||||||
def self.error_404(env)
|
def self.error_404(env)
|
||||||
# Workaround for #3117
|
# Workaround for #3117
|
||||||
if HOST_URL.empty? && env.request.path.starts_with?("/v1/storyboards/sb")
|
if env.request.headers["Host"].empty? && env.request.path.starts_with?("/v1/storyboards/sb")
|
||||||
return env.redirect "#{env.request.path[15..]}?#{env.params.query}"
|
return env.redirect "#{env.request.path[15..]}?#{env.params.query}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -199,21 +199,21 @@ module Invidious::Routes::Feeds
|
|||||||
xml.element("feed", "xmlns:yt": "http://www.youtube.com/xml/schemas/2015",
|
xml.element("feed", "xmlns:yt": "http://www.youtube.com/xml/schemas/2015",
|
||||||
"xmlns:media": "http://search.yahoo.com/mrss/", xmlns: "http://www.w3.org/2005/Atom",
|
"xmlns:media": "http://search.yahoo.com/mrss/", xmlns: "http://www.w3.org/2005/Atom",
|
||||||
"xml:lang": "en-US") do
|
"xml:lang": "en-US") do
|
||||||
xml.element("link", rel: "self", href: "#{HOST_URL}#{env.request.resource}")
|
xml.element("link", rel: "self", href: "#{env.request.headers["Host"]}#{env.request.resource}")
|
||||||
xml.element("id") { xml.text "yt:channel:#{ucid}" }
|
xml.element("id") { xml.text "yt:channel:#{ucid}" }
|
||||||
xml.element("yt:channelId") { xml.text ucid }
|
xml.element("yt:channelId") { xml.text ucid }
|
||||||
xml.element("title") { author }
|
xml.element("title") { author }
|
||||||
xml.element("link", rel: "alternate", href: "#{HOST_URL}/channel/#{ucid}")
|
xml.element("link", rel: "alternate", href: "#{env.request.headers["Host"]}/channel/#{ucid}")
|
||||||
|
|
||||||
xml.element("author") do
|
xml.element("author") do
|
||||||
xml.element("name") { xml.text author }
|
xml.element("name") { xml.text author }
|
||||||
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{ucid}" }
|
xml.element("uri") { xml.text "#{env.request.headers["Host"]}/channel/#{ucid}" }
|
||||||
end
|
end
|
||||||
|
|
||||||
xml.element("image") do
|
xml.element("image") do
|
||||||
xml.element("url") { xml.text "" }
|
xml.element("url") { xml.text "" }
|
||||||
xml.element("title") { xml.text author }
|
xml.element("title") { xml.text author }
|
||||||
xml.element("link", rel: "self", href: "#{HOST_URL}#{env.request.resource}")
|
xml.element("link", rel: "self", href: "#{env.request.headers["Host"]}#{env.request.resource}")
|
||||||
end
|
end
|
||||||
|
|
||||||
videos.each do |video|
|
videos.each do |video|
|
||||||
@ -255,9 +255,9 @@ module Invidious::Routes::Feeds
|
|||||||
xml.element("feed", "xmlns:yt": "http://www.youtube.com/xml/schemas/2015",
|
xml.element("feed", "xmlns:yt": "http://www.youtube.com/xml/schemas/2015",
|
||||||
"xmlns:media": "http://search.yahoo.com/mrss/", xmlns: "http://www.w3.org/2005/Atom",
|
"xmlns:media": "http://search.yahoo.com/mrss/", xmlns: "http://www.w3.org/2005/Atom",
|
||||||
"xml:lang": "en-US") do
|
"xml:lang": "en-US") do
|
||||||
xml.element("link", "type": "text/html", rel: "alternate", href: "#{HOST_URL}/feed/subscriptions")
|
xml.element("link", "type": "text/html", rel: "alternate", href: "#{env.request.headers["Host"]}/feed/subscriptions")
|
||||||
xml.element("link", "type": "application/atom+xml", rel: "self",
|
xml.element("link", "type": "application/atom+xml", rel: "self",
|
||||||
href: "#{HOST_URL}#{env.request.resource}")
|
href: "#{env.request.headers["Host"]}#{env.request.resource}")
|
||||||
xml.element("title") { xml.text translate(locale, "Invidious Private Feed for `x`", user.email) }
|
xml.element("title") { xml.text translate(locale, "Invidious Private Feed for `x`", user.email) }
|
||||||
|
|
||||||
(notifications + videos).each do |video|
|
(notifications + videos).each do |video|
|
||||||
@ -286,11 +286,11 @@ module Invidious::Routes::Feeds
|
|||||||
xml.element("feed", "xmlns:yt": "http://www.youtube.com/xml/schemas/2015",
|
xml.element("feed", "xmlns:yt": "http://www.youtube.com/xml/schemas/2015",
|
||||||
"xmlns:media": "http://search.yahoo.com/mrss/", xmlns: "http://www.w3.org/2005/Atom",
|
"xmlns:media": "http://search.yahoo.com/mrss/", xmlns: "http://www.w3.org/2005/Atom",
|
||||||
"xml:lang": "en-US") do
|
"xml:lang": "en-US") do
|
||||||
xml.element("link", rel: "self", href: "#{HOST_URL}#{env.request.resource}")
|
xml.element("link", rel: "self", href: "#{env.request.headers["Host"]}#{env.request.resource}")
|
||||||
xml.element("id") { xml.text "iv:playlist:#{plid}" }
|
xml.element("id") { xml.text "iv:playlist:#{plid}" }
|
||||||
xml.element("iv:playlistId") { xml.text plid }
|
xml.element("iv:playlistId") { xml.text plid }
|
||||||
xml.element("title") { xml.text playlist.title }
|
xml.element("title") { xml.text playlist.title }
|
||||||
xml.element("link", rel: "alternate", href: "#{HOST_URL}/playlist?list=#{plid}")
|
xml.element("link", rel: "alternate", href: "#{env.request.headers["Host"]}/playlist?list=#{plid}")
|
||||||
|
|
||||||
xml.element("author") do
|
xml.element("author") do
|
||||||
xml.element("name") { xml.text playlist.author }
|
xml.element("name") { xml.text playlist.author }
|
||||||
@ -314,7 +314,7 @@ module Invidious::Routes::Feeds
|
|||||||
when "url", "href"
|
when "url", "href"
|
||||||
request_target = URI.parse(node[attribute.name]).request_target
|
request_target = URI.parse(node[attribute.name]).request_target
|
||||||
query_string_opt = request_target.starts_with?("/watch?v=") ? "&#{params}" : ""
|
query_string_opt = request_target.starts_with?("/watch?v=") ? "&#{params}" : ""
|
||||||
node[attribute.name] = "#{HOST_URL}#{request_target}#{query_string_opt}"
|
node[attribute.name] = "#{env.request.headers["Host"]}#{request_target}#{query_string_opt}"
|
||||||
else nil # Skip
|
else nil # Skip
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -323,7 +323,7 @@ module Invidious::Routes::Feeds
|
|||||||
document = document.to_xml(options: XML::SaveOptions::NO_DECL)
|
document = document.to_xml(options: XML::SaveOptions::NO_DECL)
|
||||||
|
|
||||||
document.scan(/<uri>(?<url>[^<]+)<\/uri>/).each do |match|
|
document.scan(/<uri>(?<url>[^<]+)<\/uri>/).each do |match|
|
||||||
content = "#{HOST_URL}#{URI.parse(match["url"]).request_target}"
|
content = "#{env.request.headers["Host"]}#{URI.parse(match["url"]).request_target}"
|
||||||
document = document.gsub(match[0], "<uri>#{content}</uri>")
|
document = document.gsub(match[0], "<uri>#{content}</uri>")
|
||||||
end
|
end
|
||||||
document
|
document
|
||||||
|
@ -60,7 +60,13 @@ module Invidious::Routes::Login
|
|||||||
sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32))
|
sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32))
|
||||||
Invidious::Database::SessionIDs.insert(sid, email)
|
Invidious::Database::SessionIDs.insert(sid, email)
|
||||||
|
|
||||||
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid)
|
# Checks if there is any alternative domain, like a second domain name,
|
||||||
|
# TOR or I2P address
|
||||||
|
if alt = CONFIG.alternative_domains.index(env.request.headers["Host"])
|
||||||
|
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.alternative_domains[alt], sid)
|
||||||
|
else
|
||||||
|
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
return error_template(401, "Wrong username or password")
|
return error_template(401, "Wrong username or password")
|
||||||
end
|
end
|
||||||
@ -163,7 +169,13 @@ module Invidious::Routes::Login
|
|||||||
view_name = "subscriptions_#{sha256(user.email)}"
|
view_name = "subscriptions_#{sha256(user.email)}"
|
||||||
PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(user.email)}")
|
PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(user.email)}")
|
||||||
|
|
||||||
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid)
|
# Checks if there is any alternative domain, like a second domain name,
|
||||||
|
# TOR or I2P address
|
||||||
|
if alt = CONFIG.alternative_domains.index(env.request.headers["Host"])
|
||||||
|
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.alternative_domains[alt], sid)
|
||||||
|
else
|
||||||
|
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid)
|
||||||
|
end
|
||||||
|
|
||||||
if env.request.cookies["PREFS"]?
|
if env.request.cookies["PREFS"]?
|
||||||
user.preferences = env.get("preferences").as(Preferences)
|
user.preferences = env.get("preferences").as(Preferences)
|
||||||
|
@ -224,7 +224,13 @@ module Invidious::Routes::PreferencesRoute
|
|||||||
File.write("config/config.yml", CONFIG.to_yaml)
|
File.write("config/config.yml", CONFIG.to_yaml)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
env.response.cookies["PREFS"] = Invidious::User::Cookies.prefs(CONFIG.domain, preferences)
|
# Checks if there is any alternative domain, like a second domain name,
|
||||||
|
# TOR or I2P address
|
||||||
|
if alt = CONFIG.alternative_domains.index(env.request.headers["Host"])
|
||||||
|
env.response.cookies["PREFS"] = Invidious::User::Cookies.prefs(CONFIG.alternative_domains[alt], preferences)
|
||||||
|
else
|
||||||
|
env.response.cookies["PREFS"] = Invidious::User::Cookies.prefs(CONFIG.domain, preferences)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
env.redirect referer
|
env.redirect referer
|
||||||
@ -259,7 +265,13 @@ module Invidious::Routes::PreferencesRoute
|
|||||||
preferences.dark_mode = "dark"
|
preferences.dark_mode = "dark"
|
||||||
end
|
end
|
||||||
|
|
||||||
env.response.cookies["PREFS"] = Invidious::User::Cookies.prefs(CONFIG.domain, preferences)
|
# Checks if there is any alternative domain, like a second domain name,
|
||||||
|
# TOR or I2P address
|
||||||
|
if alt = CONFIG.alternative_domains.index(env.request.headers["Host"])
|
||||||
|
env.response.cookies["PREFS"] = Invidious::User::Cookies.prefs(CONFIG.alternative_domains[alt], preferences)
|
||||||
|
else
|
||||||
|
env.response.cookies["PREFS"] = Invidious::User::Cookies.prefs(CONFIG.domain, preferences)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if redirect
|
if redirect
|
||||||
|
@ -11,8 +11,8 @@ module Invidious::Routes::Search
|
|||||||
xml.element("LongName") { xml.text "Invidious Search" }
|
xml.element("LongName") { xml.text "Invidious Search" }
|
||||||
xml.element("Description") { xml.text "Search for videos, channels, and playlists on Invidious" }
|
xml.element("Description") { xml.text "Search for videos, channels, and playlists on Invidious" }
|
||||||
xml.element("InputEncoding") { xml.text "UTF-8" }
|
xml.element("InputEncoding") { xml.text "UTF-8" }
|
||||||
xml.element("Image", width: 48, height: 48, type: "image/x-icon") { xml.text "#{HOST_URL}/favicon.ico" }
|
xml.element("Image", width: 48, height: 48, type: "image/x-icon") { xml.text "#{env.request.headers["Host"]}/favicon.ico" }
|
||||||
xml.element("Url", type: "text/html", method: "get", template: "#{HOST_URL}/search?q={searchTerms}")
|
xml.element("Url", type: "text/html", method: "get", template: "#{env.request.headers["Host"]}/search?q={searchTerms}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -6,17 +6,23 @@ struct Invidious::User
|
|||||||
|
|
||||||
# Note: we use ternary operator because the two variables
|
# Note: we use ternary operator because the two variables
|
||||||
# used in here are not booleans.
|
# used in here are not booleans.
|
||||||
SECURE = (Kemal.config.ssl || CONFIG.https_only) ? true : false
|
@@secure = (Kemal.config.ssl || CONFIG.https_only) ? true : false
|
||||||
|
|
||||||
# Session ID (SID) cookie
|
# Session ID (SID) cookie
|
||||||
# Parameter "domain" comes from the global config
|
# Parameter "domain" comes from the global config
|
||||||
def sid(domain : String?, sid) : HTTP::Cookie
|
def sid(domain : String?, sid) : HTTP::Cookie
|
||||||
|
# Not secure if it's being accessed from I2P
|
||||||
|
# Browsers expect the domain to include https. On I2P there is no HTTPS
|
||||||
|
if domain.not_nil!.split(".").last == "i2p"
|
||||||
|
@@secure = false
|
||||||
|
end
|
||||||
|
|
||||||
return HTTP::Cookie.new(
|
return HTTP::Cookie.new(
|
||||||
name: "SID",
|
name: "SID",
|
||||||
domain: domain,
|
domain: domain,
|
||||||
value: sid,
|
value: sid,
|
||||||
expires: Time.utc + 2.years,
|
expires: Time.utc + 2.years,
|
||||||
secure: SECURE,
|
secure: @@secure,
|
||||||
http_only: true,
|
http_only: true,
|
||||||
samesite: HTTP::Cookie::SameSite::Lax
|
samesite: HTTP::Cookie::SameSite::Lax
|
||||||
)
|
)
|
||||||
@ -25,12 +31,18 @@ struct Invidious::User
|
|||||||
# Preferences (PREFS) cookie
|
# Preferences (PREFS) cookie
|
||||||
# Parameter "domain" comes from the global config
|
# Parameter "domain" comes from the global config
|
||||||
def prefs(domain : String?, preferences : Preferences) : HTTP::Cookie
|
def prefs(domain : String?, preferences : Preferences) : HTTP::Cookie
|
||||||
|
# Not secure if it's being accessed from I2P
|
||||||
|
# Browsers expect the domain to include https. On I2P there is no HTTPS
|
||||||
|
if domain.not_nil!.split(".").last == "i2p"
|
||||||
|
@@secure = false
|
||||||
|
end
|
||||||
|
|
||||||
return HTTP::Cookie.new(
|
return HTTP::Cookie.new(
|
||||||
name: "PREFS",
|
name: "PREFS",
|
||||||
domain: domain,
|
domain: domain,
|
||||||
value: URI.encode_www_form(preferences.to_json),
|
value: URI.encode_www_form(preferences.to_json),
|
||||||
expires: Time.utc + 2.years,
|
expires: Time.utc + 2.years,
|
||||||
secure: SECURE,
|
secure: @@secure,
|
||||||
http_only: false,
|
http_only: false,
|
||||||
samesite: HTTP::Cookie::SameSite::Lax
|
samesite: HTTP::Cookie::SameSite::Lax
|
||||||
)
|
)
|
||||||
|
@ -25,21 +25,22 @@
|
|||||||
first_page: continuation.nil?,
|
first_page: continuation.nil?,
|
||||||
params: env.params.query,
|
params: env.params.query,
|
||||||
)
|
)
|
||||||
|
host = env.request.headers["Host"]
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<%- if selected_tab.videos? -%>
|
<%- if selected_tab.videos? -%>
|
||||||
<meta name="description" content="<%= channel.description %>">
|
<meta name="description" content="<%= channel.description %>">
|
||||||
<meta property="og:site_name" content="Invidious">
|
<meta property="og:site_name" content="Invidious">
|
||||||
<meta property="og:url" content="<%= HOST_URL %>/channel/<%= ucid %>">
|
<meta property="og:url" content="<%= host %>/channel/<%= ucid %>">
|
||||||
<meta property="og:title" content="<%= author %>">
|
<meta property="og:title" content="<%= author %>">
|
||||||
<meta property="og:image" content="<%= HOST_URL %>/ggpht<%= channel_profile_pic %>">
|
<meta property="og:image" content="<%= host %>/ggpht<%= channel_profile_pic %>">
|
||||||
<meta property="og:description" content="<%= channel.description %>">
|
<meta property="og:description" content="<%= channel.description %>">
|
||||||
<meta name="twitter:card" content="summary">
|
<meta name="twitter:card" content="summary">
|
||||||
<meta name="twitter:url" content="<%= HOST_URL %>/channel/<%= ucid %>">
|
<meta name="twitter:url" content="<%= host %>/channel/<%= ucid %>">
|
||||||
<meta name="twitter:title" content="<%= author %>">
|
<meta name="twitter:title" content="<%= author %>">
|
||||||
<meta name="twitter:description" content="<%= channel.description %>">
|
<meta name="twitter:description" content="<%= channel.description %>">
|
||||||
<meta name="twitter:image" content="<%= HOST_URL %>/ggpht<%= channel_profile_pic %>">
|
<meta name="twitter:image" content="<%= host %>/ggpht<%= channel_profile_pic %>">
|
||||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="/feed/channel/<%= ucid %>" />
|
<link rel="alternate" type="application/rss+xml" title="RSS" href="/feed/channel/<%= ucid %>" />
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
|
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
<% ucid = video.ucid %>
|
<% ucid = video.ucid %>
|
||||||
<% title = HTML.escape(video.title) %>
|
<% title = HTML.escape(video.title) %>
|
||||||
<% author = HTML.escape(video.author) %>
|
<% author = HTML.escape(video.author) %>
|
||||||
|
<% host = env.request.headers["Host"] %>
|
||||||
|
|
||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<meta name="thumbnail" content="<%= thumbnail %>">
|
<meta name="thumbnail" content="<%= thumbnail %>">
|
||||||
<meta name="description" content="<%= HTML.escape(video.short_description) %>">
|
<meta name="description" content="<%= HTML.escape(video.short_description) %>">
|
||||||
<meta name="keywords" content="<%= video.keywords.join(",") %>">
|
<meta name="keywords" content="<%= video.keywords.join(",") %>">
|
||||||
<meta property="og:site_name" content="<%= author %> | Invidious">
|
<meta property="og:site_name" content="<%= author %> | Invidious">
|
||||||
<meta property="og:url" content="<%= HOST_URL %>/watch?v=<%= video.id %>">
|
<meta property="og:url" content="<%= host %>/watch?v=<%= video.id %>">
|
||||||
<meta property="og:title" content="<%= title %>">
|
<meta property="og:title" content="<%= title %>">
|
||||||
<meta property="og:image" content="<%= HOST_URL %>/vi/<%= video.id %>/maxres.jpg">
|
<meta property="og:image" content="<%= host %>/vi/<%= video.id %>/maxres.jpg">
|
||||||
<meta property="og:description" content="<%= HTML.escape(video.short_description) %>">
|
<meta property="og:description" content="<%= HTML.escape(video.short_description) %>">
|
||||||
<meta property="og:type" content="video.other">
|
<meta property="og:type" content="video.other">
|
||||||
<meta property="og:video:url" content="<%= HOST_URL %>/embed/<%= video.id %>">
|
<meta property="og:video:url" content="<%= host %>/embed/<%= video.id %>">
|
||||||
<meta property="og:video:secure_url" content="<%= HOST_URL %>/embed/<%= video.id %>">
|
<meta property="og:video:secure_url" content="<%= host %>/embed/<%= video.id %>">
|
||||||
<meta property="og:video:type" content="text/html">
|
<meta property="og:video:type" content="text/html">
|
||||||
<meta property="og:video:width" content="1280">
|
<meta property="og:video:width" content="1280">
|
||||||
<meta property="og:video:height" content="720">
|
<meta property="og:video:height" content="720">
|
||||||
<meta name="twitter:card" content="player">
|
<meta name="twitter:card" content="player">
|
||||||
<meta name="twitter:url" content="<%= HOST_URL %>/watch?v=<%= video.id %>">
|
<meta name="twitter:url" content="<%= host %>/watch?v=<%= video.id %>">
|
||||||
<meta name="twitter:title" content="<%= title %>">
|
<meta name="twitter:title" content="<%= title %>">
|
||||||
<meta name="twitter:description" content="<%= HTML.escape(video.short_description) %>">
|
<meta name="twitter:description" content="<%= HTML.escape(video.short_description) %>">
|
||||||
<meta name="twitter:image" content="<%= HOST_URL %>/vi/<%= video.id %>/maxres.jpg">
|
<meta name="twitter:image" content="<%= host %>/vi/<%= video.id %>/maxres.jpg">
|
||||||
<meta name="twitter:player" content="<%= HOST_URL %>/embed/<%= video.id %>">
|
<meta name="twitter:player" content="<%= host %>/embed/<%= video.id %>">
|
||||||
<meta name="twitter:player:width" content="1280">
|
<meta name="twitter:player:width" content="1280">
|
||||||
<meta name="twitter:player:height" content="720">
|
<meta name="twitter:player:height" content="720">
|
||||||
<link rel="alternate" href="https://www.youtube.com/watch?v=<%= video.id %>">
|
<link rel="alternate" href="https://www.youtube.com/watch?v=<%= video.id %>">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user