diff options
| -rw-r--r-- | CHANGELOG.md | 62 | ||||
| -rw-r--r-- | assets/css/darktheme.css | 4 | ||||
| -rw-r--r-- | assets/css/default.css | 4 | ||||
| -rw-r--r-- | shard.yml | 2 | ||||
| -rw-r--r-- | src/invidious.cr | 57 | ||||
| -rw-r--r-- | src/invidious/channels.cr | 4 | ||||
| -rw-r--r-- | src/invidious/helpers/utils.cr | 17 | ||||
| -rw-r--r-- | src/invidious/jobs.cr | 10 | ||||
| -rw-r--r-- | src/invidious/views/components/player.ecr | 5 | ||||
| -rw-r--r-- | src/invidious/views/playlist.ecr | 1 | ||||
| -rw-r--r-- | src/invidious/views/subscription_manager.ecr | 10 |
11 files changed, 128 insertions, 48 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 46de1263..6030f01f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,65 @@ +# 0.15.0 (2019-03-06)
+
+## Version 0.15.0: Preferences and Channel Playlists
+
+The project has seen quite a bit of activity this past month. Large focus has been on fixing bugs, but there's still quite a few new features I'm happy to announce. There have been [133 commits](https://github.com/omarroth/invidious/compare/0.14.0...0.15.0) from 15 contributors this past month.
+
+As a couple miscellaneous changes, a couple [nice screenshots](https://github.com/omarroth/invidious#screenshots) have been added to the README, so folks can see more of what the site has to offer without creating an account.
+
+The footer has also been cleaned up quite a bit, and now displays the current version, so it's easier to know what features are available from the current instance.
+
+## For Administrators
+
+This past month there has been a minor release - `0.14.1` - which fixes a breaking change made by YouTube for their polymer redesign.
+
+There have been several new features that unfortunately require a database migration. There are migration scripts provided in `config/migrate-scripts`, and the [wiki](https://github.com/omarroth/invidious/wiki/Updating) has instructions for automatically applying them. I'll do my best to keep those changes to a minimum, and expect to see a corresponding script to automatically apply any new changes.
+
+Administrator preferences have been added with [#312](https://github.com/omarroth/invidious/issues/312), which allows administrators to customize their instance. Administrators can change the order of feed menus, change the default homepage, disable open registration, and several other options. There's a short 'how-to' [here](https://github.com/omarroth/invidious/issues/312#issuecomment-468831842), and the new options are documented [here](https://github.com/omarroth/invidious/wiki/Configuration).
+
+An `/api/v1/stats` endpoint has been added with [#356](https://github.com/omarroth/invidious/issues/356), which reports the instance version and number of active users. Statistics are disabled by default, and can be enabled in administator preferences. Statistics for the official instance are available [here](https://invidio.us/api/v1/stats?pretty=1).
+
+## For Developers
+
+`/api/v1/channels/:ucid` now provides an `autoGenerated` tag, which returns true for [topic channels](https://www.youtube.com/channel/UCE80FOXpJydkkMo-BYoJdEg), and larger [genre channels](https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ) generated by YouTube. These channels don't have any videos of their own, so `latestVideos` will be empty. It is recommended instead to display a list of playlists generated by YouTube.
+
+You can now pull a list of playlists from a channel with `/api/v1/channels/playlists/:ucid`. Supported options are documented in the [wiki](https://github.com/omarroth/invidious/wiki/API#get-apiv1channelsplaylistsucid-apiv1channelsucidplaylists). Pagination is handled with a `continuation` token, which is generated on each call. Of note is that auto-generated channels currently have one page of results, and subsequent calls will be empty.
+
+For quickly pulling the latest 30 videos from a channel, there is now `/api/v1/channels/latest/:ucid`. It is much faster than a call to `/api/v1/channels/:ucid`. It will not convert an author name to a valid ucid automatically, and will not return any extra data about a channel.
+
+## Preferences
+
+In addition to administrator preferences mentioned above, you can now change your preferences without an account (see [#42](https://github.com/omarroth/invidious/pull/42)). I think this is quite an improvement to the usability of the site, and is much friendlier to privacy-conscious folks that don't want to make an account. Preferences will be automatically imported to a newly created account.
+
+Several issues with sorting subscriptions have been fixed, and `/manage_subscriptions` has been sped up significantly. The subscription feed has also seen a bump in performance. Delayed notifications have unfortunately started becoming a problem now that there are more users on the site. Some new changes are currently being tested which should mostly resolve the issue, so expect to see more in the next release.
+
+## Channel Playlists
+
+You can now view available playlists from a channel, and [auto-generated channels](https://invidio.us/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ) are no longer empty. You can sort as you would on YouTube, and all the same functionality should be available. I'm quite pleased to finally have it implemented, since it's currently the only data available from the above mentioned auto-generated channels, and makes it much easier to consume music on the site.
+
+There's also more discussion on improving Invidious for streaming music in [#304](https://github.com/omarroth/invidious/issues/304), and adding support for music.youtube.com. I would appreciate any thoughts on how to improve that experience, since it's a very large and useful part of YouTube.
+
+## Finances
+
+### Donations
+
+[Patreon](https://www.patreon.com/omarroth) : \$42.42
+[Liberapay](https://liberapay.com/omarroth) : \$30.97
+Crypto : ~\$0.00 (converted from BCH, BTC)
+Total : \$73.39
+
+### Expenses
+
+invidious-load1 (nyc1) : \$10.00 (load balancer)
+invidious-update1 (s-1vcpu-1gb) : \$5.00 (updates feeds)
+invidious-node1 (s-1vcpu-1gb) : \$5.00 (web server)
+invidious-node2 (s-1vcpu-1gb) : \$5.00 (web server)
+invidious-node3 (s-1vcpu-1gb) : \$5.00 (web server)
+invidious-node4 (s-1vcpu-1gb) : \$5.00 (web server)
+invidious-db1 (s-4vcpu-8gb) : \$40.00 (database)
+Total : \$75.00
+
+It's been very humbling to see how fast the project has grown, and I look forward to making the site even better. Thank you everyone.
+
# 0.14.0 (2019-02-06)
## Version 0.14.0: Community
diff --git a/assets/css/darktheme.css b/assets/css/darktheme.css index dce2cb91..1b70956b 100644 --- a/assets/css/darktheme.css +++ b/assets/css/darktheme.css @@ -28,6 +28,10 @@ body { color: rgba(35, 35, 35, 1); } +.pure-form input[type="file"] { + color: #f0f0f0; +} + .navbar > .searchbar input { background-color: inherit; color: inherit; diff --git a/assets/css/default.css b/assets/css/default.css index b9e9d0b8..cec255dd 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -1,3 +1,7 @@ +.deleted { + background-color: rgb(255, 0, 0, 0.5); +} + .channel-owner { background-color: #008bec; color: #fff; @@ -1,5 +1,5 @@ name: invidious -version: 0.14.1 +version: 0.15.0 authors: - Omar Roth <omarroth@hotmail.com> diff --git a/src/invidious.cr b/src/invidious.cr index e0967753..bbf9b4c9 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -90,9 +90,9 @@ REDDIT_URL = URI.parse("https://www.reddit.com") LOGIN_URL = URI.parse("https://accounts.google.com") PUBSUB_URL = URI.parse("https://pubsubhubbub.appspot.com") TEXTCAPTCHA_URL = URI.parse("http://textcaptcha.com/omarroth@hotmail.com.json") +CURRENT_BRANCH = `git branch | sed -n '/\* /s///p'`.strip CURRENT_COMMIT = `git rev-list HEAD --max-count=1 --abbrev-commit`.strip -CURRENT_VERSION = `git describe --tags $(git rev-list --tags --max-count=1)`.strip -CURRENT_BRANCH = `git status | head -1`.strip +CURRENT_VERSION = `git describe --tags --abbrev=0`.strip LOCALES = { "ar" => load_locale("ar"), @@ -264,7 +264,7 @@ get "/" do |env| if user user = user.as(User) if user.preferences.redirect_feed - env.redirect "/feed/subscriptions" + next env.redirect "/feed/subscriptions" end end @@ -417,7 +417,7 @@ get "/watch" do |env| video.description = replace_links(video.description) description = video.short_description - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) host_params = env.request.query_params host_params.delete_all("v") @@ -517,7 +517,7 @@ get "/embed/:id" do |env| video.description = replace_links(video.description) description = video.short_description - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) host_params = env.request.query_params host_params.delete_all("v") @@ -603,7 +603,7 @@ get "/opensearch.xml" do |env| locale = LOCALES[env.get("locale").as(String)]? env.response.content_type = "application/opensearchdescription+xml" - host = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host = make_host_url(config, Kemal.config) XML.build(indent: " ", encoding: "UTF-8") do |xml| xml.element("OpenSearchDescription", xmlns: "http://a9.com/-/spec/opensearch/1.1/") do @@ -1500,7 +1500,7 @@ get "/subscription_manager" do |env| subscriptions.sort_by! { |channel| channel.author.downcase } if action_takeout - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) if format == "json" env.response.content_type = "application/json" @@ -1583,14 +1583,7 @@ post "/data_control" do |env| user.subscriptions += body["subscriptions"].as_a.map { |a| a.as_s } user.subscriptions.uniq! - user.subscriptions.select! do |ucid| - begin - get_channel(ucid, PG_DB, false, false) - true - rescue ex - false - end - end + user.subscriptions = get_batch_channels(user.subscriptions, PG_DB, false, false) PG_DB.exec("UPDATE users SET subscriptions = $1 WHERE email = $2", user.subscriptions, user.email) end @@ -1964,7 +1957,7 @@ get "/feed/subscriptions" do |env| # Show latest video from each channel videos = PG_DB.query_all("SELECT DISTINCT ON (ucid) * FROM #{view_name} \ - ORDER BY ucid, published", as: ChannelVideo) + ORDER BY ucid, published DESC", as: ChannelVideo) end videos.sort_by! { |video| video.published }.reverse! @@ -2057,7 +2050,8 @@ end get "/feed/channel/:ucid" do |env| locale = LOCALES[env.get("locale").as(String)]? - env.response.content_type = "text/xml" + env.response.content_type = "application/atom+xml" + ucid = env.params.url["ucid"] begin @@ -2101,7 +2095,7 @@ get "/feed/channel/:ucid" do |env| ) end - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) path = env.request.path feed = XML.build(indent: " ", encoding: "UTF-8") do |xml| @@ -2168,6 +2162,8 @@ end get "/feed/private" do |env| locale = LOCALES[env.get("locale").as(String)]? + env.response.content_type = "application/atom+xml" + token = env.params.query["token"]? if !token @@ -2210,7 +2206,7 @@ get "/feed/private" do |env| if latest_only videos = PG_DB.query_all("SELECT DISTINCT ON (ucid) * FROM #{view_name} \ - ORDER BY ucid, published", as: ChannelVideo) + ORDER BY ucid, published DESC", as: ChannelVideo) videos.sort_by! { |video| video.published }.reverse! else @@ -2235,7 +2231,7 @@ get "/feed/private" do |env| videos = videos[0..max_results] end - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) path = env.request.path query = env.request.query.not_nil! @@ -2281,16 +2277,17 @@ get "/feed/private" do |env| end end - env.response.content_type = "application/atom+xml" feed end get "/feed/playlist/:plid" do |env| locale = LOCALES[env.get("locale").as(String)]? + env.response.content_type = "application/atom+xml" + plid = env.params.url["plid"] - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) path = env.request.path client = make_client(YT_URL) @@ -2315,7 +2312,6 @@ get "/feed/playlist/:plid" do |env| document = document.gsub(match[0], "<uri>#{content}</uri>") end - env.response.content_type = "text/xml" document end @@ -2327,7 +2323,6 @@ get "/feed/webhook/:token" do |env| mode = env.params.query["hub.mode"] topic = env.params.query["hub.topic"] challenge = env.params.query["hub.challenge"] - lease_seconds = env.params.query["hub.lease_seconds"] if verify_token.starts_with? "v1" _, time, nonce, signature = verify_token.split(":") @@ -2536,15 +2531,15 @@ end get "/api/v1/stats" do |env| env.response.content_type = "application/json" - if statistics["error"]? - halt env, status_code: 500, response: statistics.to_json - end - if !config.statistics_enabled error_message = {"error" => "Statistics are not enabled."}.to_json halt env, status_code: 400, response: error_message end + if statistics["error"]? + halt env, status_code: 500, response: statistics.to_json + end + if env.params.query["pretty"]? && env.params.query["pretty"] == "1" statistics.to_pretty_json else @@ -2897,7 +2892,7 @@ get "/api/v1/videos/:id" do |env| end if video.player_response["streamingData"]?.try &.["hlsManifestUrl"]? - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) host_params = env.request.query_params host_params.delete_all("v") @@ -4091,7 +4086,7 @@ get "/api/manifest/hls_variant/*" do |env| env.response.content_type = "application/x-mpegURL" env.response.headers.add("Access-Control-Allow-Origin", "*") - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) manifest = manifest.body manifest.gsub("https://www.youtube.com", host_url) @@ -4105,7 +4100,7 @@ get "/api/manifest/hls_playlist/*" do |env| halt env, status_code: manifest.status_code end - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) manifest = manifest.body.gsub("https://www.youtube.com", host_url) manifest = manifest.gsub(/https:\/\/r\d---.{11}\.c\.youtube\.com/, host_url) diff --git a/src/invidious/channels.cr b/src/invidious/channels.cr index b38c5e1a..484aac2f 100644 --- a/src/invidious/channels.cr +++ b/src/invidious/channels.cr @@ -197,11 +197,11 @@ def subscribe_pubsub(ucid, key, config) nonce = Random::Secure.hex(4) signature = "#{time}:#{nonce}" - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) body = { "hub.callback" => "#{host_url}/feed/webhook/v1:#{time}:#{nonce}:#{OpenSSL::HMAC.hexdigest(:sha1, key, signature)}", - "hub.topic" => "https://www.youtube.com/feeds/videos.xml?channel_id=#{ucid}", + "hub.topic" => "https://www.youtube.com/xml/feeds/videos.xml?channel_id=#{ucid}", "hub.verify" => "async", "hub.mode" => "subscribe", "hub.lease_seconds" => "432000", diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr index 5ccc1009..c200801f 100644 --- a/src/invidious/helpers/utils.cr +++ b/src/invidious/helpers/utils.cr @@ -193,19 +193,28 @@ def arg_array(array, start = 1) return args end -def make_host_url(ssl, host) +def make_host_url(config, kemal_config) + ssl = config.https_only || kemal_config.ssl + if ssl scheme = "https://" else scheme = "http://" end - if host - host = host.lchop(".") - return "#{scheme}#{host}" + if kemal_config.port != 80 && kemal_config.port != 443 + port = ":#{kemal_config.port}" else + port = "" + end + + if !config.domain return "" end + + host = config.domain.not_nil!.lchop(".") + + return "#{scheme}#{host}#{port}" end def get_referer(env, fallback = "/") diff --git a/src/invidious/jobs.cr b/src/invidious/jobs.cr index 49745aba..50374601 100644 --- a/src/invidious/jobs.cr +++ b/src/invidious/jobs.cr @@ -158,11 +158,13 @@ def subscribe_to_feeds(db, logger, key, config) spawn do loop do db.query_all("SELECT id FROM channels WHERE CURRENT_TIMESTAMP - subscribed > '4 days'") do |rs| - ucid = rs.read(String) - response = subscribe_pubsub(ucid, key, config) + rs.each do + ucid = rs.read(String) + response = subscribe_pubsub(ucid, key, config) - if response.status_code >= 400 - logger.write("#{ucid} : #{response.body}\n") + if response.status_code >= 400 + logger.write("#{ucid} : #{response.body}\n") + end end end diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index 99f99c58..e2cfa3a1 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -137,8 +137,6 @@ player.on('error', function(event) { } }); -player.share(shareOptions); - <% if params[:video_start] > 0 || params[:video_end] > 0 %> player.markers({ onMarkerReached: function(marker) { @@ -188,4 +186,7 @@ if (bpb) { }); } <% end %> + +// Since videojs-share can sometimes be blocked, we try to load it last +player.share(shareOptions); </script> diff --git a/src/invidious/views/playlist.ecr b/src/invidious/views/playlist.ecr index e6775e15..cd767140 100644 --- a/src/invidious/views/playlist.ecr +++ b/src/invidious/views/playlist.ecr @@ -1,5 +1,6 @@ <% content_for "header" do %> <title><%= playlist.title %> - Invidious</title> +<link rel="alternate" type="application/rss+xml" title="RSS" href="/feed/playlist/<%= plid %>" /> <% end %> <div class="pure-g h-box"> diff --git a/src/invidious/views/subscription_manager.ecr b/src/invidious/views/subscription_manager.ecr index 3c0836ea..0f9762f9 100644 --- a/src/invidious/views/subscription_manager.ecr +++ b/src/invidious/views/subscription_manager.ecr @@ -4,7 +4,9 @@ <div class="pure-g h-box"> <div class="pure-u-1-3"> - <h3><%= translate(locale, "`x` subscriptions", %(<span id="count">#{subscriptions.size}</span>)) %></h3> + <h3> + <a href="/feed/subscriptions"><%= translate(locale, "`x` subscriptions", %(<span id="count">#{subscriptions.size}</span>)) %></a> + </h3> </div> <div class="pure-u-1-3" style="text-align:center;"> <h3> @@ -20,15 +22,15 @@ <% subscriptions.each do |channel| %> <div class="h-box"> - <div class="pure-g"> + <div class="pure-g<% if channel.deleted %> deleted <% end%>"> <div class="pure-u-2-5"> - <h3> + <h3 style="padding-left: 0.5em"> <a href="/channel/<%= channel.id %>"><%= channel.author %></a> </h3> </div> <div class="pure-u-2-5"></div> <div class="pure-u-1-5" style="text-align: right;"> - <h3> + <h3 style="padding-right: 0.5em"> <a onclick="remove_subscription(this)" data-id="<%= channel.id %>" onmouseenter='this["href"]="javascript:void(0)"' |
