From 677ad510d83ea5992bc2f6ccc4615b98ae172467 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 25 Feb 2019 09:27:31 -0600 Subject: [PATCH 01/22] Filter premium content from descriptions (#210) --- src/js/subscriptions.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/js/subscriptions.js b/src/js/subscriptions.js index 5b73903ab..2e118d461 100644 --- a/src/js/subscriptions.js +++ b/src/js/subscriptions.js @@ -131,6 +131,10 @@ function loadSubscriptions() { } function addSubsToView(videoList) { + videoList = videoList.filter(a => { + return !a.premium; + }); + videoList.sort((a, b) => { return b.published - a.published; }); From ff1fdee3f66b5d0352caa6bfc90bb35c3c65bb71 Mon Sep 17 00:00:00 2001 From: rm-rf-home <36776420+rm-rf-home@users.noreply.github.com> Date: Mon, 25 Feb 2019 16:29:00 +0100 Subject: [PATCH 02/22] Grammar and spelling fixes (#213) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Grammar and spelling fixes "Kündigen" is a better translation for "unsubscribe" because "Abmelden" usually means "log out". No need to use formal "Sie", "Du" is now used for all translations * Update translation for "unsubscribe" --- locales/de.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/locales/de.json b/locales/de.json index ad867fa04..3915e0941 100644 --- a/locales/de.json +++ b/locales/de.json @@ -42,9 +42,9 @@ "Latest Subscriptions": "Neueste Abonnements", "Save Video": "Video Speichern", "Remove Saved Video": "Gespeichertes Video entfernen", - "Open in YouTube": "Öffnen in YouTube", + "Open in YouTube": "In YouTube öffnen", "Copy YouTube Link": "YouTube Link kopieren", - "Open in HookTube": "Öffnen in HookTube", + "Open in HookTube": "In HookTube öffnen", "Copy HookTube Link": "HookTube Link kopieren", "URL has been copied to the clipboard": "Die URL wurde in die Zwischenablage kopiert", "Found valid URL for 480p, but returned a 404. Video type might be available in the future.": "Es wurde eine gültige URL für 480p gefunden, aber eine 404 zurückgegeben. Der Videotyp könnte in Zukunft verfügbar sein.", @@ -53,7 +53,7 @@ "View": "Wiedergabe", "Views": "Wiedergaben", "Subscribe": "Abonnieren", - "Unsubscribe": "Abmelden", + "Unsubscribe": "Abo beenden", "Published on": "Veröffentlicht am", "Jan": "Jan", "Feb": "Feb", @@ -72,7 +72,7 @@ "Recommendations": "Empfehlungen", "Latest Subscriptions": "Neueste Abonnements", "Getting Subscriptions. Please wait...": "Rufe Abonnements ab. Bitte warten...", - "Your Subscription list is currently empty. Start adding subscriptions to see them here.": "Ihre Abonnementsliste ist momentan lehr. Füge Abonnements hinzu, um sie hier zu sehen.", + "Your Subscription list is currently empty. Start adding subscriptions to see them here.": "Deine Abonnementsliste ist momentan leer. Füge Abonnements hinzu, um sie hier zu sehen.", "Saved Videos": "Gespeichterte Videos", "Watch History": "Wiedergabeverlauf", "API Key": "API Schlüssel", @@ -85,7 +85,7 @@ "Clear Saved Videos": "Gespeicherte Videos löschen", "Are you sure you want to remove all saved videos?": "Möchtest du deine gespeicherten Videos wirklich löschen?", "Clear Subscriptions": "Abonnements löschen", - "Are you sure you want to remove all subscriptions?": "Möchten Sie Ihrer Abonnements wirklich löschen?", + "Are you sure you want to remove all subscriptions?": "Möchtest du deine Abonnements wirklich löschen?", "Save Settings": "Einstellungen Speichern", "Yes": "Ja", "No": "Nein", From 54767e65c82bc259ddb89f9ec406da73892ba84d Mon Sep 17 00:00:00 2001 From: ivan1548 <47206132+ivan1548@users.noreply.github.com> Date: Mon, 25 Feb 2019 07:31:04 -0800 Subject: [PATCH 03/22] [Fix] Toggle saved video in videoListTemplate (#214) --- src/js/templates.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/templates.js b/src/js/templates.js index 29fba6a2e..c1371901c 100644 --- a/src/js/templates.js +++ b/src/js/templates.js @@ -165,7 +165,7 @@ let subscriptionView = new Vue({ goToChannel(channelId); }, toggleSave: (videoId) => { - toggleSavedVideo(videoId); + addSavedVideo(videoId); }, copy: (site, videoId) => { const url = 'https://' + site + '/watch?v=' + videoId; @@ -255,7 +255,7 @@ let savedView = new Vue({ goToChannel(channelId); }, toggleSave: (videoId) => { - addSavedVideo(videoId); + toggleSavedVideo(videoId); }, copy: (site, videoId) => { const url = 'https://' + site + '/watch?v=' + videoId; @@ -528,7 +528,7 @@ let playerView = new Vue({ showToast('URL has been copied to the clipboard'); }, save: (videoId) => { - toggleSavedVideo(videoId); + addSavedVideo(videoId); }, play: (videoId, playlistId = '') => { loadingView.seen = true; From 916812b8f9a198f1154611f426720fbdb8d04150 Mon Sep 17 00:00:00 2001 From: ivan1548 <47206132+ivan1548@users.noreply.github.com> Date: Mon, 25 Feb 2019 07:31:29 -0800 Subject: [PATCH 04/22] Remove API Keys section from CONTRIBUTING.md (#215) --- CONTRIBUTING.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 79c8c1264..a14a1bf2d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,3 @@ -# API Keys - -When you are testing and working on FreeTube, PLEASE use your own API Key. The keys included in the project are in use by the userbase and testing can cause these keys to max out. Please do not risk degrading the experience for other users and use your own key if at all possible. Thank you for your cooperation. - # Code Contributions Please follow these guidlines before sending your pull request and making contributions. From 390bc24c1cf18063be67b3c9f565ed874ed0a2a8 Mon Sep 17 00:00:00 2001 From: ivan1548 <47206132+ivan1548@users.noreply.github.com> Date: Mon, 25 Feb 2019 07:35:49 -0800 Subject: [PATCH 05/22] Remove duplicates from history (#216) --- src/js/history.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/js/history.js b/src/js/history.js index 750f141ff..784435d79 100644 --- a/src/js/history.js +++ b/src/js/history.js @@ -30,7 +30,20 @@ function addToHistory(videoId){ videoId: videoId, timeWatched: new Date().getTime(), }; - historyDb.insert(data, (err, newDoc) => {}); + + historyDb.findOne({ videoId: videoId }, function (err, doc) { + if(doc === null) { + historyDb.insert(data, (err, newDoc) => {}); + } else { + historyDb.update( + { videoId: videoId }, + { + $set: { + timeWatched: data.timeWatched, + } + }, {}, (err, newDoc) => {}); + } + }); } /** From 378dad873dcf1bd29849097701d27660bfd9fef5 Mon Sep 17 00:00:00 2001 From: ivan1548 <47206132+ivan1548@users.noreply.github.com> Date: Mon, 25 Feb 2019 07:37:04 -0800 Subject: [PATCH 06/22] [Fix] Inconsistent order in history (#217) --- src/js/history.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/js/history.js b/src/js/history.js index 784435d79..da32cd4c8 100644 --- a/src/js/history.js +++ b/src/js/history.js @@ -75,12 +75,10 @@ function showHistory(){ historyDb.find({}).sort({ timeWatched: -1 }).exec((err, docs) => { - let position = 0; - docs.forEach((video) => { + docs.forEach((video, index) => { invidiousAPI('videos', video.videoId, {}, (data) => { - data.position = position; + data.position = index; displayVideo(data, 'history'); - position++; }); }); From 6b0c7512583e75eb0610275fc0b780c22e17eb7c Mon Sep 17 00:00:00 2001 From: Hiers <47784553+Hiers@users.noreply.github.com> Date: Mon, 25 Feb 2019 15:38:06 +0000 Subject: [PATCH 07/22] Created portuguese translation (#224) * Created portuguese tranlation * Update pt-PT.json --- locales/pt-PT.json | 95 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 locales/pt-PT.json diff --git a/locales/pt-PT.json b/locales/pt-PT.json new file mode 100644 index 000000000..d0e74962d --- /dev/null +++ b/locales/pt-PT.json @@ -0,0 +1,95 @@ +{ + "File": "Ficheiro", + "Quit": "Sair", + "Edit": "Editar", + "Undo": "Desfazer", + "Redo": "Refazer", + "Cut": "Cortar", + "Copy": "Copiar", + "Paste": "Colar", + "Delete": "Apagar", + "Select all": "Selecionar tudo", + "View": "Ver", + "Reload": "Recarregar", + "Force Reload": "Forçar a recarregar", + "Toggle Developer Tools": "Inspecionar", + "Actual size": "100% zoom", + "Zoom in": "Aumentar zoom", + "Zoom out": "Diminuir zoom", + "Toggle fullscreen": "Ecrã inteiro", + "Window": "Janela", + "Minimize": "Minimizar", + "Close": "Fechar", + "FreeTube": "FreeTube", + "Subscriptions": "Subscrições", + "Featured": "Em destaque", + "Most Popular": "Popular", + "Saved": "Guardado", + "Playlists": "Listas", + "History": "Histórico", + "Settings": "Definições", + "About": "Sobre", + "Search / Go to URL": "Pesquisar / Ir para URL", + "Search Results": "Resultados", + "Subscriber": "Subscritor", + "Subscribers": "Subscritores", + "Video": "Vídeo", + "Videos": "Vídeos", + "View Full Playlist": "Ver Lista Completa", + "Live Now": "Ao Vivo", + "Fetch more results": "Buscar mais resultados", + "Fetching results. Please wait": "A buscar mais resultados. Por favor espere", + "Latest Subscriptions": "Subscrições recentes", + "Save Video": "Guardar vídeo", + "Remove Saved Video": "Remover vídeo guardado", + "Open in YouTube": "Abrir no Youtube", + "Copy YouTube Link": "Copiar link para Youtube", + "Open in HookTube": "Abrir no HookTube", + "Copy HookTube Link": "Copiar link para HookTube", + "URL has been copied to the clipboard": "O URL foi copiado", + "Found valid URL for 480p, but returned a 404. Video type might be available in the future.": "Foi encontrado um URL válido para 480p, mas devolveu um erro 404. Este tipo de vídeo pode estar disponível no futuro.", + "Save": "Guardar", + "Mini Player": "Mini Janela", + "View": "Ver", + "Views": "Visualizacões", + "Subscribe": "Subscrever", + "Unsubscribe": "Cancelar subscrição", + "Published on": "Publicado em", + "Jan": "Jan", + "Feb": "Fev", + "Mar": "Mar", + "Apr": "Abr", + "May": "Mai", + "Jun": "Jun", + "Jul": "Jul", + "Aug": "Ago", + "Sep": "Set", + "Oct": "Out", + "Nov": "Nov", + "Dec": "Dez", + "Show Comments": "Mostrar comentários", + "Max of 100": "Máximo de 100", + "Recommendations": "Recomendado", + "Latest Subscriptions": "Subscrições recentes", + "Getting Subscriptions. Please wait...": "A buscar subscrições. Por favor espere...", + "Your Subscription list is currently empty. Start adding subscriptions to see them here.": "De momento não tens subscrições. Adiciona algumas para as ver aqui.", + "Saved Videos": "Vídeos guardados", + "Watch History": "Ver histórico", + "API Key": "Chave API", + "Set API Key: Leave blank to use default": "Definir chave API: Deixe em branco para usar padrão", + "Use Dark Theme": "Usar Tema Escuro", + "Import Subscriptions": "Importar Subscrições", + "Export Subscriptions": "Exportar Subcrições", + "Clear History": "Apagar Histórico", + "Are you sure you want to delete your history?": "Tens certeza que queres apagar o teu histórico?", + "Clear Saved Videos": "Apagar vídeos guardados", + "Are you sure you want to remove all saved videos?": "Tens a certeza que queres apagar todos os teus vídeos guardados?", + "Clear Subscriptions": "Cancelar Subscrições", + "Are you sure you want to remove all subscriptions?": "Tens a certeza que queres cancelar todas as tuas subscrições?", + "Save Settings": "Guardar definições", + "Yes": "Sim", + "No": "Não", + "Beta": "Beta", + "This software is FOSS and released under the GNU Public License v3+.": "Este software é FOSS e lançado sobre a GPLv3+.", + "Found a bug? Want to suggest a feature? Want to help out? Check out our GitHub page. Pull requests are welcome.": "Encontraste um defeito? Queres fazer uma sugestão? Queres ajudar? Vê a nossa página no GitHub. Contribuições são agradecidas." +} From 7b530b0dcba9b54ccfc590bb8dcc4b144ca26fb7 Mon Sep 17 00:00:00 2001 From: ivan1548 <47206132+ivan1548@users.noreply.github.com> Date: Tue, 26 Feb 2019 06:19:12 -0800 Subject: [PATCH 08/22] Add back button for searchView and playlistView (#223) * Add back button for searchView and playlistView * Remove title --- src/index.html | 3 ++- src/js/templates.js | 52 +++++++++++++++++++++++++++++++++++++++++++++ src/style/main.css | 9 ++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/index.html b/src/index.html index 18abc9563..997919912 100644 --- a/src/index.html +++ b/src/index.html @@ -32,9 +32,10 @@ -
+
+
+
+

Search Filters

+ + + + +

Your Subscription list is currently empty. Start adding subscriptions diff --git a/src/js/templates.js b/src/js/templates.js index 644e93f12..9612872ec 100644 --- a/src/js/templates.js +++ b/src/js/templates.js @@ -50,6 +50,13 @@ let loadingView = new Vue({ } }); +let searchFilter = new Vue({ + el: '#searchFilter', + data: { + seen: false + } +}); + let noSubscriptions = new Vue({ el: '#noSubscriptions', data: { @@ -377,7 +384,7 @@ let searchView = new Vue({ play: (videoId) => { loadingView.seen = true; playVideo(videoId); - + backButtonView.lastView = searchView }, channel: (channelId) => { diff --git a/src/js/videos.js b/src/js/videos.js index add044e1f..1fb11837a 100644 --- a/src/js/videos.js +++ b/src/js/videos.js @@ -33,6 +33,12 @@ const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", */ function search(page = 1) { const query = document.getElementById('search').value; + const searchSortby = document.getElementById('searchSortby').value; + const searchType = document.getElementById('searchType').value; + const searchDate = document.getElementById('searchDate').value; + const searchDuration = document.getElementById('searchDuration').value; + + searchFilter.seen = false; if (query === '') { return; @@ -52,7 +58,10 @@ function search(page = 1) { invidiousAPI('search', '', { q: query, page: page, - type: 'all', + sort_by: searchSortby, + date: searchDate, + duration: searchDuration, + type: searchType, }, function (data) { console.log(data); @@ -219,7 +228,7 @@ function displayChannel(channel) { let channelData = {}; channelData.channelId = channel.authorId; - channelData.thumbnail = channel.authorThumbnails[4].url; + channelData.thumbnail = "https:" + channel.authorThumbnails[4].url; channelData.channelName = channel.author; channelData.description = channel.description; channelData.subscriberCount = channel.subCount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); diff --git a/src/style/darkTheme.css b/src/style/darkTheme.css index 299392e7d..af72d1924 100644 --- a/src/style/darkTheme.css +++ b/src/style/darkTheme.css @@ -85,6 +85,14 @@ input[type=text] { color: #E0E0E0; } +.filterButton { + color: #E0E0E0; +} + +#searchFilter { + background-color: #424242; +} + .jumpToInput { border-bottom: 1px solid #E0E0E0; } diff --git a/src/style/lightTheme.css b/src/style/lightTheme.css index 287a6b4a9..bc2090c74 100644 --- a/src/style/lightTheme.css +++ b/src/style/lightTheme.css @@ -72,6 +72,14 @@ body { color: black; } +.filterButton { + color: black; +} + +#searchFilter { + background-color: white; +} + .jumpToInput { border-bottom: 1px solid #757575; } diff --git a/src/style/main.css b/src/style/main.css index cb185c77e..2c4c4cf7d 100644 --- a/src/style/main.css +++ b/src/style/main.css @@ -118,17 +118,31 @@ a { .searchBar { position: absolute; - right: 135px; + right: 100px; top: 0; - width: 50%; + width: 600px; } .searchBar input { - width: 90%; + width: 60%; +} + +#searchFilter { + width: 90%; + padding: 10px; + margin-bottom: 20px; + -webkit-box-shadow: 4px -2px 51px -6px rgba(0, 0, 0, 0.75); } .searchButton { text-decoration: none; + cursor: pointer; +} + +.filterButton { + text-decoration: none; + margin-right: 10px; + cursor: pointer; } #sideNav { From 47094ecb855266ac6fa2f5e96b8476f89e196cb0 Mon Sep 17 00:00:00 2001 From: ivan1548 <47206132+ivan1548@users.noreply.github.com> Date: Tue, 26 Feb 2019 12:45:55 -0800 Subject: [PATCH 12/22] Update tor support with electron proxy (#225) * Update tor support with electron proxy * Update proxy testing, fix variable conflict * Add default value to settings view for proxyAddress * Remove tor-request --- package-lock.json | 175 ++---------------------------------- package.json | 1 - src/js/init.js | 14 ++- src/js/layout.js | 35 +++++++- src/js/settings.js | 34 +++++++ src/js/templates.js | 33 +++++++ src/js/youtubeApi.js | 24 ++--- src/style/main.css | 13 +++ src/templates/settings.html | 27 +++++- 9 files changed, 172 insertions(+), 184 deletions(-) diff --git a/package-lock.json b/package-lock.json index 06250c9a4..58ac3669d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -125,6 +125,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz", "integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==", + "dev": true, "requires": { "es6-promisify": "^5.0.0" } @@ -4441,12 +4442,14 @@ "es6-promise": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==" + "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", + "dev": true }, "es6-promisify": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, "requires": { "es6-promise": "^4.0.3" } @@ -4623,11 +4626,6 @@ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" - }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -5402,8 +5400,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/imul/-/imul-1.0.1.tgz", "integrity": "sha1-nVhnFh6LPelsLDjV3HyxAvNeKsk=", - "dev": true, - "optional": true + "dev": true }, "indent-string": { "version": "2.1.0", @@ -5546,11 +5543,6 @@ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - }, "is-absolute-url": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", @@ -5647,8 +5639,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true, - "optional": true + "dev": true }, "is-relative-path": { "version": "1.0.2", @@ -6782,8 +6773,7 @@ "version": "2.11.1", "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==", - "dev": true, - "optional": true + "dev": true }, "natives": { "version": "1.1.6", @@ -8445,11 +8435,6 @@ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", "dev": true }, - "smart-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.1.tgz", - "integrity": "sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==" - }, "sntp": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", @@ -8460,24 +8445,6 @@ "hoek": "2.x.x" } }, - "socks": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.1.tgz", - "integrity": "sha512-0GabKw7n9mI46vcNrVfs0o6XzWzjVa3h6GaSo2UPxtWAROXUWavfJWh1M4PR5tnE0dcnQXZIDFP4yrAysLze/w==", - "requires": { - "ip": "^1.1.5", - "smart-buffer": "^4.0.1" - } - }, - "socks-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz", - "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==", - "requires": { - "agent-base": "~4.2.0", - "socks": "~2.2.0" - } - }, "sorcery": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.10.0.tgz", @@ -9144,134 +9111,6 @@ "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", "dev": true }, - "tor-request": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tor-request/-/tor-request-2.3.0.tgz", - "integrity": "sha512-uFsCDUYaJn1S55bPyIbi7TrpQyEB6F2WdU7LxgCqKN+VrfRladNaE99V9zCD5TrCPvizmLL+sTH2JHWFIpgv3A==", - "requires": { - "request": "~2.88.0", - "socks-proxy-agent": "~4.0.1" - }, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" - }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "har-validator": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", - "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", - "requires": { - "ajv": "^5.3.0", - "har-schema": "^2.0.0" - } - }, - "mime-db": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", - "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==" - }, - "mime-types": { - "version": "2.1.20", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", - "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", - "requires": { - "mime-db": "~1.36.0" - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - } - } - }, "touch": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/touch/-/touch-0.0.3.tgz", diff --git a/package.json b/package.json index 7934fb15c..c2bf0d3ab 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,6 @@ "mustache": "^2.3.0", "nedb": "^1.8.0", "opml-to-json": "0.0.3", - "tor-request": "^2.3.0", "vue": "^2.5.17", "ytdl-core": "^0.29.1" } diff --git a/src/js/init.js b/src/js/init.js index 09b305f84..2a639b544 100644 --- a/src/js/init.js +++ b/src/js/init.js @@ -24,7 +24,8 @@ const { app, BrowserWindow, dialog, - protocol + protocol, + ipcMain } = require('electron'); const path = require('path'); const url = require('url'); @@ -163,6 +164,17 @@ let init = function () { const menu = Menu.buildFromTemplate(template); Menu.setApplicationMenu(menu); + + /** + * Sets proxy when setProxy event is sent from renderer + * + * example data "SOCKS5://127.0.0.1:9050" + */ + ipcMain.on("setProxy", (_e, data) => { + win.webContents.session.setProxy({ proxyRules: data }, function () { + win.webContents.send("proxyAvailable") + }); + }); }; /** diff --git a/src/js/layout.js b/src/js/layout.js index ed2ee00fd..b78c95418 100644 --- a/src/js/layout.js +++ b/src/js/layout.js @@ -38,7 +38,6 @@ const shell = electron.shell; // Used to open external links into the user's nat const clipboard = electron.clipboard; const getOpml = require('opml-to-json'); // Gets the file type for imported files. const fs = require('fs'); // Used to read files. Specifically in the settings page. -const tor = require('tor-request'); // User Defaults let currentTheme = ''; @@ -50,6 +49,10 @@ let checkForUpdates = true; let currentVolume = 1; let defaultQuality = 720; let defaultPlaybackRate = '1'; +// Proxy address variable +let defaultProxy = false; +// This variable is to make sure that proxy was set before making any API calls +let proxyAvailable = false; let dialog = electron.remote.dialog; // Used for opening file browser to export / import subscriptions. let toastTimeout; // Timeout for toast notifications. @@ -69,6 +72,11 @@ electron.ipcRenderer.on('ping', function(event, message) { ft.log(message); }); +// Listens for proxy to be set in main process +electron.ipcRenderer.on('proxyAvailable', function(event, message) { + proxyAvailable = true; +}); + $(document).ready(() => { const searchBar = document.getElementById('search'); const jumpToInput = document.getElementById('jumpToInput'); @@ -209,3 +217,28 @@ function showVideoOptions(element) { element.nextElementSibling.style.display = 'none' } } + +/** + * Wrapper around AJAX calls to wait for proxy to become available + * @return {Void} + */ +function proxyRequest(callback) { + let proxyCheckingInterval; + let counter = 0; + + // Wait for proxy to become available + proxyCheckingInterval = setInterval(function() { + if(proxyAvailable) { + clearInterval(proxyCheckingInterval) + + callback(); + + } else { + if(counter > 10) { + clearInterval(proxyCheckingInterval); + showToast('Unable to connect to the Tor network. Check the help page if you\'re having trouble setting up your node.'); + } + counter++; + } + }, 100); +} \ No newline at end of file diff --git a/src/js/settings.js b/src/js/settings.js index f2cc04ae5..fb3431212 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -77,6 +77,12 @@ function updateSettingsView() { document.getElementById('qualitySelect').value = defaultQuality; document.getElementById('rateSelect').value = defaultPlaybackRate; + + if(defaultProxy) { + settingsView.proxyAddress = defaultProxy; + } else { + settingsView.proxyAddress = "SOCKS5://127.0.0.1:9050"; + } }); } @@ -98,6 +104,7 @@ function checkDefaultSettings() { 'updates': true, 'quality': '720', 'rate': '1', + 'proxy': "SOCKS5://127.0.0.1:9050" // This is default value for tor client }; console.log(settingDefaults); @@ -151,6 +158,13 @@ function checkDefaultSettings() { case 'rate': defaultPlaybackRate = docs[0]['value']; break; + case 'proxy': + defaultProxy = docs[0]['value']; + + if(useTor && defaultProxy) { + electron.ipcRenderer.send("setProxy", defaultProxy); + } + break; default: break; } @@ -173,6 +187,7 @@ function updateSettings() { let updatesSwitch = document.getElementById('updatesSwitch').checked; let qualitySelect = document.getElementById('qualitySelect').value; let rateSelect = document.getElementById('rateSelect').value; + let proxyAddress = document.getElementById('proxyAddress').value; let theme = 'light'; settingsView.useTor = torSwitch; @@ -180,6 +195,7 @@ function updateSettings() { settingsView.autoplay = autoplaySwitch; settingsView.subtitles = subtitlesSwitch; settingsView.updates = updatesSwitch; + settingsView.proxyAddress = proxyAddress; rememberHistory = historySwitch; defaultQuality = qualitySelect; defaultPlaybackRate = rateSelect; @@ -209,6 +225,17 @@ function updateSettings() { useTor = torSwitch; }); + // Update proxy address + settingsDb.update({ + _id: 'proxy' + }, { + value: proxyAddress + }, {}, function(err, numReplaced) { + console.log(err); + console.log(numReplaced); + defaultProxy = proxyAddress; + }); + // Update history settingsDb.update({ _id: 'history' @@ -275,6 +302,13 @@ function updateSettings() { defaultPlaybackRate = rateSelect; }); + // set proxy in electron based on new values + if(torSwitch) { + electron.ipcRenderer.send("setProxy", proxyAddress); + } else { + electron.ipcRenderer.send("setProxy", {}); + } + showToast('Settings have been saved.'); } diff --git a/src/js/templates.js b/src/js/templates.js index 9612872ec..030be3756 100644 --- a/src/js/templates.js +++ b/src/js/templates.js @@ -368,6 +368,39 @@ let settingsView = new Vue({ autoplay: true, subtitles: false, updates: true, + proxyAddress: false, + checkProxyResult: false, + proxyTestLoading: false + }, + methods: { + checkProxy() { + this.checkProxyResult = false; + this.proxyTestLoading = true; + electron.ipcRenderer.send("setProxy", this.proxyAddress) + + proxyRequest(() => { + $.ajax({ + url: "https://ifconfig.co/json", + dataType: 'json', + timeout: 3000 // 3 second timeout + }).done(response => { + this.checkProxyResult = response; + }) + .fail((xhr, textStatus, error) => { + console.log(xhr); + console.log(textStatus); + showToast('Proxy test failed'); + }).always(() =>{ + this.proxyTestLoading = false; + electron.ipcRenderer.send("setProxy", {}); + }); + }) + } + }, + computed: { + proxyTestButtonText() { + return this.proxyTestLoading ? "LOADING..." : "TEST PROXY" + } }, template: settingsTemplate }); diff --git a/src/js/youtubeApi.js b/src/js/youtubeApi.js index 37bb661bc..34bdca9b8 100644 --- a/src/js/youtubeApi.js +++ b/src/js/youtubeApi.js @@ -32,17 +32,19 @@ function invidiousAPI(resource, id, params, success, fail = function(){ let requestUrl = 'https://www.invidio.us/api/v1/' + resource + '/' + id + '?' + $.param(params); if (useTor) { - tor.request(requestUrl, (err, res, body) => { - if (!err && res.statusCode == 200) { - success(JSON.parse(body)); - } else { - showToast('Unable to connect to the Tor network. Check the help page if you\'re having trouble setting up your node.'); - console.log(err); - console.log(res); - console.log(body); - loadingView.seen = false; - } - }); + + proxyRequest(() => { + $.getJSON( + requestUrl, + success + ).fail((xhr, textStatus, error) => { + fail(xhr); + console.log(xhr); + console.log(textStatus); + console.log(requestUrl); + }); + }) + } else { $.getJSON( requestUrl, diff --git a/src/style/main.css b/src/style/main.css index 2c4c4cf7d..2846302e5 100644 --- a/src/style/main.css +++ b/src/style/main.css @@ -242,6 +242,19 @@ a { border-bottom: 1px solid #616161; } +.input-text-settings input { + width: auto; + border-bottom: 1px solid #616161; + width: 50%; + margin: 0 auto; + display: block; +} + +#ipInfoSettings { + border: 1px solid; + margin: 10px 0; +} + .settingsButton { padding: 10px; display: inline-block; diff --git a/src/templates/settings.html b/src/templates/settings.html index aa91807dc..927829c4e 100644 --- a/src/templates/settings.html +++ b/src/templates/settings.html @@ -3,8 +3,6 @@
- -
@@ -43,6 +41,31 @@

+
+ + + +
+ + +
+ +
+

Your Info:

+

ip: {{checkProxyResult.ip}}

+

ip_decimal: {{checkProxyResult.ip_decimal}}

+

country: {{checkProxyResult.country}}

+

city: {{checkProxyResult.city || "N/A"}}

+

hostname: {{checkProxyResult.hostname || "N/A"}}

+
+ +

Clicking "TEST PROXY" button will send a request to https://ifconfig.co/json

+
+ {{proxyTestButtonText}} +
+
+
+
IMPORT SUBSCRIPTIONS From 942c39a6872078bd9726f7769df14c51593796b9 Mon Sep 17 00:00:00 2001 From: PrestonN Date: Wed, 27 Feb 2019 13:46:25 -0500 Subject: [PATCH 13/22] Allow use of self-hosted Invidious Instance --- src/js/layout.js | 21 +++---------------- src/js/player.js | 42 ++++++++++++++++--------------------- src/js/settings.js | 37 +++++++++++++++++++++++++++++++- src/js/subscriptions.js | 9 ++------ src/js/templates.js | 3 ++- src/js/youtubeApi.js | 6 +++--- src/templates/settings.html | 9 ++++++-- 7 files changed, 71 insertions(+), 56 deletions(-) diff --git a/src/js/layout.js b/src/js/layout.js index b78c95418..eff376204 100644 --- a/src/js/layout.js +++ b/src/js/layout.js @@ -39,21 +39,6 @@ const clipboard = electron.clipboard; const getOpml = require('opml-to-json'); // Gets the file type for imported files. const fs = require('fs'); // Used to read files. Specifically in the settings page. -// User Defaults -let currentTheme = ''; -let useTor = false; -let rememberHistory = true; -let autoplay = true; -let enableSubtitles = false; -let checkForUpdates = true; -let currentVolume = 1; -let defaultQuality = 720; -let defaultPlaybackRate = '1'; -// Proxy address variable -let defaultProxy = false; -// This variable is to make sure that proxy was set before making any API calls -let proxyAvailable = false; - let dialog = electron.remote.dialog; // Used for opening file browser to export / import subscriptions. let toastTimeout; // Timeout for toast notifications. let mouseTimeout; // Timeout for hiding the mouse cursor on video playback @@ -225,12 +210,12 @@ function showVideoOptions(element) { function proxyRequest(callback) { let proxyCheckingInterval; let counter = 0; - + // Wait for proxy to become available proxyCheckingInterval = setInterval(function() { if(proxyAvailable) { clearInterval(proxyCheckingInterval) - + callback(); } else { @@ -241,4 +226,4 @@ function proxyRequest(callback) { counter++; } }, 100); -} \ No newline at end of file +} diff --git a/src/js/player.js b/src/js/player.js index ae432aaca..58fcf46dc 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -32,15 +32,13 @@ function playVideo(videoId, playlistId = '') { playerView.playerSeen = true; playerView.firstLoad = true; playerView.videoId = videoId; - playerView.videoAudio = ''; playerView.videoAudio = undefined; playerView.validAudio = true; - playerView.video480p = ''; playerView.video480p = undefined; playerView.valid480p = true; - playerView.video720p = ''; playerView.video720p = undefined; playerView.valid720p = true; + playerView.videoUrl = ''; playerView.embededHtml = ""; let videoHtml = ''; @@ -69,11 +67,11 @@ function playVideo(videoId, playlistId = '') { switch (videoUrls[key]['itag']) { case '18': playerView.video480p = decodeURIComponent(videoUrls[key]['url']); - //console.log(video480p); + // console.log(playerView.video480p); break; case '22': playerView.video720p = decodeURIComponent(videoUrls[key]['url']); - //console.log(video720p); + // console.log(playerView.video720p); break; } }); @@ -82,6 +80,7 @@ function playVideo(videoId, playlistId = '') { playerView.videoAudio = decodeURIComponent(formatUrls[formatUrls.length - 1]['url']); if (typeof(playerView.videoAudio) === 'undefined') { + console.log(playerView.videoAudio); playerView.validAudio = false; } @@ -107,15 +106,25 @@ function playVideo(videoId, playlistId = '') { //playerView.videoUrl = playerView.liveManifest; } - if (!useEmbedPlayer && data.player_response.captions !== undefined) { + if (!useEmbedPlayer && data.player_response.captions.playerCaptionsTracklistRenderer.captionTracks !== undefined) { data.player_response.captions.playerCaptionsTracklistRenderer.captionTracks.forEach((caption) => { - let subtitleUrl = 'https://www.invidio.us/api/v1/captions/' + videoId + '?label=' + caption.name.simpleText; + let subtitleUrl = invidiousInstance + '/api/v1/captions/' + videoId + '?label=' + caption.name.simpleText; videoHtml = videoHtml + ''; }); playerView.subtitleHtml = videoHtml; } + + loadingView.seen = false; + + if (subscriptionView.seen === false && aboutView.seen === false && headerView.seen === false && searchView.seen === false && settingsView.seen === false && popularView.seen === false && savedView.seen === false && historyView.seen === false && channelView.seen === false && channelVideosView.seen === false) { + playerView.seen = true; + } else { + return; + } + + window.setTimeout(checkVideoUrls, 5000, playerView.video480p, playerView.video720p, playerView.videoAudio); }); invidiousAPI('videos', videoId, {}, (data) => { @@ -148,13 +157,9 @@ function playVideo(videoId, playlistId = '') { checkSubscription.then((results) => { if (results === false) { - if (subscribeButton != null) { - playerView.subscribedText = 'SUBSCRIBE'; - } + playerView.subscribedText = 'SUBSCRIBE'; } else { - if (subscribeButton != null) { - playerView.subscribedText = 'UNSUBSCRIBE'; - } + playerView.subscribedText = 'UNSUBSCRIBE'; } }); @@ -234,20 +239,9 @@ function playVideo(videoId, playlistId = '') { playerView.playlistId = ''; } - loadingView.seen = false; - - if (subscriptionView.seen === false && aboutView.seen === false && headerView.seen === false && searchView.seen === false && settingsView.seen === false && popularView.seen === false && savedView.seen === false && historyView.seen === false && channelView.seen === false && channelVideosView.seen === false) { - playerView.seen = true; - } else { - return; - } - if (rememberHistory === true){ addToHistory(videoId); } - - window.setTimeout(checkVideoUrls, 5000, playerView.video480p, playerView.video720p, playerView.videoAudio); - }); } diff --git a/src/js/settings.js b/src/js/settings.js index fb3431212..dfdddb0dc 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -18,6 +18,23 @@ along with FreeTube. If not, see . * A file for functions used for settings. */ + // User Defaults + let currentTheme = ''; + let useTor = false; + let rememberHistory = true; + let autoplay = true; + let enableSubtitles = false; + let checkForUpdates = true; + let currentVolume = 1; + let defaultQuality = 720; + let defaultPlaybackRate = '1'; + // Proxy address variable + let defaultProxy = false; + // This variable is to make sure that proxy was set before making any API calls + let proxyAvailable = false; + let invidiousInstance = 'https://invidio.us'; + let checkedSettings = false; // Used to prevent data leak when using self-hosted Invidious Instance + /** * Display the settings screen to the user. * @@ -104,6 +121,7 @@ function checkDefaultSettings() { 'updates': true, 'quality': '720', 'rate': '1', + 'invidious': 'https://invidio.us', 'proxy': "SOCKS5://127.0.0.1:9050" // This is default value for tor client }; @@ -160,11 +178,15 @@ function checkDefaultSettings() { break; case 'proxy': defaultProxy = docs[0]['value']; - + if(useTor && defaultProxy) { electron.ipcRenderer.send("setProxy", defaultProxy); } break; + case 'invidious': + settingsView.invidiousInstance = docs[0]['value']; + invidiousInstance = docs[0]['value']; + break; default: break; } @@ -188,6 +210,7 @@ function updateSettings() { let qualitySelect = document.getElementById('qualitySelect').value; let rateSelect = document.getElementById('rateSelect').value; let proxyAddress = document.getElementById('proxyAddress').value; + let invidious = document.getElementById('invidiousInstance').value; let theme = 'light'; settingsView.useTor = torSwitch; @@ -236,6 +259,18 @@ function updateSettings() { defaultProxy = proxyAddress; }); + // Update Invidious Instance + settingsDb.update({ + _id: 'invidious' + }, { + value: invidious + }, {}, function(err, numReplaced) { + console.log(err); + console.log(numReplaced); + settingsView.invidiousInstance = invidious; + invidiousInstance = invidious; + }); + // Update history settingsDb.update({ _id: 'history' diff --git a/src/js/subscriptions.js b/src/js/subscriptions.js index 648ecade5..8a66b5e3e 100644 --- a/src/js/subscriptions.js +++ b/src/js/subscriptions.js @@ -238,19 +238,14 @@ function toggleSubscription(channelId) { event.stopPropagation(); const checkIfSubscribed = isSubscribed(channelId); - const subscribeButton = document.getElementById('subscribeButton'); checkIfSubscribed.then((results) => { if (results === false) { - if (subscribeButton != null) { - subscribeButton.innerHTML = 'UNSUBSCRIBE'; - } + playerView.subscribedText = 'SUBSCRIBE'; addSubscription(channelId); } else { - if (subscribeButton != null) { - subscribeButton.innerHTML = 'SUBSCRIBE'; - } + playerView.subscribedText = 'UNSUBSCRIBE'; removeSubscription(channelId); } }); diff --git a/src/js/templates.js b/src/js/templates.js index 030be3756..cf50c0a2c 100644 --- a/src/js/templates.js +++ b/src/js/templates.js @@ -369,6 +369,7 @@ let settingsView = new Vue({ subtitles: false, updates: true, proxyAddress: false, + invidiousInstance: 'https://invidio.us', checkProxyResult: false, proxyTestLoading: false }, @@ -383,7 +384,7 @@ let settingsView = new Vue({ url: "https://ifconfig.co/json", dataType: 'json', timeout: 3000 // 3 second timeout - }).done(response => { + }).done(response => { this.checkProxyResult = response; }) .fail((xhr, textStatus, error) => { diff --git a/src/js/youtubeApi.js b/src/js/youtubeApi.js index 34bdca9b8..61e032758 100644 --- a/src/js/youtubeApi.js +++ b/src/js/youtubeApi.js @@ -29,10 +29,10 @@ function invidiousAPI(resource, id, params, success, fail = function(){ showToast('There was an error calling the Invidious API.'); loadingView.seen = false; }) { - let requestUrl = 'https://www.invidio.us/api/v1/' + resource + '/' + id + '?' + $.param(params); + let requestUrl = invidiousInstance + '/api/v1/' + resource + '/' + id + '?' + $.param(params); if (useTor) { - + proxyRequest(() => { $.getJSON( requestUrl, @@ -42,7 +42,7 @@ function invidiousAPI(resource, id, params, success, fail = function(){ console.log(xhr); console.log(textStatus); console.log(requestUrl); - }); + }); }) } else { diff --git a/src/templates/settings.html b/src/templates/settings.html index 927829c4e..8bf461271 100644 --- a/src/templates/settings.html +++ b/src/templates/settings.html @@ -41,15 +41,20 @@

+
+
+ + +
+
-
- +

Your Info:

ip: {{checkProxyResult.ip}}

From 5aba0d6cda41c328989695bfc8a0f21f4fbcfa30 Mon Sep 17 00:00:00 2001 From: PrestonN Date: Wed, 27 Feb 2019 14:14:08 -0500 Subject: [PATCH 14/22] Subscribe to channel without needing to make a new API call --- src/js/subscriptions.js | 41 ++++++++++++++++------------------------- src/js/templates.js | 16 +++++++++++++--- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/js/subscriptions.js b/src/js/subscriptions.js index 8a66b5e3e..e2f5f771d 100644 --- a/src/js/subscriptions.js +++ b/src/js/subscriptions.js @@ -33,26 +33,15 @@ let forceSubs = true; * * @return {Void} */ -function addSubscription(channelId, useToast = true) { - ft.log('Channel ID: ', channelId); +function addSubscription(data, useToast = true) { + ft.log('Channel Data: ', data); - invidiousAPI('channels', channelId, {}, (data) => { - const channelName = data.author; - const thumbnail = data.authorThumbnails[3].url; - - const channel = { - channelId: data.authorId, - channelName: channelName, - channelThumbnail: thumbnail, - }; - - // Refresh the list of subscriptions on the side navigation bar. - subDb.insert(channel, (err, newDoc) => { - if (useToast) { - showToast('Added ' + channelName + ' to subscriptions.'); - displaySubs(); - } - }); + // Refresh the list of subscriptions on the side navigation bar. + subDb.insert(data, (err, newDoc) => { + if (useToast) { + showToast('Added ' + data.channelName + ' to subscriptions.'); + displaySubs(); + } }); } @@ -234,19 +223,21 @@ function displaySubs() { * * @return {Void} */ -function toggleSubscription(channelId) { +function toggleSubscription(data) { event.stopPropagation(); - const checkIfSubscribed = isSubscribed(channelId); + const checkIfSubscribed = isSubscribed(data.channelId); checkIfSubscribed.then((results) => { if (results === false) { - playerView.subscribedText = 'SUBSCRIBE'; - addSubscription(channelId); - } else { playerView.subscribedText = 'UNSUBSCRIBE'; - removeSubscription(channelId); + channelView.subButtonText = 'UNSUBSCRIBE'; + addSubscription(data); + } else { + playerView.subscribedText = 'SUBSCRIBE'; + channelView.subButtonText = 'SUBSCRIBE'; + removeSubscription(data.channelId); } }); } diff --git a/src/js/templates.js b/src/js/templates.js index cf50c0a2c..350acc3dd 100644 --- a/src/js/templates.js +++ b/src/js/templates.js @@ -461,7 +461,12 @@ let channelView = new Vue({ }, methods: { subscription: (channelId) => { - toggleSubscription(channelId); + let channelData = { + channelId: channelView.id, + channelName: channelView.name, + channelThumbnail: channelView.icon + }; + toggleSubscription(channelData); }, }, template: channelTemplate @@ -549,8 +554,13 @@ let playerView = new Vue({ channel: (channelId) => { goToChannel(channelId); }, - subscription: (videoId) => { - toggleSubscription(videoId); + subscription: () => { + let channelData = { + channelId: playerView.channelId, + channelName: playerView.channelName, + channelThumbnail: playerView.channelIcon + }; + toggleSubscription(channelData); }, quality: (url, qualityText) => { console.log(url); From 5d2706848d8599edb3511a592053aa40f896a207 Mon Sep 17 00:00:00 2001 From: PrestonN Date: Thu, 28 Feb 2019 15:24:28 -0500 Subject: [PATCH 15/22] Make History Functionality more Responsive --- src/index.html | 2 +- src/js/events.js | 1 - src/js/history.js | 63 +++++++++++++++++++++++++++---------- src/js/player.js | 22 +++++++++++-- src/js/videos.js | 7 ++++- src/templates/settings.html | 2 +- 6 files changed, 75 insertions(+), 22 deletions(-) diff --git a/src/index.html b/src/index.html index 2d7cd4743..65aab2155 100644 --- a/src/index.html +++ b/src/index.html @@ -13,7 +13,7 @@ - Freetube Player + FreeTube Player diff --git a/src/js/events.js b/src/js/events.js index 66b1d6b81..4d5e3ce35 100644 --- a/src/js/events.js +++ b/src/js/events.js @@ -73,7 +73,6 @@ let playPauseVideo = function (event) { * Handle keyboard shortcut commands. */ let videoShortcutHandler = function (event) { - if (event.which == 68 && event.altKey === true) { $('#search').focus(); } diff --git a/src/js/history.js b/src/js/history.js index da32cd4c8..4df612414 100644 --- a/src/js/history.js +++ b/src/js/history.js @@ -25,22 +25,27 @@ along with FreeTube. If not, see . * * @return {Void} */ -function addToHistory(videoId){ - const data = { - videoId: videoId, - timeWatched: new Date().getTime(), - }; - - historyDb.findOne({ videoId: videoId }, function (err, doc) { +function addToHistory(data){ + historyDb.findOne({ videoId: data.videoId }, function (err, doc) { if(doc === null) { historyDb.insert(data, (err, newDoc) => {}); } else { historyDb.update( - { videoId: videoId }, - { - $set: { - timeWatched: data.timeWatched, - } + { videoId: data.videoId }, + { + videoId: data.videoId, + author: data.author, + authorId: data.authorId, + published: data.published, + publishedText: data.publishedText, + description: data.description, + viewCount: data.viewCount, + title: data.title, + description: data.description, + lengthSeconds: data.lengthSeconds, + videoThumbnails: data.videoThumbnails, + type: 'video', + timeWatched: data.timeWatched, }, {}, (err, newDoc) => {}); } }); @@ -76,10 +81,36 @@ function showHistory(){ timeWatched: -1 }).exec((err, docs) => { docs.forEach((video, index) => { - invidiousAPI('videos', video.videoId, {}, (data) => { - data.position = index; - displayVideo(data, 'history'); - }); + if (video.authorId === undefined) { + // History data is from old version of FreeTube, update data for future calls. + invidiousAPI('videos', video.videoId, {}, (data) => { + let publishedText = new Date(data.published * 1000); + publishedText = dateFormat(publishedText, "mmm dS, yyyy"); + let videoData = { + videoId: video.videoId, + published: data.published, + publishedText: publishedText, + description: data.description, + viewCount: data.viewCount, + title: data.title, + lengthSeconds: data.lengthSeconds, + videoThumbnails: data.videoThumbnails[4].url, + author: data.author, + authorId: data.authorId, + liveNow: false, + paid: false, + type: 'video', + timeWatched: video.timeWatched, + }; + addToHistory(videoData); + videoData.position = index; + displayVideo(videoData, 'history'); + }); + } + else{ + video.position = index; + displayVideo(video, 'history'); + } }); loadingView.seen = false; diff --git a/src/js/player.js b/src/js/player.js index 58fcf46dc..fb35ddc67 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -106,7 +106,7 @@ function playVideo(videoId, playlistId = '') { //playerView.videoUrl = playerView.liveManifest; } - if (!useEmbedPlayer && data.player_response.captions.playerCaptionsTracklistRenderer.captionTracks !== undefined) { + if (!useEmbedPlayer && typeof(data.player_response.captions) !== 'undefined') { data.player_response.captions.playerCaptionsTracklistRenderer.captionTracks.forEach((caption) => { let subtitleUrl = invidiousInstance + '/api/v1/captions/' + videoId + '?label=' + caption.name.simpleText; @@ -240,7 +240,25 @@ function playVideo(videoId, playlistId = '') { } if (rememberHistory === true){ - addToHistory(videoId); + let historyData = { + videoId: videoId, + published: data.published, + publishedText: playerView.publishedDate, + description: data.description, + viewCount: data.viewCount, + title: playerView.videoTitle, + lengthSeconds: data.lengthSeconds, + videoThumbnails: playerView.videoThumbnail, + author: playerView.channelName, + authorId: playerView.channelId, + liveNow: false, + paid: false, + type: 'video', + timeWatched: new Date().getTime(), + }; + + console.log(historyData); + addToHistory(historyData); } }); } diff --git a/src/js/videos.js b/src/js/videos.js index 1fb11837a..5d99703b6 100644 --- a/src/js/videos.js +++ b/src/js/videos.js @@ -184,7 +184,12 @@ function displayVideo(videoData, listType = '') { video.youtubeUrl = 'https://youtube.com/watch?v=' + video.id; video.invidiousUrl = 'https://invidio.us/watch?v=' + video.id; - video.thumbnail = videoData.videoThumbnails[4].url; + if (typeof(videoData.videoThumbnails) === 'string'){ + video.thumbnail = videoData.videoThumbnails; + } + else { + video.thumbnail = videoData.videoThumbnails[4].url; + } video.title = videoData.title; video.channelName = videoData.author; video.channelId = videoData.authorId; diff --git a/src/templates/settings.html b/src/templates/settings.html index 8bf461271..a75d48b4d 100644 --- a/src/templates/settings.html +++ b/src/templates/settings.html @@ -51,7 +51,7 @@
- +
From 8c42fb8caeddf7f26f450417824f1497439f48e6 Mon Sep 17 00:00:00 2001 From: PrestonN Date: Thu, 28 Feb 2019 15:52:47 -0500 Subject: [PATCH 16/22] Make Favorites Functionality more Responsive --- src/js/savedVideos.js | 65 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/src/js/savedVideos.js b/src/js/savedVideos.js index a9d455e50..b01ffb9bd 100644 --- a/src/js/savedVideos.js +++ b/src/js/savedVideos.js @@ -33,14 +33,29 @@ function addSavedVideo(videoId) { checkIfSaved.then((saved) => { if (saved === false) { - let data = { - videoId: videoId, - timeSaved: new Date().getTime(), - }; + invidiousAPI('videos', video.videoId, {}, (data) => { + let publishedText = new Date(data.published * 1000); + publishedText = dateFormat(publishedText, "mmm dS, yyyy"); + let videoData = { + videoId: video.videoId, + published: data.published, + publishedText: publishedText, + description: data.description, + viewCount: data.viewCount, + title: data.title, + lengthSeconds: data.lengthSeconds, + videoThumbnails: data.videoThumbnails[4].url, + author: data.author, + authorId: data.authorId, + liveNow: false, + paid: false, + type: 'video', + }; - savedVidsDb.insert(data, (err, newDoc) => { - showToast('The video has been favorited!'); - }); + savedVidsDb.insert(data, (err, newDoc) => { + showToast('The video has been favorited!'); + }); + }); } else { showToast('The video has already been favorited!') } @@ -122,13 +137,39 @@ function showSavedVideos() { savedVidsDb.find({}).sort({ timeSaved: -1 }).exec((err, docs) => { - let position = 0; - docs.forEach((video) => { + docs.forEach((video, index) => { + if (typeof(video.author) === 'undefined'){ invidiousAPI('videos', video.videoId, {}, (data) => { - data.position = position; - displayVideo(data, 'saved'); - position++; + let publishedText = new Date(data.published * 1000); + publishedText = dateFormat(publishedText, "mmm dS, yyyy"); + let videoData = { + videoId: video.videoId, + published: data.published, + publishedText: publishedText, + description: data.description, + viewCount: data.viewCount, + title: data.title, + lengthSeconds: data.lengthSeconds, + videoThumbnails: data.videoThumbnails[4].url, + author: data.author, + authorId: data.authorId, + liveNow: false, + paid: false, + type: 'video', + }; + + savedVidsDb.update( + { videoId: data.videoId }, + videoData, {}, (err, newDoc) => {}); + + videoData.position = index; + displayVideo(videoData, 'saved'); }); + } + else { + video.position = index; + displayVideo(video, 'saved'); + } }); loadingView.seen = false; From 80de67a747b3bcf05eff5099e9a7450942f9695c Mon Sep 17 00:00:00 2001 From: PrestonN Date: Fri, 1 Mar 2019 10:43:07 -0500 Subject: [PATCH 17/22] Fix Importing of OPML files --- src/js/player.js | 15 +++++++++------ src/js/settings.js | 30 +++++++++++++++++++++++++----- src/js/subscriptions.js | 2 +- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/js/player.js b/src/js/player.js index fb35ddc67..84b34b8a4 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -106,14 +106,17 @@ function playVideo(videoId, playlistId = '') { //playerView.videoUrl = playerView.liveManifest; } - if (!useEmbedPlayer && typeof(data.player_response.captions) !== 'undefined') { - data.player_response.captions.playerCaptionsTracklistRenderer.captionTracks.forEach((caption) => { - let subtitleUrl = invidiousInstance + '/api/v1/captions/' + videoId + '?label=' + caption.name.simpleText; + if (!useEmbedPlayer && + typeof(data.player_response.captions) !== 'undefined' && + typeof(data.player_response.captions.playerCaptionsTracklistRenderer) !== 'undefined' && + typeof(data.player_response.captions.playerCaptionsTracklistRenderer.captionTracks) !== 'undefined') { + data.player_response.captions.playerCaptionsTracklistRenderer.captionTracks.forEach((caption) => { + let subtitleUrl = invidiousInstance + '/api/v1/captions/' + videoId + '?label=' + caption.name.simpleText; - videoHtml = videoHtml + ''; - }); + videoHtml = videoHtml + ''; + }); - playerView.subtitleHtml = videoHtml; + playerView.subtitleHtml = videoHtml; } loadingView.seen = false; diff --git a/src/js/settings.js b/src/js/settings.js index dfdddb0dc..3ab76aa93 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -420,14 +420,34 @@ function importOpmlSubs(json){ return; } - json.forEach((channel) => { + showToast('Importing susbcriptions, please wait.'); + + progressView.seen = true; + progressView.width = 0; + + let counter = 0; + json.forEach((channel, index) => { let channelId = channel['xmlurl'].replace('https://www.youtube.com/feeds/videos.xml?channel_id=', ''); - addSubscription(channelId, false); + invidiousAPI('channels', channelId, {}, (data) => { + let subscription = { + channelId: data.authorId, + channelName: data.author, + channelThumbnail: data.authorThumbnails[2].url + }; + + addSubscription(subscription, false); + counter++; + progressView.progressWidth = (counter / json.length) * 100; + + if ((counter + 1) == json.length) { + showToast('Subscriptions have been imported!'); + progressView.seen = false; + progressView.seen = 0; + return; + } + }); }); - window.setTimeout(displaySubs, 1000); - showToast('Subscriptions have been imported!'); - return; } /** diff --git a/src/js/subscriptions.js b/src/js/subscriptions.js index e2f5f771d..aeeccdddd 100644 --- a/src/js/subscriptions.js +++ b/src/js/subscriptions.js @@ -40,8 +40,8 @@ function addSubscription(data, useToast = true) { subDb.insert(data, (err, newDoc) => { if (useToast) { showToast('Added ' + data.channelName + ' to subscriptions.'); - displaySubs(); } + displaySubs(); }); } From ac5a5d80e4ff835b7f533832b962e3a73a1cf5fb Mon Sep 17 00:00:00 2001 From: PrestonN Date: Fri, 1 Mar 2019 10:46:44 -0500 Subject: [PATCH 18/22] Bump version number to 0.5.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 58ac3669d..b4459f57d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "FreeTube", - "version": "0.4.1", + "version": "0.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c2bf0d3ab..c431f53ea 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "FreeTube", "productName": "FreeTube", - "version": "0.4.1", + "version": "0.5.0", "description": "An Open Source YouTube app for privacy.", "main": "src/js/init.js", "scripts": { From adf89276f1519f6f797af5a40b726aeca7ce963a Mon Sep 17 00:00:00 2001 From: Preston Date: Fri, 1 Mar 2019 19:22:21 +0000 Subject: [PATCH 19/22] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 77c8a9ba7..e17fd0df0 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

FreeTube is an open source desktop YouTube player built with privacy in mind. -Use YouTube without advertisements and prevent Google tracking from you with their cookies and JavaScript. +Use YouTube without advertisements and prevent Google from tracking you with their cookies and JavaScript. Available for Windows, Mac & Linux thanks to Electron. Please note that FreeTube is currently in Beta. While it should work well for @@ -32,7 +32,7 @@ to hide your IP while using FreeTube. ## Features * Watch videos without ads * Use YouTube without Google tracking you using cookies and JavaScript -* Make API calls through the Tor network +* Tor / Proxy Support * Subscribe to channels without an account * Local subscriptions, history, and saved videos * Export & import subscriptions From 4f47a8fe23cc71a208089c193a7d90927a58d042 Mon Sep 17 00:00:00 2001 From: PrestonN Date: Fri, 1 Mar 2019 15:21:28 -0500 Subject: [PATCH 20/22] Fixed Favoriting videos from videoView and playerView --- src/js/savedVideos.js | 7 ++++--- src/js/templates.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/js/savedVideos.js b/src/js/savedVideos.js index b01ffb9bd..4661d519b 100644 --- a/src/js/savedVideos.js +++ b/src/js/savedVideos.js @@ -33,11 +33,11 @@ function addSavedVideo(videoId) { checkIfSaved.then((saved) => { if (saved === false) { - invidiousAPI('videos', video.videoId, {}, (data) => { + invidiousAPI('videos', videoId, {}, (data) => { let publishedText = new Date(data.published * 1000); publishedText = dateFormat(publishedText, "mmm dS, yyyy"); let videoData = { - videoId: video.videoId, + videoId: videoId, published: data.published, publishedText: publishedText, description: data.description, @@ -50,9 +50,10 @@ function addSavedVideo(videoId) { liveNow: false, paid: false, type: 'video', + timeSaved: new Date().getTime(), }; - savedVidsDb.insert(data, (err, newDoc) => { + savedVidsDb.insert(videoData, (err, newDoc) => { showToast('The video has been favorited!'); }); }); diff --git a/src/js/templates.js b/src/js/templates.js index 350acc3dd..64b7f88de 100644 --- a/src/js/templates.js +++ b/src/js/templates.js @@ -589,7 +589,7 @@ let playerView = new Vue({ showToast('URL has been copied to the clipboard'); }, save: (videoId) => { - addSavedVideo(videoId); + toggleSavedVideo(videoId); }, play: (videoId, playlistId = '') => { loadingView.seen = true; From 0dc5caaf6558f91f965217ba8cd22246a8062f82 Mon Sep 17 00:00:00 2001 From: ivan1548 <47206132+ivan1548@users.noreply.github.com> Date: Fri, 1 Mar 2019 16:12:52 -0800 Subject: [PATCH 21/22] Hide loading view after clicking on back button (#226) --- src/js/templates.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/templates.js b/src/js/templates.js index 64b7f88de..8677384a8 100644 --- a/src/js/templates.js +++ b/src/js/templates.js @@ -645,6 +645,7 @@ let backButtonView = new Vue({ const isSearch = this.lastView.$options.el === "#searchView"; hideViews(); + loadingView.seen = false; // Check if lastView was search if(isSearch) { From dd7bbda0cb05b2592c5b399019f45b1d0e9b93f1 Mon Sep 17 00:00:00 2001 From: Preston Date: Fri, 1 Mar 2019 19:26:14 -0500 Subject: [PATCH 22/22] Hide Views and Post Date on Premium content in a search --- src/js/videos.js | 3 +-- src/templates/videoTemplate.html | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/js/videos.js b/src/js/videos.js index 5d99703b6..c9bfd736f 100644 --- a/src/js/videos.js +++ b/src/js/videos.js @@ -170,8 +170,6 @@ function displayVideo(videoData, listType = '') { } } - //const searchMenu = $('#videoListContainer').html(); - // Include a remove icon in the list if the application is displaying the history list or saved videos. video.deleteHtml = () => { switch (listType) { @@ -195,6 +193,7 @@ function displayVideo(videoData, listType = '') { video.channelId = videoData.authorId; video.description = videoData.description; video.isVideo = true; + video.premium = videoData.premium; switch (listType) { case 'subscriptions': diff --git a/src/templates/videoTemplate.html b/src/templates/videoTemplate.html index 92238a641..b20a91cee 100644 --- a/src/templates/videoTemplate.html +++ b/src/templates/videoTemplate.html @@ -29,7 +29,7 @@

{{video.title}}

{{video.channelName}}

-

{{video.views}} {{video.viewText}} - {{video.publishedDate}}

+

{{video.views}} {{video.viewText}} - {{video.publishedDate}}

{{video.description}}

{{video.liveText}}