forked from midou/invidious
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
81ea2bf799 | |||
ed3d9ce540 | |||
ef95dc2380 | |||
4875aa1d7e | |||
3ee7201f5d | |||
3c634d9f66 | |||
94d116974b | |||
5c87cf1547 | |||
1cfa1f6559 | |||
8b69e23471 |
30
CHANGELOG.md
30
CHANGELOG.md
@ -1,3 +1,22 @@
|
||||
# 0.10.0 (2018-10-16)
|
||||
|
||||
## Week 10: Subscriptions
|
||||
|
||||
This week I'm happy to announce that subscriptions have been drastically sped up with
|
||||
35e63fa. As I mentioned last week, this essentially "caches" a user's feed, meaning that operations that previously took 20 seconds or timed out, now can load in under a second. I'd take a look at [#173](https://github.com/omarroth/invidious/issues/173) for a sample benchmark. Previously features that made Invidious's feed so useful, such as filtering by unseen and by author would take too long to load, and so instead would timeout. I'm very happy that this has been fixed, and folks can get back to using these features.
|
||||
|
||||
Among some smaller features that have been added this week include [#118](https://github.com/omarroth/invidious/issues/118), which adds, in my opinion, some very attractive subscribe and unsubscribe buttons. I think it's also a bit of a functional improvement as well, since it doesn't require a user to reload the page in order to subscribe or unsubscribe to a channel, and also gives the opportunity to put the channel's sub count on display.
|
||||
|
||||
An option to swap between Reddit and YouTube comments without a page reload has been added with
|
||||
5eefab6, bringing it somewhat closer in functionality to the popular [AlienTube](https://github.com/xlexi/alientube) extension, on which it is based (although the extension unfortunately appears now to be fragmented).
|
||||
|
||||
As always, there are a couple smaller improvements this week, including some minor fixes for geo-bypass with
|
||||
e46e618 and [`245d0b5`](https://github.com/omarroth/invidious/245d0b5), playlist preferences with [`81b4477`](https://github.com/omarroth/invidious/81b4477), and YouTube comments with [`02335f3`](https://github.com/omarroth/invidious/02335f3).
|
||||
|
||||
This coming week I'd also recommend keeping an eye on the excellent [FreeTube](https://github.com/FreeTubeApp/FreeTube), which is looking forward to a new release. I've been very lucky to work with [**@PrestonN**](https://github.com/PrestonN) for the past few weeks to improve the Invidious API, and I'm quite looking forward to the new release.
|
||||
|
||||
That's all for this week folks, thank you all again for your continued interest and support.
|
||||
|
||||
# 0.9.0 (2018-10-08)
|
||||
|
||||
## Week 9: Playlists
|
||||
@ -43,6 +62,7 @@ A [CHANGELOG](https://github.com/omarroth/invidious/blob/master/CHANGELOG.md) ha
|
||||
Recently, users have been reporting 504s when attempting to access their subscriptions, which is tracked in [#173](https://github.com/omarroth/invidious/issues/173). This is most likely caused by an uptick in usage, which I am absolutely grateful for, but unfortunately has resulted in an increase in costs for hosting the site, which is why I will be bumping my goal on Patreon from $60 to $80. I would appreciate any feedback on how subscriptions could be improved.
|
||||
|
||||
Other minor improvements include:
|
||||
|
||||
- Additional regions added to bypass geo-block with [`9a78523`](https://github.com/omarroth/invidious/9a78523)
|
||||
- Fix for playlists containing less than 100 videos (previously shown as empty) with [`35ac887`](https://github.com/omarroth/invidious/35ac887)
|
||||
- Fix for `published` date for Reddit comments (previously showing negative seconds) with [`6e09202`](https://github.com/omarroth/invidious/6e09202)
|
||||
@ -55,7 +75,7 @@ Thank you everyone for your support!
|
||||
|
||||
Hello again! This week I'm happy to mention a couple new features to search as well as some miscellaneous usability improvements.
|
||||
|
||||
You can now constrain your search query to a specific channel with the `channel:CHANNEL` filter (see [#165](https://github.com/omarroth/invidious/issues/165) for more details). Unfortunately, other search filters combined with channel search are not yet supported. I hope to add support for them in the coming weeks.
|
||||
You can now constrain your search query to a specific channel with the `channel:CHANNEL` filter (see [#165](https://github.com/omarroth/invidious/issues/165) for more details). Unfortunately, other search filters combined with channel search are not yet supported. I hope to add support for them in the coming weeks.
|
||||
|
||||
You can also now search only your subscriptions by adding `subscriptions:true` to your query (see [#30](https://github.com/omarroth/invidious/issues/30) for more details). It's not quite ready for widespread use but I would appreciate feedback as the site updates to fully support it. Other search filters are not yet supported with `subscriptions:true`, but I hope to add more functionality to this as well.
|
||||
|
||||
@ -81,7 +101,7 @@ A potential XSS vector has also been fixed in YouTube comments with [`8c45694`](
|
||||
|
||||
All the above vulnerabilities were brought to my attention by someone who wishes to remain anonymous, but I would like to say again here how thankful I am. If anyone else would like to get in touch please feel free to email me at omarroth@hotmail.com or omarroth@protonmail.com.
|
||||
|
||||
This week a couple changes have been made to better protect user's privacy as well.
|
||||
This week a couple changes have been made to better protect user's privacy as well.
|
||||
All CSS and JS assets are now served locally with [`3ec684a`](https://github.com/omarroth/invidious/3ec684a), which means users no longer need to whitelist unpkg.com. Although I personally have encountered few issues, I understand that many folks would like to keep their browsing activity contained to as few parties as possible. In the coming week I also hope to proxy YouTube images, so that no user data is sent to Google.
|
||||
|
||||
YouTube links in comments now should redirect properly to the Invidious alternate with [`1c8bd67`](https://github.com/omarroth/invidious/1c8bd67) and [`cf63c82`](https://github.com/omarroth/invidious/cf63c82), so users can more easily evade Google tracking.
|
||||
@ -131,7 +151,7 @@ I'd also like to announce that I've set up an account on [Liberapay](https://lib
|
||||
|
||||
[Two weeks ago](https://github.com/omarroth/invidious/releases/tag/0.1.0) I mentioned adding 1080p support into the player. Currently, the only thing blocking is [#207](https://github.com/videojs/http-streaming/pull/207) in the excellent [http-streaming](https://github.com/videojs/http-streaming) library. I hope to work with the videojs team to merge it soon and finally implement 1080p support!
|
||||
|
||||
That's all for this week, thank you again everyone for your support!
|
||||
That's all for this week, thank you again everyone for your support!
|
||||
|
||||
# 0.2.0 (2018-09-06)
|
||||
|
||||
@ -146,9 +166,9 @@ A couple of miscellaneous features and bugfixes:
|
||||
|
||||
- You can now login to Invidious simultaneously from multiple devices - [#109](https://github.com/omarroth/invidious/issues/109)
|
||||
|
||||
- Added a note for scheduled livestreams - [#124](https://github.com/omarroth/invidious/issues/124)
|
||||
- Added a note for scheduled livestreams - [#124](https://github.com/omarroth/invidious/issues/124)
|
||||
|
||||
- Changed YouTube comment header to "View x comments" - [#120](https://github.com/omarroth/invidious/issues/120)
|
||||
- Changed YouTube comment header to "View x comments" - [#120](https://github.com/omarroth/invidious/issues/120)
|
||||
|
||||
Enjoy your week everyone!
|
||||
|
||||
|
@ -22,8 +22,37 @@ div {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.pure-button-primary {
|
||||
background: rgba(0, 182, 240, 1);
|
||||
a.pure-button-primary {
|
||||
background-color: #a0a0a0;
|
||||
color: rgba(35, 35, 35, 1);
|
||||
}
|
||||
|
||||
a.pure-button-primary:hover {
|
||||
background-color: rgba(0, 182, 240, 1);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
div.thumbnail {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
img.thumbnail {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.length {
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
background-color: rgba(35, 35, 35, 0.75);
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
padding: 2px;
|
||||
font-size: 16px;
|
||||
font-family: sans-serif;
|
||||
right: 0.5em;
|
||||
bottom: -0.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -28,13 +28,6 @@ function swap_comments(source) {
|
||||
}
|
||||
}
|
||||
|
||||
function commaSeparateNumber(val) {
|
||||
while (/(\d+)(\d{3})/.test(val.toString())) {
|
||||
val = val.toString().replace(/(\d+)(\d{3})/, "$1" + "," + "$2");
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
String.prototype.supplant = function(o) {
|
||||
return this.replace(/{([^{}]*)}/g, function(a, b) {
|
||||
var r = o[b];
|
||||
|
@ -1,5 +1,5 @@
|
||||
name: invidious
|
||||
version: 0.9.0
|
||||
version: 0.10.0
|
||||
|
||||
authors:
|
||||
- Omar Roth <omarroth@hotmail.com>
|
||||
|
@ -2245,6 +2245,8 @@ get "/api/v1/videos/:id" do |env|
|
||||
json.field "likeCount", video.likes
|
||||
json.field "dislikeCount", video.dislikes
|
||||
|
||||
json.field "paid", video.paid
|
||||
json.field "premium", video.premium
|
||||
json.field "isFamilyFriendly", video.is_family_friendly
|
||||
json.field "allowedRegions", video.allowed_regions
|
||||
json.field "genre", video.genre
|
||||
@ -2602,6 +2604,8 @@ get "/api/v1/channels/:ucid" do |env|
|
||||
json.field "published", video.published.epoch
|
||||
json.field "publishedText", "#{recode_date(video.published)} ago"
|
||||
json.field "lengthSeconds", video.length_seconds
|
||||
json.field "paid", video.paid
|
||||
json.field "premium", video.premium
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -2657,6 +2661,8 @@ end
|
||||
json.field "published", video.published.epoch
|
||||
json.field "publishedText", "#{recode_date(video.published)} ago"
|
||||
json.field "lengthSeconds", video.length_seconds
|
||||
json.field "paid", video.paid
|
||||
json.field "premium", video.premium
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -2704,6 +2710,8 @@ get "/api/v1/channels/search/:ucid" do |env|
|
||||
json.field "publishedText", "#{recode_date(item.published)} ago"
|
||||
json.field "lengthSeconds", item.length_seconds
|
||||
json.field "liveNow", item.live_now
|
||||
json.field "paid", item.paid
|
||||
json.field "premium", item.premium
|
||||
when SearchPlaylist
|
||||
json.field "type", "playlist"
|
||||
json.field "title", item.title
|
||||
@ -2825,6 +2833,8 @@ get "/api/v1/search" do |env|
|
||||
json.field "publishedText", "#{recode_date(item.published)} ago"
|
||||
json.field "lengthSeconds", item.length_seconds
|
||||
json.field "liveNow", item.live_now
|
||||
json.field "paid", item.paid
|
||||
json.field "premium", item.premium
|
||||
when SearchPlaylist
|
||||
json.field "type", "playlist"
|
||||
json.field "title", item.title
|
||||
|
@ -15,6 +15,11 @@ class ChannelVideo
|
||||
ucid: String,
|
||||
author: String,
|
||||
})
|
||||
|
||||
# TODO: Add length_seconds to channel_video
|
||||
def length_seconds
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
def get_channel(id, client, db, refresh = true, pull_all_videos = true)
|
||||
@ -184,15 +189,15 @@ end
|
||||
def get_about_info(ucid)
|
||||
client = make_client(YT_URL)
|
||||
|
||||
about = client.get("/user/#{ucid}/about?disable_polymer=1")
|
||||
about = client.get("/user/#{ucid}/about?disable_polymer=1&gl=US&hl=en")
|
||||
about = XML.parse_html(about.body)
|
||||
|
||||
if !about.xpath_node(%q(//span[@class="qualified-channel-title-text"]/a))
|
||||
about = client.get("/channel/#{ucid}/about?disable_polymer=1")
|
||||
if !about.xpath_node(%q(//span[contains(@class,"qualified-channel-title-text")]/a))
|
||||
about = client.get("/channel/#{ucid}/about?disable_polymer=1&gl=US&hl=en")
|
||||
about = XML.parse_html(about.body)
|
||||
end
|
||||
|
||||
if !about.xpath_node(%q(//span[@class="qualified-channel-title-text"]/a))
|
||||
if !about.xpath_node(%q(//span[contains(@class,"qualified-channel-title-text")]/a))
|
||||
raise "User does not exist."
|
||||
end
|
||||
|
||||
@ -202,7 +207,7 @@ def get_about_info(ucid)
|
||||
end
|
||||
sub_count ||= 0
|
||||
|
||||
author = about.xpath_node(%q(//span[@class="qualified-channel-title-text"]/a)).not_nil!.content
|
||||
author = about.xpath_node(%q(//span[contains(@class,"qualified-channel-title-text")]/a)).not_nil!.content
|
||||
ucid = about.xpath_node(%q(//link[@rel="canonical"])).not_nil!["href"].split("/")[-1]
|
||||
|
||||
# Auto-generated channels
|
||||
|
@ -115,7 +115,7 @@ def template_youtube_comments(comments)
|
||||
<p style="white-space:pre-wrap">#{child["contentHtml"]}</p>
|
||||
#{recode_date(Time.epoch(child["published"].as_i64))} ago
|
||||
|
|
||||
<i class="icon ion-ios-thumbs-up"></i> #{child["likeCount"]}
|
||||
<i class="icon ion-ios-thumbs-up"></i> #{number_with_separator(child["likeCount"])}
|
||||
</p>
|
||||
#{replies_html}
|
||||
</div>
|
||||
@ -129,7 +129,7 @@ def template_youtube_comments(comments)
|
||||
<div class="pure-u-1">
|
||||
<p>
|
||||
<a href="javascript:void(0)" data-continuation="#{comments["continuation"]}"
|
||||
onclick="get_youtube_replies(this)">Load more</a>
|
||||
onclick="get_youtube_replies(this, true)">Load more</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -158,7 +158,7 @@ def template_reddit_comments(root)
|
||||
<p>
|
||||
<a href="javascript:void(0)" onclick="toggle_parent(this)">[ - ]</a>
|
||||
<b><a href="https://www.reddit.com/user/#{author}">#{author}</a></b>
|
||||
#{score} points
|
||||
#{number_with_separator(score)} points
|
||||
#{recode_date(child.created_utc)} ago
|
||||
</p>
|
||||
<div>
|
||||
|
@ -358,6 +358,18 @@ def extract_items(nodeset, ucid = nil)
|
||||
live_now = false
|
||||
end
|
||||
|
||||
if node.xpath_node(%q(.//span[text()="Premium"]))
|
||||
premium = true
|
||||
else
|
||||
premium = false
|
||||
end
|
||||
|
||||
if node.xpath_node(%q(.//span[contains(text(), "Get YouTube Premium")]))
|
||||
paid = true
|
||||
else
|
||||
paid = false
|
||||
end
|
||||
|
||||
items << SearchVideo.new(
|
||||
title,
|
||||
id,
|
||||
@ -368,7 +380,9 @@ def extract_items(nodeset, ucid = nil)
|
||||
description,
|
||||
description_html,
|
||||
length_seconds,
|
||||
live_now
|
||||
live_now,
|
||||
paid,
|
||||
premium
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -40,6 +40,23 @@ def decode_length_seconds(string)
|
||||
return length_seconds
|
||||
end
|
||||
|
||||
def recode_length_seconds(time)
|
||||
if time <= 0
|
||||
return ""
|
||||
else
|
||||
time = time.seconds
|
||||
text = "#{time.minutes.to_s.rjust(2, '0')}:#{time.seconds.to_s.rjust(2, '0')}"
|
||||
|
||||
if time.hours > 0
|
||||
text = "#{time.hours.to_s.rjust(2, '0')}:#{text}"
|
||||
end
|
||||
|
||||
text = text.lchop('0')
|
||||
|
||||
return text
|
||||
end
|
||||
end
|
||||
|
||||
def decode_time(string)
|
||||
time = string.try &.to_f?
|
||||
|
||||
@ -138,6 +155,25 @@ def number_with_separator(number)
|
||||
number.to_s.reverse.gsub(/(\d{3})(?=\d)/, "\\1,").reverse
|
||||
end
|
||||
|
||||
def number_to_short_text(number)
|
||||
seperated = number_with_separator(number).gsub(",", ".").split("")
|
||||
text = seperated.first(2).join
|
||||
|
||||
if seperated[2]? && seperated[2] != "."
|
||||
text += seperated[2]
|
||||
end
|
||||
|
||||
text = text.rchop(".0")
|
||||
|
||||
if number / 1000000 != 0
|
||||
text += "M"
|
||||
elsif number / 1000 != 0
|
||||
text += "K"
|
||||
end
|
||||
|
||||
text
|
||||
end
|
||||
|
||||
def arg_array(array, start = 1)
|
||||
if array.size == 0
|
||||
args = "NULL"
|
||||
|
@ -10,6 +10,8 @@ class SearchVideo
|
||||
description_html: String,
|
||||
length_seconds: Int32,
|
||||
live_now: Bool,
|
||||
paid: Bool,
|
||||
premium: Bool,
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -407,6 +407,23 @@ class Video
|
||||
return @player_json.not_nil!
|
||||
end
|
||||
|
||||
def paid
|
||||
reason = self.player_response["playabilityStatus"]?.try &.["reason"]?
|
||||
|
||||
if reason == "This video requires payment to watch."
|
||||
paid = true
|
||||
else
|
||||
paid = false
|
||||
end
|
||||
|
||||
return paid
|
||||
end
|
||||
|
||||
def premium
|
||||
premium = self.player_response.to_s.includes? "Get YouTube without the ads."
|
||||
return premium
|
||||
end
|
||||
|
||||
def captions
|
||||
captions = [] of Caption
|
||||
if player_response["captions"]?
|
||||
@ -434,6 +451,10 @@ class Video
|
||||
return description
|
||||
end
|
||||
|
||||
def length_seconds
|
||||
return self.info["length_seconds"].to_i
|
||||
end
|
||||
|
||||
add_mapping({
|
||||
id: String,
|
||||
info: {
|
||||
|
@ -19,14 +19,14 @@
|
||||
<p>
|
||||
<a id="subscribe" onclick="unsubscribe()" class="pure-button pure-button-primary"
|
||||
href="/subscription_ajax?action_remove_subscriptions=1&c=<%= ucid %>&referer=<%= env.get("current_page") %>">
|
||||
<b>Unsubscribe from <%= author %> <%= number_with_separator(sub_count) %></b>
|
||||
<b>Unsubscribe | <%= number_to_short_text(sub_count) %></b>
|
||||
</a>
|
||||
</p>
|
||||
<% else %>
|
||||
<p>
|
||||
<a id="subscribe" onclick="subscribe()" class="pure-button pure-button-primary"
|
||||
href="/subscription_ajax?action_create_subscription_to_channel=1&c=<%= ucid %>&referer=<%= env.get("current_page") %>">
|
||||
<b>Subscribe to <%= author %> <%= number_with_separator(sub_count) %></b>
|
||||
<b>Subscribe | <%= number_to_short_text(sub_count) %></b>
|
||||
</a>
|
||||
</p>
|
||||
<% end %>
|
||||
@ -82,7 +82,7 @@ function subscribe() {
|
||||
if (xhr.status == 200) {
|
||||
subscribe_button = document.getElementById("subscribe");
|
||||
subscribe_button.onclick = unsubscribe;
|
||||
subscribe_button.innerHTML = '<b>Unsubscribe from <%= author %> <%= number_with_separator(sub_count + 1) %></b>'
|
||||
subscribe_button.innerHTML = '<b>Unsubscribe | <%= number_to_short_text(sub_count) %></b>'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -101,7 +101,7 @@ function unsubscribe() {
|
||||
if (xhr.status == 200) {
|
||||
subscribe_button = document.getElementById("subscribe");
|
||||
subscribe_button.onclick = subscribe;
|
||||
subscribe_button.innerHTML = '<b>Subscribe to <%= author %> <%= number_with_separator(sub_count) %></b>'
|
||||
subscribe_button.innerHTML = '<b>Subscribe | <%= number_to_short_text(sub_count) %></b>'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,10 @@
|
||||
<a style="width:100%;" href="<%= url %>">
|
||||
<% if env.get?("user") && env.get("user").as(User).preferences.thin_mode %>
|
||||
<% else %>
|
||||
<img style="width:100%;" src="/vi/<%= item.videos[0]?.try &.id %>/mqdefault.jpg"/>
|
||||
<div class="thumbnail">
|
||||
<img class="thumbnail" src="/vi/<%= item.videos[0]?.try &.id %>/mqdefault.jpg"/>
|
||||
<p class="length"><%= recode_length_seconds(item.videos[0]?.try &.length_seconds || 0) %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
<p><%= item.title %></p>
|
||||
</a>
|
||||
@ -35,23 +38,45 @@
|
||||
<a style="width:100%;" href="/watch?v=<%= item.id %>&list=<%= item.mixes[0] %>">
|
||||
<% if env.get?("user") && env.get("user").as(User).preferences.thin_mode %>
|
||||
<% else %>
|
||||
<img style="width:100%;" src="/vi/<%= item.id %>/mqdefault.jpg"/>
|
||||
<div class="thumbnail">
|
||||
<img class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/>
|
||||
<p class="length"><%= recode_length_seconds(item.length_seconds) %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
<p><%= item.title %></p>
|
||||
</a>
|
||||
<p>
|
||||
<b><a style="width:100%;" href="/channel/<%= item.ucid %>"><%= item.author %></a></b>
|
||||
</p>
|
||||
<% else %>
|
||||
<% if item.responds_to?(:playlists) && !item.playlists.empty? %>
|
||||
<% params = "&list=#{item.playlists[0]}" %>
|
||||
<% else %>
|
||||
<% params = nil %>
|
||||
<% end %>
|
||||
<a style="width:100%;" href="/watch?v=<%= item.id %><%= params %>">
|
||||
<% when PlaylistVideo %>
|
||||
<a style="width:100%;" href="/watch?v=<%= item.id %>&list=<%= item.playlists[0] %>">
|
||||
<% if env.get?("user") && env.get("user").as(User).preferences.thin_mode %>
|
||||
<% else %>
|
||||
<img style="width:100%;" src="/vi/<%= item.id %>/mqdefault.jpg"/>
|
||||
<div class="thumbnail">
|
||||
<img class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/>
|
||||
<p class="length"><%= recode_length_seconds(item.length_seconds) %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
<p><%= item.title %></p>
|
||||
</a>
|
||||
<% if item.responds_to?(:live_now) && item.live_now %>
|
||||
<p>LIVE</p>
|
||||
<% end %>
|
||||
<p>
|
||||
<b><a style="width:100%;" href="/channel/<%= item.ucid %>"><%= item.author %></a></b>
|
||||
</p>
|
||||
|
||||
<% if Time.now - item.published > 1.minute %>
|
||||
<h5>Shared <%= recode_date(item.published) %> ago</h5>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<a style="width:100%;" href="/watch?v=<%= item.id %>">
|
||||
<% if env.get?("user") && env.get("user").as(User).preferences.thin_mode %>
|
||||
<% else %>
|
||||
<div class="thumbnail">
|
||||
<img class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/>
|
||||
<p class="length"><%= recode_length_seconds(item.length_seconds) %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
<p><%= item.title %></p>
|
||||
</a>
|
||||
|
@ -94,14 +94,14 @@
|
||||
<p>
|
||||
<a id="subscribe" onclick="unsubscribe()" class="pure-button pure-button-primary"
|
||||
href="/subscription_ajax?action_remove_subscriptions=1&c=<%= video.ucid %>&referer=<%= env.get("current_page") %>">
|
||||
<b>Unsubscribe from <%= video.author %> <%= video.sub_count_text %></b>
|
||||
<b>Unsubscribe | <%= video.sub_count_text %></b>
|
||||
</a>
|
||||
</p>
|
||||
<% else %>
|
||||
<p>
|
||||
<a id="subscribe" onclick="subscribe()" class="pure-button pure-button-primary"
|
||||
href="/subscription_ajax?action_create_subscription_to_channel=1&c=<%= video.ucid %>&referer=<%= env.get("current_page") %>">
|
||||
<b>Subscribe to <%= video.author %> <%= video.sub_count_text %></b>
|
||||
<b>Subscribe | <%= video.sub_count_text %></b>
|
||||
</a>
|
||||
</p>
|
||||
<% end %>
|
||||
@ -137,7 +137,10 @@
|
||||
<a href="/watch?v=<%= rv["id"] %>">
|
||||
<% if preferences && preferences.thin_mode %>
|
||||
<% else %>
|
||||
<img style="width:100%;" src="/vi/<%= rv["id"] %>/mqdefault.jpg">
|
||||
<div class="thumbnail">
|
||||
<img class="thumbnail" src="/vi/<%= rv["id"] %>/mqdefault.jpg">
|
||||
<p class="length"><%= recode_length_seconds(rv["length_seconds"]?.try &.to_i? || 0) %></p>
|
||||
</div>
|
||||
<% end %>
|
||||
<p style="width:100%"><%= rv["title"] %></p>
|
||||
<p>
|
||||
@ -152,6 +155,13 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function number_with_separator(val) {
|
||||
while (/(\d+)(\d{3})/.test(val.toString())) {
|
||||
val = val.toString().replace(/(\d+)(\d{3})/, "$1" + "," + "$2");
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
subscribe_button = document.getElementById("subscribe");
|
||||
if (subscribe_button.getAttribute('onclick')) {
|
||||
subscribe_button["href"] = "javascript:void(0);";
|
||||
@ -170,7 +180,7 @@ function subscribe() {
|
||||
if (xhr.status == 200) {
|
||||
subscribe_button = document.getElementById("subscribe");
|
||||
subscribe_button.onclick = unsubscribe;
|
||||
subscribe_button.innerHTML = '<b>Unsubscribe from <%= video.author %> <%= video.sub_count_text %></b>'
|
||||
subscribe_button.innerHTML = '<b>Unsubscribe | <%= video.sub_count_text %></b>'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -189,7 +199,7 @@ function unsubscribe() {
|
||||
if (xhr.status == 200) {
|
||||
subscribe_button = document.getElementById("subscribe");
|
||||
subscribe_button.onclick = subscribe;
|
||||
subscribe_button.innerHTML = '<b>Subscribe to <%= video.author %> <%= video.sub_count_text %></b>'
|
||||
subscribe_button.innerHTML = '<b>Subscribe | <%= video.sub_count_text %></b>'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -345,7 +355,7 @@ function get_youtube_comments() {
|
||||
<div>{contentHtml}</div> \
|
||||
<hr>'.supplant({
|
||||
contentHtml: xhr.response.contentHtml,
|
||||
commentCount: commaSeparateNumber(xhr.response.commentCount)
|
||||
commentCount: number_with_separator(xhr.response.commentCount)
|
||||
});
|
||||
} else {
|
||||
comments.innerHTML = "";
|
||||
@ -369,7 +379,7 @@ function get_youtube_comments() {
|
||||
};
|
||||
}
|
||||
|
||||
function get_youtube_replies(target) {
|
||||
function get_youtube_replies(target, load_more) {
|
||||
var continuation = target.getAttribute('data-continuation');
|
||||
|
||||
var body = target.parentNode.parentNode;
|
||||
@ -388,13 +398,19 @@ function get_youtube_replies(target) {
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200) {
|
||||
body.innerHTML = ' \
|
||||
<p><a href="javascript:void(0)" \
|
||||
onclick="hide_youtube_replies(this)">Hide replies \
|
||||
</a></p> \
|
||||
<div>{contentHtml}</div>'.supplant({
|
||||
contentHtml: xhr.response.contentHtml,
|
||||
});
|
||||
if (load_more) {
|
||||
body = body.parentNode.parentNode;
|
||||
body.removeChild(body.lastElementChild);
|
||||
body.innerHTML += xhr.response.contentHtml;
|
||||
} else {
|
||||
body.innerHTML = ' \
|
||||
<p><a href="javascript:void(0)" \
|
||||
onclick="hide_youtube_replies(this)">Hide replies \
|
||||
</a></p> \
|
||||
<div>{contentHtml}</div>'.supplant({
|
||||
contentHtml: xhr.response.contentHtml,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
body.innerHTML = fallback;
|
||||
}
|
||||
|
Reference in New Issue
Block a user