Compare commits

...

32 Commits

Author SHA1 Message Date
Samantaz Fox
2414e7db41
User: Add support for importing Youtube watch history (#4171) 2023-10-21 18:33:05 +02:00
Samantaz Fox
1a33012cad
CSS: Remove inline styling of the download widget (#4162) 2023-10-21 18:32:40 +02:00
Samantaz Fox
cf7c49deb0
Captions: Use 'fmt=vtt' instead of 'format=vtt' (#4152) 2023-10-21 18:32:28 +02:00
Samantaz Fox
d543a68a84
API: Add "authorVerified" to the video endpoint (#4150) 2023-10-21 18:32:01 +02:00
Samantaz Fox
2a65b5f52e
Frontend: Add video timestamp on external links (#4101) 2023-10-21 18:31:42 +02:00
Samantaz Fox
9072fa4355
CSS: Improve links contrast on dark theme (#4100) 2023-10-21 18:31:30 +02:00
Samantaz Fox
88cc62d45e
User: Fix importing FreeTube subscriptions with multiple profiles (#4011) 2023-10-21 18:31:18 +02:00
Samantaz Fox
40919c6a83
JS: Update external links exactly once per second 2023-10-21 13:45:15 +02:00
Ulysses Zhan
3b219a4c7f remove a debug statement 2023-10-20 13:45:16 -07:00
RadoslavL
cc703b0274 Removed commented lines 2023-10-17 10:02:08 +03:00
Ulysses Zhan
81a4f29c73 add 'Import YouTube watch history (.json)' entry to en-US.json 2023-10-16 21:46:41 -07:00
jt404
d7ea5609b2 move styles for download widget to default.css file 2023-10-17 01:00:14 +02:00
ChunkyProgrammer
0bd415158f Fix importing FreeTube subscriptions with multiple profiles
Co-Authored-By: Samantaz Fox <coding@samantaz.fr>
2023-10-16 00:32:24 -04:00
Ulysses Zhan
50977fb5d9 added translation importing watch history from youtube: zh-CN, zh-TW 2023-10-14 16:05:07 -07:00
Ulysses Zhan
a1a0e4c59f update readme about importing watch history 2023-10-14 15:56:04 -07:00
Ulysses Zhan
b9cbdce976 add: importing watch history from YouTube 2023-10-14 15:49:33 -07:00
jt404
8125ddca06 Replace inline styling for download widget with css file 2023-10-12 03:22:34 +02:00
RadoslavL
8e45e05fba
Get "author_verified" using the instance method instead of using the info hash.
Co-authored-by: Samantaz Fox <coding@samantaz.fr>
2023-10-09 09:20:35 +03:00
Samantaz Fox
0aebac5f3e
Captions: Use 'fmt=vtt' instead of 'format=vtt' 2023-10-08 12:40:49 +02:00
RadoslavL
7dc9b3f088 Fixed formatting 2023-10-07 20:29:48 +03:00
RadoslavL
ed2a44149e Added authorVerified to the video API v1 route 2023-10-07 17:55:42 +03:00
Samantaz Fox
47cc9dc169
JS: Fix missing domain in URL constructor 2023-09-27 23:03:24 +02:00
RadoslavL
ea781ceeee Removed unnecessary lines 2023-09-24 10:08:16 +03:00
RadoslavL
8542c974c8
Merge branch 'iv-org:master' into dark-mode-contrast 2023-09-22 11:01:38 +03:00
RadoslavL
e8c9b85ef5 Increased footer contrast 2023-09-19 09:15:44 +03:00
RadoslavL
270d606ad8 Changed the default dark theme as well 2023-09-18 08:14:34 +03:00
Samantaz Fox
700c57559b
JS: Replace '.setAttribute("href")' with '.href'
Co-authored-by: syeopite <70992037+syeopite@users.noreply.github.com>
2023-09-17 10:21:11 +00:00
Samantaz Fox
06b2a4ba9d
Frontend: Use 'timeupdate' event listener rather than setTimeout() 2023-09-16 00:41:46 +02:00
Samantaz Fox
58f4a012b7
Frontend: Add timestamp on switch invidious instance links 2023-09-14 22:28:07 +02:00
Samantaz Fox
2456b62936
Frontend: Add timestamp on invidious embed links 2023-09-14 22:27:36 +02:00
Samantaz Fox
792a999386
Frontend: Add timestamp on youtube+embed links 2023-09-14 22:26:36 +02:00
RadoslavL
beec62cf0e Increased link contrast in dark mode 2023-09-14 20:37:35 +03:00
14 changed files with 163 additions and 37 deletions

View File

@ -82,7 +82,7 @@
**Data import/export**
- Import subscriptions from YouTube, NewPipe and Freetube
- Import watch history from NewPipe
- Import watch history from YouTube and NewPipe
- Export subscriptions to NewPipe and Freetube
- Import/Export Invidious user data

View File

@ -441,16 +441,26 @@ p.video-data { margin: 0; font-weight: bold; font-size: 80%; }
*/
footer {
color: #919191;
margin-top: auto;
padding: 1.5em 0;
text-align: center;
max-height: 30vh;
}
footer a {
color: #919191 !important;
text-decoration: underline;
.light-theme footer {
color: #7c7c7c;
}
.dark-theme footer {
color: #adadad;
}
.light-theme footer a {
color: #7c7c7c !important;
}
.dark-theme footer a {
color: #adadad !important;
}
footer span {
@ -556,6 +566,14 @@ span > select {
color: #303030;
}
.no-theme footer {
color: #7c7c7c;
}
.no-theme footer a {
color: #7c7c7c !important;
}
.light-theme .pure-menu-heading {
color: #565d64;
}
@ -589,7 +607,7 @@ span > select {
}
.dark-theme a {
color: #a0a0a0;
color: #adadad;
text-decoration: none;
}
@ -643,7 +661,7 @@ body.dark-theme {
}
.no-theme a {
color: #a0a0a0;
color: #adadad;
text-decoration: none;
}
@ -674,6 +692,14 @@ body.dark-theme {
background-color: inherit;
color: inherit;
}
.no-theme footer {
color: #adadad;
}
.no-theme footer a {
color: #adadad !important;
}
}
@ -767,3 +793,7 @@ h1, h2, h3, h4, h5, p,
.channel-emoji {
margin: 0 2px;
}
#download_widget {
width: 100%;
}

View File

@ -98,11 +98,13 @@ if (video_data.params.quality === 'dash') {
/**
* Function for add time argument to url
*
* @param {String} url
* @param {String} [base]
* @returns {URL} urlWithTimeArg
*/
function addCurrentTimeToURL(url) {
var urlUsed = new URL(url);
function addCurrentTimeToURL(url, base) {
var urlUsed = new URL(url, base);
urlUsed.searchParams.delete('start');
var currentTime = Math.ceil(player.currentTime());
if (currentTime > 0)
@ -112,6 +114,50 @@ function addCurrentTimeToURL(url) {
return urlUsed;
}
/**
* Global variable to save the last timestamp (in full seconds) at which the external
* links were updated by the 'timeupdate' callback below.
*
* It is initialized to 5s so that the video will always restart from the beginning
* if the user hasn't really started watching before switching to the other website.
*/
var timeupdate_last_ts = 5;
/**
* Callback that updates the timestamp on all external links
*/
player.on('timeupdate', function () {
// Only update once every second
let current_ts = Math.floor(player.currentTime());
if (current_ts > timeupdate_last_ts) timeupdate_last_ts = current_ts;
else return;
// YouTube links
let elem_yt_watch = document.getElementById('link-yt-watch');
let elem_yt_embed = document.getElementById('link-yt-embed');
let base_url_yt_watch = elem_yt_watch.getAttribute('data-base-url');
let base_url_yt_embed = elem_yt_embed.getAttribute('data-base-url');
elem_yt_watch.href = addCurrentTimeToURL(base_url_yt_watch);
elem_yt_embed.href = addCurrentTimeToURL(base_url_yt_embed);
// Invidious links
let domain = window.location.origin;
let elem_iv_embed = document.getElementById('link-iv-embed');
let elem_iv_other = document.getElementById('link-iv-other');
let base_url_iv_embed = elem_iv_embed.getAttribute('data-base-url');
let base_url_iv_other = elem_iv_other.getAttribute('data-base-url');
elem_iv_embed.href = addCurrentTimeToURL(base_url_iv_embed, domain);
elem_iv_other.href = addCurrentTimeToURL(base_url_iv_other, domain);
});
var shareOptions = {
socials: ['fbFeed', 'tw', 'reddit', 'email'],

View File

@ -40,6 +40,7 @@
"Import Invidious data": "Import Invidious JSON data",
"Import YouTube subscriptions": "Import YouTube/OPML subscriptions",
"Import YouTube playlist (.csv)": "Import YouTube playlist (.csv)",
"Import YouTube watch history (.json)": "Import YouTube watch history (.json)",
"Import FreeTube subscriptions (.db)": "Import FreeTube subscriptions (.db)",
"Import NewPipe subscriptions (.json)": "Import NewPipe subscriptions (.json)",
"Import NewPipe data (.zip)": "Import NewPipe data (.zip)",

View File

@ -461,6 +461,7 @@
"Standard YouTube license": "标准 YouTube 许可证",
"Download is disabled": "已禁用下载",
"Import YouTube playlist (.csv)": "导入 YouTube 播放列表(.csv",
"Import YouTube watch history (.json)": "导入 YouTube 观看历史(.json",
"generic_button_cancel": "取消",
"playlist_button_add_items": "添加视频",
"generic_button_delete": "删除",

View File

@ -461,6 +461,7 @@
"Standard YouTube license": "標準 YouTube 授權條款",
"Download is disabled": "已停用下載",
"Import YouTube playlist (.csv)": "匯入 YouTube 播放清單 (.csv)",
"Import YouTube watch history (.json)": "匯入 YouTube 觀看歷史 (.json)",
"generic_button_cancel": "取消",
"generic_button_edit": "編輯",
"generic_button_save": "儲存",

View File

@ -42,8 +42,7 @@ module Invidious::Frontend::WatchPage
str << translate(locale, "Download as: ")
str << "</label>\n"
# TODO: remove inline style
str << "\t\t<select style=\"width:100%\" name='download_widget' id='download_widget'>\n"
str << "\t\t<select name='download_widget' id='download_widget'>\n"
# Non-DASH videos (audio+video)

View File

@ -39,6 +39,7 @@ module Invidious::JSONify::APIv1
json.field "author", video.author
json.field "authorId", video.ucid
json.field "authorUrl", "/channel/#{video.ucid}"
json.field "authorVerified", video.author_verified
json.field "authorThumbnails" do
json.array do

View File

@ -136,17 +136,17 @@ module Invidious::Routes::API::V1::Videos
end
end
else
# Some captions have "align:[start/end]" and "position:[num]%"
# attributes. Those are causing issues with VideoJS, which is unable
# to properly align the captions on the video, so we remove them.
#
# See: https://github.com/iv-org/invidious/issues/2391
webvtt = YT_POOL.client &.get("#{url}&format=vtt").body
webvtt = YT_POOL.client &.get("#{url}&fmt=vtt").body
if webvtt.starts_with?("<?xml")
webvtt = caption.timedtext_to_vtt(webvtt)
else
webvtt = YT_POOL.client &.get("#{url}&format=vtt").body
.gsub(/([0-9:.]{12} --> [0-9:.]{12}).+/, "\\1")
# Some captions have "align:[start/end]" and "position:[num]%"
# attributes. Those are causing issues with VideoJS, which is unable
# to properly align the captions on the video, so we remove them.
#
# See: https://github.com/iv-org/invidious/issues/2391
webvtt = webvtt.gsub(/([0-9:.]{12} --> [0-9:.]{12}).+/, "\\1")
end
end
end

View File

@ -319,6 +319,15 @@ module Invidious::Routes::PreferencesRoute
response: error_template(415, "Invalid playlist file uploaded")
)
end
when "import_youtube_wh"
filename = part.filename || ""
success = Invidious::User::Import.from_youtube_wh(user, body, filename, type)
if !success
haltf(env, status_code: 415,
response: error_template(415, "Invalid watch history file uploaded")
)
end
when "import_freetube"
Invidious::User::Import.from_freetube(user, body)
when "import_newpipe_subscriptions"

View File

@ -30,14 +30,6 @@ module Invidious::Routes::Watch
return env.redirect "/"
end
embed_link = "/embed/#{id}"
if env.params.query.size > 1
embed_params = HTTP::Params.parse(env.params.query.to_s)
embed_params.delete_all("v")
embed_link += "?"
embed_link += embed_params.to_s
end
plid = env.params.query["list"]?.try &.gsub(/[^a-zA-Z0-9_-]/, "")
continuation = process_continuation(env.params.query, plid, id)

View File

@ -218,6 +218,26 @@ struct Invidious::User
end
end
def from_youtube_wh(user : User, body : String, filename : String, type : String) : Bool
extension = filename.split(".").last
if extension == "json" || type == "application/json"
data = JSON.parse(body)
watched = data.as_a.compact_map do |item|
next unless url = item["titleUrl"]?
next unless match = url.as_s.match(/\?v=(?<video_id>[a-zA-Z0-9_-]+)$/)
match["video_id"]
end
watched.reverse! # YouTube have newest first
user.watched += watched
user.watched.uniq!
Invidious::Database::Users.update_watch_history(user)
return true
else
return false
end
end
# -------------------
# Freetube
# -------------------
@ -228,8 +248,12 @@ struct Invidious::User
subs = matches.map(&.["channel_id"])
if subs.empty?
data = JSON.parse(body)["subscriptions"]
subs = data.as_a.map(&.["id"].as_s)
profiles = body.split('\n', remove_empty: true)
profiles.each do |profile|
if data = JSON.parse(profile)["subscriptions"]?
subs += data.as_a.map(&.["id"].as_s)
end
end
end
user.subscriptions += subs

View File

@ -26,6 +26,11 @@
<input type="file" id="import_youtube_pl" name="import_youtube_pl">
</div>
<div class="pure-control-group">
<label for="import_youtube_wh"><%= translate(locale, "Import YouTube watch history (.json)") %></label>
<input type="file" id="import_youtube_wh" name="import_youtube_wh">
</div>
<div class="pure-control-group">
<label for="import_freetube"><%= translate(locale, "Import FreeTube subscriptions (.db)") %></label>
<input type="file" id="import_freetube" name="import_freetube">

View File

@ -113,19 +113,36 @@ we're going to need to do it here in order to allow for translations.
<div class="pure-u-1 pure-u-lg-1-5">
<div class="h-box">
<span id="watch-on-youtube">
<a href="https://www.youtube.com/watch?v=<%= video.id %>"><%= translate(locale, "videoinfo_watch_on_youTube") %></a>
(<a href="https://www.youtube.com/embed/<%= video.id %>"><%= translate(locale, "videoinfo_youTube_embed_link") %></a>)
<%-
link_yt_watch = URI.new(scheme: "https", host: "www.youtube.com", path: "/watch", query: "v=#{video.id}")
link_yt_embed = URI.new(scheme: "https", host: "www.youtube.com", path: "/embed/#{video.id}")
if !plid.nil? && !continuation.nil?
link_yt_param = URI::Params{"plid" => [plid], "index" => [continuation.to_s]}
link_yt_watch = IV::HttpServer::Utils.add_params_to_url(link_yt_watch, link_yt_param)
link_yt_embed = IV::HttpServer::Utils.add_params_to_url(link_yt_embed, link_yt_param)
end
-%>
<a id="link-yt-watch" data-base-url="<%= link_yt_watch %>" href="<%= link_yt_watch %>"><%= translate(locale, "videoinfo_watch_on_youTube") %></a>
(<a id="link-yt-embed" data-base-url="<%= link_yt_embed %>" href="<%= link_yt_embed %>"><%= translate(locale, "videoinfo_youTube_embed_link") %></a>)
</span>
<p id="watch-on-another-invidious-instance">
<% if env.get("preferences").as(Preferences).automatic_instance_redirect%>
<a href="/redirect?referer=<%= env.get?("current_page") %>"><%= translate(locale, "Switch Invidious Instance") %></a>
<% else %>
<a href="https://redirect.invidious.io<%= env.request.resource %>"><%= translate(locale, "Switch Invidious Instance") %></a>
<% end %>
<%- link_iv_other = IV::Frontend::Misc.redirect_url(env) -%>
<a id="link-iv-other" data-base-url="<%= link_iv_other %>" href="<%= link_iv_other %>"><%= translate(locale, "Switch Invidious Instance") %></a>
</p>
<p id="embed-link">
<a href="<%= embed_link %>"><%= translate(locale, "videoinfo_invidious_embed_link") %></a>
<%-
params_iv_embed = env.params.query.dup
params_iv_embed.delete_all("v")
link_iv_embed = URI.new(path: "/embed/#{id}")
link_iv_embed = IV::HttpServer::Utils.add_params_to_url(link_iv_embed, params_iv_embed)
-%>
<a id="link-iv-embed" data-base-url="<%= link_iv_embed %>" href="<%= link_iv_embed %>"><%= translate(locale, "videoinfo_invidious_embed_link") %></a>
</p>
<p id="annotations">
<% if params.annotations %>
<a href="/watch?<%= env.params.query %>&iv_load_policy=3">