/* This file is part of FreeTube. FreeTube is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. FreeTube is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with FreeTube. If not, see . */ const mainHeaderTemplate = require('./templates/mainHeader.html'); const aboutTemplate = require('./templates/about.html'); const settingsTemplate = require('./templates/settings.html'); const videoListTemplate = require('./templates/videoTemplate.html'); const playerTemplate = require('./templates/player.html'); const channelTemplate = require('./templates/channelView.html'); const progressViewTemplate = require('./templates/progressView.html'); const playlistViewTemplate = require('./templates/playlistView.html'); /* * Progress view * * Shows progress bar on bottom of application. * * seen: Toggles visibility of view * progressWidth: sets width of the progress bar */ let progressView = new Vue({ el: '#progressView', data: { seen: true, progressWidth: 0 }, template: progressViewTemplate }); let loadingView = new Vue({ el: '#loading', data: { seen: false } }); let searchFilter = new Vue({ el: '#searchFilter', data: { seen: false } }); let noSubscriptions = new Vue({ el: '#noSubscriptions', data: { seen: false } }); let sideNavBar = new Vue({ el: '#sideNav', data: { distractionFreeMode: false }, methods: { subscriptions: (event) => { hideViews(); if(subscriptionView.videoList.length === 0){ loadingView.seen = true; } headerView.seen = true; headerView.title = 'Latest Subscriptions'; subscriptionView.seen = true; loadSubscriptions(); }, popular: (event) => { hideViews(); if (loadingView.seen !== false){ loadingView.seen = false; } if(popularView.videoList.length === 0){ loadingView.seen = true; } headerView.seen = true; headerView.title = 'Most Popular'; popularView.seen = true; showMostPopular(); }, trending: (event) => { hideViews(); if (loadingView.seen !== false){ loadingView.seen = false; } if(trendingView.videoList.length === 0){ loadingView.seen = true; } headerView.seen = true; headerView.title = 'Trending'; trendingView.seen = true; showTrending(); }, saved: (event) => { hideViews(); if (loadingView.seen !== false){ loadingView.seen = false; } else{ loadingView.seen = true; } headerView.seen = true; headerView.title = 'Favorited Videos'; savedView.seen = true; showSavedVideos(); }, history: (event) => { hideViews(); if (loadingView.seen !== false){ loadingView.seen = false; } else{ loadingView.seen = true; } headerView.seen = true; headerView.title = 'Video History'; historyView.seen = true; showHistory(); }, settings: (event) => { hideViews(); if (loadingView.seen !== false){ loadingView.seen = false; } settingsView.seen = true; updateSettingsView(); }, about: (event) => { hideViews(); if (loadingView.seen !== false){ loadingView.seen = false; } aboutView.seen = true; } } }); let headerView = new Vue({ el: '#mainHeaderView', data: { seen: true, title: 'Latest Subscriptions' }, template: mainHeaderTemplate }); let subscriptionView = new Vue({ el: '#subscriptionView', data: { seen: true, isSearch: false, videoList: [], }, methods: { play: (videoId) => { loadingView.seen = true; playVideo(videoId); }, channel: (channelId) => { goToChannel(channelId); }, toggleSave: (videoId) => { addSavedVideo(videoId); }, copyYouTube: (videoId) => { const url = 'https://youtube.com/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, copyInvidious: (videoId) => { const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, history: (videoId) => { removeFromHistory(videoId); }, miniPlayer: (videoId) => { ft.log(videoId); clickMiniPlayer(videoId); } }, template: videoListTemplate }); let popularView = new Vue({ el: '#popularView', data: { seen: false, isSearch: false, videoList: [] }, methods: { play: (videoId) => { loadingView.seen = true; playVideo(videoId); }, channel: (channelId) => { goToChannel(channelId); }, toggleSave: (videoId) => { addSavedVideo(videoId); }, copyYouTube: (videoId) => { const url = 'https://youtube.com/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, copyInvidious: (videoId) => { const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, history: (videoId) => { removeFromHistory(videoId); }, miniPlayer: (videoId) => { clickMiniPlayer(videoId); } }, template: videoListTemplate }); let trendingView = new Vue({ el: '#trendingView', data: { seen: false, isSearch: false, videoList: [] }, methods: { play: (videoId) => { loadingView.seen = true; playVideo(videoId); }, channel: (channelId) => { goToChannel(channelId); }, toggleSave: (videoId) => { addSavedVideo(videoId); }, copyYouTube: (videoId) => { const url = 'https://youtube.com/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, copyInvidious: (videoId) => { const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, history: (videoId) => { removeFromHistory(videoId); }, miniPlayer: (videoId) => { clickMiniPlayer(videoId); } }, template: videoListTemplate }); let savedView = new Vue({ el: '#savedView', data: { seen: false, isSearch: false, videoList: [] }, methods: { play: (videoId) => { loadingView.seen = true; playVideo(videoId); }, channel: (channelId) => { goToChannel(channelId); }, toggleSave: (videoId) => { toggleSavedVideo(videoId); }, copyYouTube: (videoId) => { const url = 'https://youtube.com/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, copyInvidious: (videoId) => { const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, history: (videoId) => { removeFromHistory(videoId); }, miniPlayer: (videoId) => { clickMiniPlayer(videoId); } }, template: videoListTemplate }); let historyView = new Vue({ el: '#historyView', data: { seen: false, isSearch: false, videoList: [] }, methods: { play: (videoId) => { loadingView.seen = true; playVideo(videoId); }, channel: (channelId) => { goToChannel(channelId); }, toggleSave: (videoId) => { addSavedVideo(videoId); }, copyYouTube: (videoId) => { const url = 'https://youtube.com/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, copyInvidious: (videoId) => { const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, history: (videoId) => { removeFromHistory(videoId); }, miniPlayer: (videoId) => { clickMiniPlayer(videoId); } }, template: videoListTemplate }); let playlistView = new Vue({ el: '#playlistView', data: { seen: false, playlistId: '', channelName: '', channelId: '', thumbnail: '', title: '', videoCount: '', viewCount: '', description: '', lastUpdated: '', videoList: [] }, methods: { play: (videoId) => { loadingView.seen = true; playVideo(videoId, playlistView.playlistId); backButtonView.lastView = playlistView }, channel: (channelId) => { goToChannel(channelId); backButtonView.lastView = playlistView }, toggleSave: (videoId) => { addSavedVideo(videoId); }, copyYouTube: (videoId) => { const url = 'https://youtube.com/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, copyInvidious: (videoId) => { const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, history: (videoId) => { removeFromHistory(videoId); } }, template: playlistViewTemplate }); let aboutView = new Vue({ el: '#aboutView', data: { seen: false, rssFeed: [], versionNumber: electron.remote.app.getVersion() }, template: aboutTemplate }); let settingsView = new Vue({ el: '#settingsView', data: { seen: false, useTheme: false, useTor: false, apiKey: '', history: true, autoplay: true, subtitles: false, updates: true, localScrape: true, region: 'US', proxyAddress: false, invidiousInstance: 'https://invidio.us', checkProxyResult: false, proxyTestLoading: false, debugMode: false, distractionFreeMode: false }, methods: { checkProxy() { this.checkProxyResult = false; this.proxyTestLoading = true; electron.ipcRenderer.send("setProxy", this.proxyAddress); proxyRequest(() => { $.ajax({ url: "https://ipinfo.io/json", dataType: 'json', }).done(response => { console.log(response); this.checkProxyResult = response; }) .fail((xhr, textStatus, error) => { console.log(xhr); console.log(textStatus); showToast('Proxy test failed'); }).always(() =>{ this.proxyTestLoading = false; if (!useTor) { electron.ipcRenderer.send("setProxy", {}); } }); }) }, setDistractionFreeMode(setting) { settingsView.distractionFreeMode = setting; sideNavBar.distractionFreeMode = setting; channelView.distractionFreeMode = setting; playerView.distractionFreeMode = setting; }, }, computed: { proxyTestButtonText() { return this.proxyTestLoading ? "LOADING..." : "TEST PROXY" } }, template: settingsTemplate }); let searchView = new Vue({ el: '#searchView', data: { seen: false, isSearch: true, page: 1, videoList: [] }, methods: { play: (videoId) => { loadingView.seen = true; playVideo(videoId); backButtonView.lastView = searchView }, channel: (channelId) => { goToChannel(channelId); backButtonView.lastView = searchView }, toggleSave: (videoId) => { addSavedVideo(videoId); }, copyYouTube: (videoId) => { const url = 'https://youtube.com/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, copyInvidious: (videoId) => { const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, nextPage: () => { ft.log(searchView.page); search(searchView.page); }, playlist: (playlistId) => { showPlaylist(playlistId); backButtonView.lastView = searchView }, miniPlayer: (videoId) => { clickMiniPlayer(videoId); } }, template: videoListTemplate }); let channelView = new Vue({ el: '#channelView', data: { seen: false, id: '', name: '', icon: '', baner: '', subCount: '', subButtonText: '', description: '', distractionFreeMode: false }, methods: { subscription: (channelId) => { let channelData = { channelId: channelView.id, channelName: channelView.name, channelThumbnail: channelView.icon }; toggleSubscription(channelData); }, }, template: channelTemplate }); let channelVideosView = new Vue({ el: '#channelVideosView', data: { seen: false, channelId: '', isSearch: true, page: 2, videoList: [] }, methods: { play: (videoId) => { loadingView.seen = true; playVideo(videoId); }, channel: (channelId) => { goToChannel(channelId); }, toggleSave: (videoId) => { addSavedVideo(videoId); }, nextPage: () => { channelNextPage(); }, copyYouTube: (videoId) => { const url = 'https://youtube.com/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, copyInvidious: (videoId) => { const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, history: (videoId) => { removeFromHistory(videoId); }, miniPlayer: (videoId) => { clickMiniPlayer(videoId); } }, template: videoListTemplate }); let playerView = new Vue({ el: '#playerView', data: { seen: false, playlistSeen: false, legacySeen: false, firstLoad: true, currentTime: undefined, publishedDate: '', videoUrl: '', videoId: '', channelId: '', channelIcon: '', channelName: '', subscribedText: '', savedText: '', savedIconType: 'far', description: '', videoThumbnail: '', subtitleHtml: '', currentQuality: '', videoAudio: '', validAudio: false, video360p: '', valid360p: false, video720p: '', valid720p: false, videoDash: '', validDash: true, videoLive: '', validLive: false, embededHtml: '', currentSpeed: 1, lengthSeconds: 0, videoTitle: '', videoViews: '', likePercentage: 0, videoLikes: 0, videoDislikes: 0, playerSeen: true, playlistTitle: '', playlistChannelName: '', playlistIndex: 1, playlistTotal: 1, playlistLoop: false, playlistShuffle: false, playlistShowList: true, recommendedVideoList: [], playlistVideoList: [], distractionFreeMode: false }, methods: { channel: (channelId) => { goToChannel(channelId); }, subscription: () => { let channelData = { channelId: playerView.channelId, channelName: playerView.channelName, channelThumbnail: playerView.channelIcon }; toggleSubscription(channelData); }, quality: (url, qualityText) => { ft.log(url); ft.log(qualityText); if(playerView.legacySeen === true){ // Update time to new url const currentPlayBackTime = $('.videoPlayer').get(0).currentTime; ft.log(currentPlayBackTime); playerView.videoUrl = url; playerView.currentQuality = qualityText; setTimeout(() => {$('.videoPlayer').get(0).currentTime = currentPlayBackTime; $('.videoPlayer').get(0).play();}, 100); } }, embededPlayer: () => { playerView.playerSeen = false; playerView.legacySeen = false; playerView.currentTime = undefined; checkedSettings = false; }, legacyFormats: () => { if (typeof(player) !== 'undefined') { playerView.currentTime = player.currentTime; } checkedSettings = false; playerView.playerSeen = false; playerView.legacySeen = true; }, dashFormats: () => { if (typeof($('#legacyPlayer').get(0)) !== 'undefined') { playerView.currentTime = $('#legacyPlayer').get(0).currentTime; } playerView.legacySeen = false; checkedSettings = false; playerView.playerSeen = true; }, copyYouTube: (videoId) => { const url = 'https://youtube.com/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, openYouTube: (videoId) => { shell.openExternal('https://youtube.com/watch?v=' + videoId); }, copyInvidious: (videoId) => { const url = invidiousInstance + '/watch?v=' + videoId; clipboard.writeText(url); showToast('URL has been copied to the clipboard'); }, openInvidious: (videoId) => { shell.openExternal(invidiousInstance + '/watch?v=' + videoId); }, save: (videoId) => { toggleSavedVideo(videoId); }, play: (videoId, playlistId = '') => { loadingView.seen = true; playVideo(videoId, playlistId); }, loop: () => { let legacyPlayer = $('.videoPlayer').get(0); if (legacyPlayer.loop === false) { legacyPlayer.loop = true; showToast('Video loop has been turned on.'); } else{ legacyPlayer.loop = false; showToast('Video loop has been turned off.') } }, playlist: (playlistId) => { showPlaylist(playlistId); }, playlistLoopToggle: () => { if (playerView.playlistLoop !== false) { showToast('Playlist will no longer loop'); playerView.playlistLoop = false; } else { showToast('Playlist will now loop'); playerView.playlistLoop = true; } }, playlistShuffleToggle: () => { if (playerView.playlistShuffle !== false) { showToast('Playlist will no longer shuffle'); playerView.playlistShuffle = false; } else{ showToast('Playlist will now shuffle'); playerView.playlistShuffle = true; } }, }, computed: { thumbnailInterval: function() { if (this.lengthSeconds < 120) { return 1; } else if (this.lengthSeconds < 300) { return 2; } else if (this.lengthSeconds < 900) { return 5; } else { return 10; } }, storyBoardUrl: function() { return invidiousInstance + '/api/v1/storyboards/' + this.videoId + '?height=90'; } }, template: playerTemplate }); let backButtonView = new Vue({ el: '#backButton', data: { lastView: false }, methods: { back: function() { // variable here because this.lastView gets reset in hideViews() const isSearch = this.lastView.$options.el === "#searchView"; hideViews(); loadingView.seen = false; // Check if lastView was search if(isSearch) { // Change back to searchView headerView.seen = true; headerView.title = 'Search Results'; searchView.seen = true; // reset this.lastView this.lastView = false; } else { // if not search then this.lastView has to be playlistView // Change back to playlistView playlistView.seen = true; // Check if searchView has videos if it does set this.lastView as searchView this.lastView = searchView.videoList.length > 0 ? searchView : false; } } }, computed: { canShowBackButton: function() { // this.lastView can be either searchView or playlistView return !!this.lastView && !this.lastView.seen && this.lastView.videoList.length > 0; } }, }); function hideViews() { if (playerView.seen !== false) { let lengthSeconds = 0; let duration = 0; if (playerView.legacySeen === false) { lengthSeconds = player.currentTime; duration = player.duration; } else { lengthSeconds = $('.videoPlayer').get(0).currentTime; duration = $('.videoPlayer').get(0).duration; } updateWatchProgress(playerView.videoId, lengthSeconds); let videoIndex = subscriptionView.videoList.findIndex(x => x.id === playerView.videoId); if (videoIndex !== -1) { subscriptionView.videoList[videoIndex].watched = true; subscriptionView.videoList[videoIndex].progressPercentage = (lengthSeconds / duration) * 100; } } subscriptionView.seen = false; noSubscriptions.seen = false; aboutView.seen = false; headerView.seen = false; searchView.seen = false; settingsView.seen = false; popularView.seen = false; trendingView.seen = false; savedView.seen = false; historyView.seen = false; playlistView.seen = false; playerView.seen = false; channelView.seen = false; channelVideosView.seen = false; backButtonView.lastView = false; }