mirror of
https://gitea.invidious.io/iv-org/invidious-copy-2023-06-08.git
synced 2024-08-15 00:53:38 +00:00
Merge e6bc502104
into 545a5937d8
This commit is contained in:
commit
2d1fdb9ce4
5 changed files with 214 additions and 181 deletions
|
@ -50,46 +50,6 @@ struct ChannelVideo
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_xml(locale, query_params, xml : XML::Builder)
|
|
||||||
query_params["v"] = self.id
|
|
||||||
|
|
||||||
xml.element("entry") do
|
|
||||||
xml.element("id") { xml.text "yt:video:#{self.id}" }
|
|
||||||
xml.element("yt:videoId") { xml.text self.id }
|
|
||||||
xml.element("yt:channelId") { xml.text self.ucid }
|
|
||||||
xml.element("title") { xml.text self.title }
|
|
||||||
xml.element("link", rel: "alternate", href: "#{HOST_URL}/watch?#{query_params}")
|
|
||||||
|
|
||||||
xml.element("author") do
|
|
||||||
xml.element("name") { xml.text self.author }
|
|
||||||
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{self.ucid}" }
|
|
||||||
end
|
|
||||||
|
|
||||||
xml.element("content", type: "xhtml") do
|
|
||||||
xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
|
|
||||||
xml.element("a", href: "#{HOST_URL}/watch?#{query_params}") do
|
|
||||||
xml.element("img", src: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
xml.element("published") { xml.text self.published.to_s("%Y-%m-%dT%H:%M:%S%:z") }
|
|
||||||
xml.element("updated") { xml.text self.updated.to_s("%Y-%m-%dT%H:%M:%S%:z") }
|
|
||||||
|
|
||||||
xml.element("media:group") do
|
|
||||||
xml.element("media:title") { xml.text self.title }
|
|
||||||
xml.element("media:thumbnail", url: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg",
|
|
||||||
width: "320", height: "180")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_xml(locale, _xml : Nil = nil)
|
|
||||||
XML.build do |xml|
|
|
||||||
to_xml(locale, xml)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_tuple
|
def to_tuple
|
||||||
{% begin %}
|
{% begin %}
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,57 +14,6 @@ struct SearchVideo
|
||||||
property premiere_timestamp : Time?
|
property premiere_timestamp : Time?
|
||||||
property author_verified : Bool
|
property author_verified : Bool
|
||||||
|
|
||||||
def to_xml(auto_generated, query_params, xml : XML::Builder)
|
|
||||||
query_params["v"] = self.id
|
|
||||||
|
|
||||||
xml.element("entry") do
|
|
||||||
xml.element("id") { xml.text "yt:video:#{self.id}" }
|
|
||||||
xml.element("yt:videoId") { xml.text self.id }
|
|
||||||
xml.element("yt:channelId") { xml.text self.ucid }
|
|
||||||
xml.element("title") { xml.text self.title }
|
|
||||||
xml.element("link", rel: "alternate", href: "#{HOST_URL}/watch?#{query_params}")
|
|
||||||
|
|
||||||
xml.element("author") do
|
|
||||||
if auto_generated
|
|
||||||
xml.element("name") { xml.text self.author }
|
|
||||||
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{self.ucid}" }
|
|
||||||
else
|
|
||||||
xml.element("name") { xml.text author }
|
|
||||||
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{ucid}" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
xml.element("content", type: "xhtml") do
|
|
||||||
xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
|
|
||||||
xml.element("a", href: "#{HOST_URL}/watch?#{query_params}") do
|
|
||||||
xml.element("img", src: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg")
|
|
||||||
end
|
|
||||||
|
|
||||||
xml.element("p", style: "word-break:break-word;white-space:pre-wrap") { xml.text html_to_content(self.description_html) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
xml.element("published") { xml.text self.published.to_s("%Y-%m-%dT%H:%M:%S%:z") }
|
|
||||||
|
|
||||||
xml.element("media:group") do
|
|
||||||
xml.element("media:title") { xml.text self.title }
|
|
||||||
xml.element("media:thumbnail", url: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg",
|
|
||||||
width: "320", height: "180")
|
|
||||||
xml.element("media:description") { xml.text html_to_content(self.description_html) }
|
|
||||||
end
|
|
||||||
|
|
||||||
xml.element("media:community") do
|
|
||||||
xml.element("media:statistics", views: self.views)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_xml(auto_generated, query_params, _xml : Nil)
|
|
||||||
XML.build do |xml|
|
|
||||||
to_xml(auto_generated, query_params, xml)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_json(locale : String?, json : JSON::Builder)
|
def to_json(locale : String?, json : JSON::Builder)
|
||||||
json.object do
|
json.object do
|
||||||
json.field "type", "video"
|
json.field "type", "video"
|
||||||
|
|
|
@ -11,41 +11,6 @@ struct PlaylistVideo
|
||||||
property index : Int64
|
property index : Int64
|
||||||
property live_now : Bool
|
property live_now : Bool
|
||||||
|
|
||||||
def to_xml(xml : XML::Builder)
|
|
||||||
xml.element("entry") do
|
|
||||||
xml.element("id") { xml.text "yt:video:#{self.id}" }
|
|
||||||
xml.element("yt:videoId") { xml.text self.id }
|
|
||||||
xml.element("yt:channelId") { xml.text self.ucid }
|
|
||||||
xml.element("title") { xml.text self.title }
|
|
||||||
xml.element("link", rel: "alternate", href: "#{HOST_URL}/watch?v=#{self.id}")
|
|
||||||
|
|
||||||
xml.element("author") do
|
|
||||||
xml.element("name") { xml.text self.author }
|
|
||||||
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{self.ucid}" }
|
|
||||||
end
|
|
||||||
|
|
||||||
xml.element("content", type: "xhtml") do
|
|
||||||
xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
|
|
||||||
xml.element("a", href: "#{HOST_URL}/watch?v=#{self.id}") do
|
|
||||||
xml.element("img", src: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
xml.element("published") { xml.text self.published.to_s("%Y-%m-%dT%H:%M:%S%:z") }
|
|
||||||
|
|
||||||
xml.element("media:group") do
|
|
||||||
xml.element("media:title") { xml.text self.title }
|
|
||||||
xml.element("media:thumbnail", url: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg",
|
|
||||||
width: "320", height: "180")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_xml(_xml : Nil = nil)
|
|
||||||
XML.build { |xml| to_xml(xml) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_json(json : JSON::Builder, index : Int32? = nil)
|
def to_json(json : JSON::Builder, index : Int32? = nil)
|
||||||
json.object do
|
json.object do
|
||||||
json.field "title", self.title
|
json.field "title", self.title
|
||||||
|
|
|
@ -146,8 +146,6 @@ module Invidious::Routes::Feeds
|
||||||
|
|
||||||
ucid = env.params.url["ucid"]
|
ucid = env.params.url["ucid"]
|
||||||
|
|
||||||
params = HTTP::Params.parse(env.params.query["params"]? || "")
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
channel = get_about_info(ucid, locale)
|
channel = get_about_info(ucid, locale)
|
||||||
rescue ex : ChannelRedirect
|
rescue ex : ChannelRedirect
|
||||||
|
@ -161,12 +159,13 @@ module Invidious::Routes::Feeds
|
||||||
response = YT_POOL.client &.get("/feeds/videos.xml?channel_id=#{channel.ucid}")
|
response = YT_POOL.client &.get("/feeds/videos.xml?channel_id=#{channel.ucid}")
|
||||||
rss = XML.parse_html(response.body)
|
rss = XML.parse_html(response.body)
|
||||||
|
|
||||||
|
updated = [] of String
|
||||||
videos = rss.xpath_nodes("//feed/entry").map do |entry|
|
videos = rss.xpath_nodes("//feed/entry").map do |entry|
|
||||||
video_id = entry.xpath_node("videoid").not_nil!.content
|
video_id = entry.xpath_node("videoid").not_nil!.content
|
||||||
title = entry.xpath_node("title").not_nil!.content
|
title = entry.xpath_node("title").not_nil!.content
|
||||||
|
|
||||||
published = Time.parse_rfc3339(entry.xpath_node("published").not_nil!.content)
|
published = Time.parse_rfc3339(entry.xpath_node("published").not_nil!.content)
|
||||||
updated = Time.parse_rfc3339(entry.xpath_node("updated").not_nil!.content)
|
updated << entry.xpath_node("updated").not_nil!.content
|
||||||
|
|
||||||
author = entry.xpath_node("author/name").not_nil!.content
|
author = entry.xpath_node("author/name").not_nil!.content
|
||||||
ucid = entry.xpath_node("channelid").not_nil!.content
|
ucid = entry.xpath_node("channelid").not_nil!.content
|
||||||
|
@ -190,33 +189,27 @@ module Invidious::Routes::Feeds
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
XML.build(indent: " ", encoding: "UTF-8") do |xml|
|
feed_created = rss.xpath_node("//feed/published").not_nil!.content
|
||||||
xml.element("feed", "xmlns:yt": "http://www.youtube.com/xml/schemas/2015",
|
feed_updated = updated.size > 0 ? updated[0] : feed_created
|
||||||
"xmlns:media": "http://search.yahoo.com/mrss/", xmlns: "http://www.w3.org/2005/Atom",
|
|
||||||
"xml:lang": "en-US") do
|
|
||||||
xml.element("link", rel: "self", href: "#{HOST_URL}#{env.request.resource}")
|
|
||||||
xml.element("id") { xml.text "yt:channel:#{channel.ucid}" }
|
|
||||||
xml.element("yt:channelId") { xml.text channel.ucid }
|
|
||||||
xml.element("icon") { xml.text channel.author_thumbnail }
|
|
||||||
xml.element("title") { xml.text channel.author }
|
|
||||||
xml.element("link", rel: "alternate", href: "#{HOST_URL}/channel/#{channel.ucid}")
|
|
||||||
|
|
||||||
xml.element("author") do
|
# Generate Atom feed
|
||||||
xml.element("name") { xml.text channel.author }
|
|
||||||
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{channel.ucid}" }
|
|
||||||
end
|
|
||||||
|
|
||||||
xml.element("image") do
|
properties = Invidious::RssAtom::AtomProperties.new(
|
||||||
xml.element("url") { xml.text channel.author_thumbnail }
|
title: "", # Use channel author name
|
||||||
xml.element("title") { xml.text channel.author }
|
icon_url: channel.author_thumbnail,
|
||||||
xml.element("link", rel: "self", href: "#{HOST_URL}#{env.request.resource}")
|
author: channel.author,
|
||||||
end
|
author_url: "#{HOST_URL}/channel/#{channel.ucid}",
|
||||||
|
date_updated: feed_updated,
|
||||||
|
date_published: feed_created,
|
||||||
|
alt_links: [{
|
||||||
|
type: "text/html",
|
||||||
|
url: "#{HOST_URL}/channel/#{channel.ucid}",
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
|
||||||
videos.each do |video|
|
return Invidious::RssAtom.atom_feed_builder(
|
||||||
video.to_xml(channel.auto_generated, params, xml)
|
env, videos, "channel/#{channel.ucid}", properties
|
||||||
end
|
)
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.rss_private(env)
|
def self.rss_private(env)
|
||||||
|
@ -247,20 +240,20 @@ module Invidious::Routes::Feeds
|
||||||
|
|
||||||
videos, notifications = get_subscription_feed(user, max_results, page)
|
videos, notifications = get_subscription_feed(user, max_results, page)
|
||||||
|
|
||||||
XML.build(indent: " ", encoding: "UTF-8") do |xml|
|
# Generate Atom feed
|
||||||
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",
|
|
||||||
"xml:lang": "en-US") do
|
|
||||||
xml.element("link", "type": "text/html", rel: "alternate", href: "#{HOST_URL}/feed/subscriptions")
|
|
||||||
xml.element("link", "type": "application/atom+xml", rel: "self",
|
|
||||||
href: "#{HOST_URL}#{env.request.resource}")
|
|
||||||
xml.element("title") { xml.text translate(locale, "Invidious Private Feed for `x`", user.email) }
|
|
||||||
|
|
||||||
(notifications + videos).each do |video|
|
properties = Invidious::RssAtom::AtomProperties.new(
|
||||||
video.to_xml(locale, params, xml)
|
title: translate(locale, "Invidious Private Feed for `x`", user.email),
|
||||||
end
|
author: user.email,
|
||||||
end
|
alt_links: [{
|
||||||
end
|
type: "text/html",
|
||||||
|
url: "#{HOST_URL}/feed/subscriptions",
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
|
||||||
|
return Invidious::RssAtom.atom_feed_builder(
|
||||||
|
env, (notifications + videos), "subscriptions/#{user.email}", properties
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.rss_playlist(env)
|
def self.rss_playlist(env)
|
||||||
|
@ -278,23 +271,20 @@ module Invidious::Routes::Feeds
|
||||||
if playlist = Invidious::Database::Playlists.select(id: plid)
|
if playlist = Invidious::Database::Playlists.select(id: plid)
|
||||||
videos = get_playlist_videos(playlist, offset: 0)
|
videos = get_playlist_videos(playlist, offset: 0)
|
||||||
|
|
||||||
return XML.build(indent: " ", encoding: "UTF-8") do |xml|
|
# Generate Atom feed
|
||||||
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",
|
|
||||||
"xml:lang": "en-US") do
|
|
||||||
xml.element("link", rel: "self", href: "#{HOST_URL}#{env.request.resource}")
|
|
||||||
xml.element("id") { xml.text "iv:playlist:#{plid}" }
|
|
||||||
xml.element("iv:playlistId") { xml.text plid }
|
|
||||||
xml.element("title") { xml.text playlist.title }
|
|
||||||
xml.element("link", rel: "alternate", href: "#{HOST_URL}/playlist?list=#{plid}")
|
|
||||||
|
|
||||||
xml.element("author") do
|
properties = Invidious::RssAtom::AtomProperties.new(
|
||||||
xml.element("name") { xml.text playlist.author }
|
title: playlist.title,
|
||||||
end
|
author: playlist.author,
|
||||||
|
alt_links: [{
|
||||||
|
type: "text/html",
|
||||||
|
url: "#{HOST_URL}/playlist?list=#{plid}",
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
|
||||||
videos.each &.to_xml(xml)
|
return Invidious::RssAtom.atom_feed_builder(
|
||||||
end
|
env, videos, "playlist/#{plid}", properties
|
||||||
end
|
)
|
||||||
else
|
else
|
||||||
haltf env, status_code: 404
|
haltf env, status_code: 404
|
||||||
end
|
end
|
||||||
|
|
169
src/invidious/rss_atom.cr
Normal file
169
src/invidious/rss_atom.cr
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
require "xml"
|
||||||
|
require "http/server"
|
||||||
|
|
||||||
|
module Invidious::RssAtom
|
||||||
|
extend self
|
||||||
|
|
||||||
|
# TODO: Merge all of those in a single type
|
||||||
|
alias AnyVideo = SearchVideo | ChannelVideo | PlaylistVideo
|
||||||
|
|
||||||
|
#
|
||||||
|
# Feed properties structure
|
||||||
|
#
|
||||||
|
|
||||||
|
alias AltLink = NamedTuple(type: String, url: String)
|
||||||
|
|
||||||
|
struct AtomProperties
|
||||||
|
getter title : String
|
||||||
|
getter icon_url : String
|
||||||
|
getter author : String
|
||||||
|
getter author_url : String
|
||||||
|
|
||||||
|
getter date_published : String
|
||||||
|
getter date_updated : String
|
||||||
|
|
||||||
|
getter alt_links : Array(AltLink)
|
||||||
|
|
||||||
|
def initialize(
|
||||||
|
*, # All parameters must be named
|
||||||
|
@title = "", @icon_url = "",
|
||||||
|
@author = "", @author_url = "",
|
||||||
|
date_updated : Time | String = Time.utc,
|
||||||
|
date_published : Time | String = "",
|
||||||
|
@alt_links = [] of AltLink
|
||||||
|
)
|
||||||
|
# Convert publication date if needed
|
||||||
|
if date_published.is_a?(Time)
|
||||||
|
@date_published = date_published.to_rfc3339
|
||||||
|
else
|
||||||
|
@date_published = date_published
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convert update date if needed
|
||||||
|
if date_updated.is_a?(Time)
|
||||||
|
@date_updated = date_updated.to_rfc3339
|
||||||
|
else
|
||||||
|
@date_updated = date_updated
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Atom Feed builder
|
||||||
|
#
|
||||||
|
|
||||||
|
def atom_feed_builder(
|
||||||
|
# Mandatory parameters
|
||||||
|
env : HTTP::Server::Context,
|
||||||
|
videos : Array(AnyVideo),
|
||||||
|
id : String,
|
||||||
|
properties : AtomProperties
|
||||||
|
)
|
||||||
|
return XML.build(indent: " ", encoding: "UTF-8") do |xml|
|
||||||
|
xml.element("feed",
|
||||||
|
xmlns: "http://www.w3.org/2005/Atom",
|
||||||
|
"xmlns:media": "http://search.yahoo.com/mrss/",
|
||||||
|
"xml:lang": "en-US"
|
||||||
|
) do
|
||||||
|
# The id must be unique, and an IANA-approved IRI, so use "ni://"
|
||||||
|
# Relevant RFC documents:
|
||||||
|
# - https://datatracker.ietf.org/doc/html/rfc4287#section-4.2.6
|
||||||
|
# - https://datatracker.ietf.org/doc/html/rfc6920
|
||||||
|
#
|
||||||
|
xml.element("id") { xml.text "ni://invidious/sha-256;" + sha256(id) }
|
||||||
|
|
||||||
|
# Feed title. Use author name if no title was provided
|
||||||
|
xml.element("title") do
|
||||||
|
xml.text(properties.title.empty? ? properties.author : properties.title)
|
||||||
|
end
|
||||||
|
|
||||||
|
if !properties.icon_url.empty?
|
||||||
|
icon_url = "#{HOST_URL}/gghpt/#{URI.parse(properties.icon_url).request_target}"
|
||||||
|
xml.element("icon") { xml.text icon_url }
|
||||||
|
xml.element("logo") { xml.text icon_url }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Feed creation (if available) and update (mandatory) dates
|
||||||
|
if !properties.date_published.empty?
|
||||||
|
xml.element("published") { xml.text properties.date_published }
|
||||||
|
end
|
||||||
|
|
||||||
|
xml.element("updated") { xml.text properties.date_updated }
|
||||||
|
|
||||||
|
# Links
|
||||||
|
xml.element("link", rel: "self",
|
||||||
|
type: "application/atom+xml",
|
||||||
|
href: "#{HOST_URL}#{env.request.resource}"
|
||||||
|
)
|
||||||
|
|
||||||
|
properties.alt_links.each do |link|
|
||||||
|
xml.element("link", rel: "alternate", type: link[:type], href: link[:url])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Author infos
|
||||||
|
xml.element("author") do
|
||||||
|
xml.element("name") { xml.text properties.author }
|
||||||
|
xml.element("uri") { xml.text properties.author_url } if !properties.author_url.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Video entries
|
||||||
|
videos.each do |video|
|
||||||
|
xml.element("entry") { atom_video(xml, video, env.params.query) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def atom_video(xml : XML::Builder, video : AnyVideo, query_params : HTTP::Params)
|
||||||
|
# URLs that are reused below
|
||||||
|
video_url = "#{HOST_URL}/watch?v=#{video.id}&#{query_params}"
|
||||||
|
video_thumb = "#{HOST_URL}/vi/#{video.id}/mqdefault.jpg"
|
||||||
|
|
||||||
|
description = video.is_a?(SearchVideo) ? video.description_html.gsub('\n', "<br/>\n") : ""
|
||||||
|
|
||||||
|
xml.element("id") { xml.text "ni://invidious/sha-256;" + sha256("video/#{video.id}") }
|
||||||
|
xml.element("title") { xml.text video.title }
|
||||||
|
xml.element("link", rel: "alternate", href: video_url)
|
||||||
|
|
||||||
|
xml.element("author") do
|
||||||
|
xml.element("name") { xml.text video.author }
|
||||||
|
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{video.ucid}" }
|
||||||
|
end
|
||||||
|
|
||||||
|
xml.element("content", type: "xhtml") do
|
||||||
|
xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
|
||||||
|
# Link to video
|
||||||
|
xml.element("a", href: video_url) do
|
||||||
|
xml.element("img", src: video_thumb)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Video sescription (SearchVideo only)
|
||||||
|
if video.is_a?(SearchVideo)
|
||||||
|
xml.element("p", style: "white-space:pre-wrap") { xml.text description }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Feed creation (if available) and update (ChannelVideo only) dates
|
||||||
|
xml.element("published") { xml.text video.published.to_rfc3339 }
|
||||||
|
xml.element("updated") { xml.text video.updated.to_rfc3339 } if video.is_a?(ChannelVideo)
|
||||||
|
|
||||||
|
# Media properties
|
||||||
|
xml.element("media:group") do
|
||||||
|
xml.element("media:title") { xml.text video.title }
|
||||||
|
xml.element("media:thumbnail", url: video_thumb, width: "320", height: "180")
|
||||||
|
|
||||||
|
# Video sescription (SearchVideo only)
|
||||||
|
if video.is_a?(SearchVideo)
|
||||||
|
xml.element("media:description") { xml.text description }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Views count (all except PlaylistVideo)
|
||||||
|
if !video.is_a?(PlaylistVideo)
|
||||||
|
xml.element("media:community") do
|
||||||
|
xml.element("media:statistics", views: video.views)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Add a link
Reference in a new issue