Commit 5fd154fa authored by Preston's avatar Preston
Browse files

Merge branch 'development'

parents cc069994 0cd8dc6f
......@@ -147,7 +147,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
asset_name: freetube-${{ env.PACKAGE_VERSION }}-win-x64-portable
asset_name: freetube-${{ env.PACKAGE_VERSION }}-win-x64-portable.zip
asset_path: build/freetube-${{ env.PACKAGE_VERSION }}-win.zip
asset_content_type: application/zip
......
......@@ -20,3 +20,4 @@ coverage
__coverage__
csak-timelog.json
.idea/
debug/
......@@ -14,11 +14,11 @@ addressed.
### Browser Extension
Download our browser extension so that you can click on videos in your browser and have them automatically open up in FreeTube.
FreeTube is supported by the [Privacy Redirect](https://github.com/SimonBrazell/privacy-redirect) extension, which will allow you to open YouTube links into FreeTube. You must enable the option within the advanced settings for it to work.
[Download for Firefox](https://addons.mozilla.org/en-US/firefox/addon/freetube-redirect/)
Download Privacy Redirect for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/privacy-redirect/) or [Google Chrome](https://chrome.google.com/webstore/detail/privacy-redirect/pmcmeagblkinmogikoikkdjiligflglb).
[Instructions for Google Chrome](https://github.com/FreeTubeApp/FreeTube/wiki/Browser-Extension)
If you have issues with the extension working with FreeTube, please create an issue in this repository instead of the extension repository.
## How does it work?
FreeTube uses a built in extractor to grab and serve data / videos. The [Invidious API](https://github.com/iv-org/invidious) can also optionally be used. FreeTube does not use any official APIs to obtain data. While YouTube can still see your video requests, it can no
......@@ -34,6 +34,7 @@ to hide your IP while using FreeTube.
* Two extractor APIs to choose from (Built in or Invidious)
* Subscribe to channels without an account
* Local subscriptions, history, and saved videos
* Organize your subscriptions into "Profiles" to create a more focused feed
* Export & import subscriptions
* Open videos from your browser directly into FreeTube (with extension)
* Mini Player
......@@ -47,12 +48,12 @@ to hide your IP while using FreeTube.
[FreeTube Website](https://freetubeapp.io/#download)
Flatpak on Flathub: [Download](https://flathub.org/apps/details/io.freetubeapp.FreeTube) [Source](https://github.com/flathub/io.freetubeapp.FreeTube)
### Unofficial Downloads
These builds are maintained by the community. While they should be safe, download at your own risk. There may be issues with using these versus the official builds. Any issues specific with these builds should be sent to their respective maintainer.
Flatpak on Flathub: [Download](https://flathub.org/apps/details/io.freetubeapp.FreeTube) [Source](https://github.com/flathub/io.freetubeapp.FreeTube)
Arch User Repository (AUR): [Download](https://aur.archlinux.org/packages/freetube-bin/)
Chocolatey: [Download](https://chocolatey.org/packages/freetube/)
......@@ -87,7 +88,7 @@ We are actively looking for translations! We use Weblate to make it easy for tr
## Contact
If you ever have any questions, feel free to make an issue here on GitHub. Alternatively, you can email me at FreeTubeApp@protonmail.com or you can join our [Matrix Community](https://riot.im/app/#/group/+freetube:matrix.org). Don't forget to check out the [rules](https://github.com/FreeTubeApp/FreeTube/wiki/Matrix-Server-Info-&-Rules) before joining.
If you ever have any questions, feel free to make an issue here on GitHub. Alternatively, you can email me at FreeTubeApp@protonmail.com or you can join our [Matrix Community](https://matrix.to/#/+freetube:matrix.org). Don't forget to check out the [rules](https://github.com/FreeTubeApp/FreeTube/wiki/Matrix-Server-Info-&-Rules) before joining.
You can also stay up to date by reading the [FreeTube Blog](https://write.as/freetube/). [View the welcome blog](https://write.as/freetube/welcome-to-freetube-blogs).
......
This diff is collapsed.
......@@ -8,23 +8,24 @@
"url": "https://github.com/FreeTubeApp/FreeTube/issues"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.32",
"@fortawesome/free-solid-svg-icons": "^5.15.1",
"@fortawesome/vue-fontawesome": "^2.0.0",
"@fortawesome/fontawesome-svg-core": "^1.2.34",
"@fortawesome/free-brands-svg-icons": "^5.15.2",
"@fortawesome/free-solid-svg-icons": "^5.15.2",
"@fortawesome/vue-fontawesome": "^2.0.2",
"@silvermine/videojs-quality-selector": "^1.2.4",
"autolinker": "^3.14.2",
"bulma-pro": "^0.2.0",
"dateformat": "^4.3.1",
"electron-context-menu": "^2.3.0",
"dateformat": "^4.4.1",
"electron-context-menu": "^2.4.0",
"jquery": "^3.5.1",
"js-yaml": "^3.14.1",
"js-yaml": "^4.0.0",
"lodash.debounce": "^4.0.8",
"lodash.isequal": "^4.5.0",
"lodash.uniqwith": "^4.5.0",
"markdown": "^0.5.0",
"material-design-icons": "^3.0.1",
"nedb": "^1.8.0",
"opml-to-json": "0.0.3",
"opml-to-json": "1.0.1",
"rss-parser": "^3.10.0",
"video.js": "7.10.2",
"videojs-abloop": "^1.2.0",
......@@ -35,74 +36,74 @@
"videojs-vtt-thumbnails-freetube": "0.0.15",
"vue": "^2.6.12",
"vue-electron": "^1.0.6",
"vue-i18n": "^8.22.2",
"vue-observe-visibility": "^0.4.6",
"vue-i18n": "^8.22.3",
"vue-observe-visibility": "^1.0.0",
"vue-router": "^3.4.9",
"vuex": "^3.6.0",
"xml2json": "^0.12.0",
"youtube-chat": "^1.1.0",
"youtube-suggest": "^1.1.0",
"yt-channel-info": "^1.1.4",
"yt-comment-scraper": "^1.3.10",
"yt-channel-info": "^1.2.0",
"yt-comment-scraper": "^1.3.11",
"yt-dash-manifest-generator": "^1.1.0",
"yt-trending-scraper": "^1.0.4",
"yt-xml2vtt": "^1.1.3",
"ytdl-core": "^4.2.0",
"ytpl": "^2.0.3",
"ytsr": "^3.0.0"
"yt-trending-scraper": "^1.0.5",
"yt-xml2vtt": "^1.2.0",
"ytdl-core": "^4.4.3",
"ytpl": "^2.0.4",
"ytsr": "^3.2.1"
},
"description": "A private YouTube client",
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-object-rest-spread": "^7.12.1",
"@babel/preset-env": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/preset-typescript": "^7.12.7",
"@typescript-eslint/eslint-plugin": "^4.10.0",
"@typescript-eslint/parser": "^4.10.0",
"@typescript-eslint/eslint-plugin": "^4.13.0",
"@typescript-eslint/parser": "^4.13.0",
"acorn": "^8.0.4",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.2.2",
"copy-webpack-plugin": "6.4.0",
"css-loader": "^5.0.1",
"devtron": "^1.4.0",
"electron": "^11.1.0",
"electron": "^11.1.1",
"electron-builder": "^22.9.1",
"electron-builder-squirrel-windows": "^22.10.3",
"electron-debug": "^3.1.0",
"electron-builder-squirrel-windows": "^22.10.4",
"electron-debug": "^3.2.0",
"electron-rebuild": "^2.3.4",
"eslint": "^7.15.0",
"eslint-config-prettier": "^7.0.0",
"eslint": "^7.17.0",
"eslint-config-prettier": "^7.1.0",
"eslint-config-standard": "^16.0.2",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.3.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^5.0.0",
"eslint-plugin-vue": "^7.2.0",
"eslint-plugin-vue": "^7.4.1",
"fast-glob": "^3.2.4",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^4.5.0",
"html-webpack-plugin": "^4.5.1",
"jest": "^26.6.3",
"mini-css-extract-plugin": "^1.3.3",
"mini-css-extract-plugin": "^1.3.4",
"node-abi": "^2.19.3",
"node-loader": "^1.0.2",
"npm-run-all": "^4.1.5",
"prettier": "^2.2.1",
"sass": "^1.30.0",
"sass-loader": "^10.1.0",
"sass": "^1.32.4",
"sass-loader": "^10.1.1",
"style-loader": "^2.0.0",
"tree-kill": "1.2.2",
"typescript": "^4.1.3",
"url-loader": "^4.1.1",
"vue-devtools": "^5.1.4",
"vue-eslint-parser": "^7.3.0",
"vue-loader": "^15.9.5",
"vue-loader": "^15.9.6",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.12",
"webpack": "4.44.2",
"webpack-cli": "3.3.12",
"webpack-dev-server": "^3.11.0"
"webpack-dev-server": "^3.11.2"
},
"license": "AGPL-3.0-or-later",
"main": "./dist/main.js",
......@@ -142,5 +143,5 @@
"test": "run-s rebuild:node pack:workers jest",
"test:watch": "run-s rebuild:node pack:workers jest:watch"
},
"version": "0.10.0"
"version": "0.11.0"
}
......@@ -65,21 +65,57 @@ if (!isDev) {
})
app.on('ready', (event, commandLine, workingDirectory) => {
settingsDb.findOne({
_id: 'disableSmoothScrolling'
settingsDb.find({
$or: [
{ _id: 'disableSmoothScrolling' },
{ _id: 'useProxy' },
{ _id: 'proxyProtocol' },
{ _id: 'proxyHostname' },
{ _id: 'proxyPort' }
]
}, function (err, doc) {
if (err) {
app.exit(0)
return
}
if (doc !== null && doc.value) {
let disableSmoothScrolling = false
let useProxy = false
let proxyProtocol = 'socks5'
let proxyHostname = '127.0.0.1'
let proxyPort = '9050'
if (typeof doc === 'object' && doc.length > 0) {
doc.forEach((dbItem) => {
switch (dbItem._id) {
case 'disableSmoothScrolling':
disableSmoothScrolling = dbItem.value
break
case 'useProxy':
useProxy = dbItem.value
break
case 'proxyProtocol':
proxyProtocol = dbItem.value
break
case 'proxyHostname':
proxyHostname = dbItem.value
break
case 'proxyPort':
proxyPort = dbItem.value
break
}
})
}
if (disableSmoothScrolling) {
app.commandLine.appendSwitch('disable-smooth-scrolling')
} else {
app.commandLine.appendSwitch('enable-smooth-scrolling')
}
createWindow()
const proxyUrl = `${proxyProtocol}://${proxyHostname}:${proxyPort}`
createWindow(useProxy, proxyUrl)
if (isDev) {
installDevTools()
......@@ -99,21 +135,57 @@ if (!isDev) {
})
app.on('ready', () => {
settingsDb.findOne({
_id: 'disableSmoothScrolling'
settingsDb.find({
$or: [
{ _id: 'disableSmoothScrolling' },
{ _id: 'useProxy' },
{ _id: 'proxyProtocol' },
{ _id: 'proxyHostname' },
{ _id: 'proxyPort' }
]
}, function (err, doc) {
if (err) {
app.exit(0)
return
}
if (doc !== null && doc.value) {
let disableSmoothScrolling = false
let useProxy = false
let proxyProtocol = 'socks5'
let proxyHostname = '127.0.0.1'
let proxyPort = '9050'
if (typeof doc === 'object' && doc.length > 0) {
doc.forEach((dbItem) => {
switch (dbItem._id) {
case 'disableSmoothScrolling':
disableSmoothScrolling = dbItem.value
break
case 'useProxy':
useProxy = dbItem.value
break
case 'proxyProtocol':
proxyProtocol = dbItem.value
break
case 'proxyHostname':
proxyHostname = dbItem.value
break
case 'proxyPort':
proxyPort = dbItem.value
break
}
})
}
if (disableSmoothScrolling) {
app.commandLine.appendSwitch('disable-smooth-scrolling')
} else {
app.commandLine.appendSwitch('enable-smooth-scrolling')
}
createWindow()
const proxyUrl = `${proxyProtocol}://${proxyHostname}:${proxyPort}`
createWindow(useProxy, proxyUrl)
if (isDev) {
installDevTools()
......@@ -137,7 +209,7 @@ async function installDevTools () {
}
}
function createWindow () {
function createWindow (useProxy = false, proxyUrl = '') {
/**
* Initial window options
*/
......@@ -164,6 +236,12 @@ function createWindow () {
height: 800
})
if (useProxy) {
mainWindow.webContents.session.setProxy({
proxyRules: proxyUrl
})
}
settingsDb.findOne({
_id: 'bounds'
}, function (err, doc) {
......@@ -264,6 +342,17 @@ function createWindow () {
mainWindow.close()
createWindow()
})
ipcMain.on('enableProxy', (event, url) => {
console.log(url)
mainWindow.webContents.session.setProxy({
proxyRules: url
})
})
ipcMain.on('disableProxy', () => {
mainWindow.webContents.session.setProxy({})
})
}
app.on('window-all-closed', () => {
......@@ -306,7 +395,8 @@ app.on('open-url', (event, url) => {
})
/*
* Check if we were passed a freetube:// URL on process startup (linux/win)
* Check if an argument was passed and send it over to the GUI (Linux / Windows).
* Remove freetube:// protocol if present
*/
const url = getLinkUrl(process.argv)
if (url) {
......@@ -318,12 +408,11 @@ function baseUrl(arg) {
}
function getLinkUrl(argv) {
for (const arg of argv) {
if (arg.indexOf('freetube://') !== -1) {
return baseUrl(arg)
}
if (argv.length > 1) {
return baseUrl(argv[argv.length - 1])
} else {
return null
}
return null
}
/**
......
......@@ -88,6 +88,7 @@ export default Vue.extend({
this.$store.dispatch('grabUserSettings')
this.$store.dispatch('grabHistory')
this.$store.dispatch('grabAllProfiles', this.$t('Profile.All Channels'))
this.$store.dispatch('grabAllPlaylists')
this.$store.commit('setUsingElectron', useElectron)
this.checkThemeSettings()
this.checkLocale()
......@@ -172,7 +173,15 @@ export default Vue.extend({
const message = this.$t('Version $ is now available! Click for more details')
this.updateBannerMessage = message.replace('$', versionNumber)
if (version < versionNumber) {
const appVersion = version.split('.')
const latestVersion = versionNumber.split('.')
if (parseInt(appVersion[0]) < parseInt(latestVersion[0])) {
this.showUpdatesBanner = true
} else if (parseInt(appVersion[1]) < parseInt(latestVersion[1])) {
this.showUpdatesBanner = true
} else if (parseInt(appVersion[2]) < parseInt(latestVersion[2])) {
this.showUpdatesBanner = true
}
}).fail((xhr, textStatus, error) => {
......
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="20px" height="20px"><path class="loop" d="M12 4V2.21c0-.45-.54-.67-.85-.35l-2.8 2.79c-.2.2-.2.51 0 .71l2.79 2.79c.32.31.86.09.86-.36V6c3.31 0 6 2.69 6 6 0 .79-.15 1.56-.44 2.25-.15.36-.04.77.23 1.04.51.51 1.37.33 1.64-.34.37-.91.57-1.91.57-2.95 0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-.79.15-1.56.44-2.25.15-.36.04-.77-.23-1.04-.51-.51-1.37-.33-1.64.34C4.2 9.96 4 10.96 4 12c0 4.42 3.58 8 8 8v1.79c0 .45.54.67.85.35l2.79-2.79c.2-.2.2-.51 0-.71l-2.79-2.79c-.31-.31-.85-.09-.85.36V18z"/></svg>
......@@ -78,6 +78,12 @@ export default Vue.extend({
}
},
methods: {
openProfileSettings: function () {
this.$router.push({
path: '/settings/profile/'
})
},
importSubscriptions: function (option) {
this.showImportSubscriptionsPrompt = false
......@@ -265,7 +271,11 @@ export default Vue.extend({
return sub.id === subscription.id || sub.name === subscription.name
})
if (subExists === -1) {
const subDuplicateExists = subscriptions.findIndex((sub) => {
return sub.id === subscription.id || sub.name === subscription.name
})
if (subExists === -1 && subDuplicateExists === -1) {
subscriptions.push(subscription)
}
......
......@@ -27,6 +27,22 @@
@click="exportHistory"
/>
</ft-flex-box>
<ft-flex-box>
<a
class="center"
href="https://github.com/FreeTubeApp/FreeTube/wiki/Importing-Your-YouTube-Subscriptions"
>
<p>
{{ $t("Settings.Data Settings.How do I import my subscriptions?") }}
</p>
</a>
</ft-flex-box>
<ft-flex-box>
<ft-button
:label="$t('Settings.Data Settings.Manage Subscriptions')"
@click="openProfileSettings"
/>
</ft-flex-box>
<ft-prompt
v-if="showImportSubscriptionsPrompt"
:label="$t('Settings.Data Settings.Select Import Type')"
......
......@@ -39,13 +39,15 @@ export default Vue.extend({
},
hideLiveChat: function () {
return this.$store.getters.getHideLiveChat
},
hideActiveSubscriptions: function () {
return this.$store.getters.getHideActiveSubscriptions
}
},
methods: {
handleHideRecommendedVideos: function (value) {
if (value) {
this.updatePlayNextVideo(false)
this.updateDefaultTheatreMode(true)
}
this.updateHideRecommendedVideos(value)
......@@ -60,6 +62,7 @@ export default Vue.extend({
'updateHideTrendingVideos',
'updateHidePopularVideos',
'updateHideLiveChat',
'updateHideActiveSubscriptions',
'updatePlayNextVideo',
'updateDefaultTheatreMode'
])
......
......@@ -33,6 +33,12 @@
:default-value="hideCommentLikes"
@change="updateHideCommentLikes"
/>
<ft-toggle-switch
:label="$t('Settings.Distraction Free Settings.Hide Active Subscriptions')"
:compact="true"
:default-value="hideActiveSubscriptions"
@change="updateHideActiveSubscriptions"
/>
</div>
<div class="switchColumn">
<ft-toggle-switch
......
......@@ -52,6 +52,9 @@
&:active
background-color: var(--accent-color-active)
&.favorite
color: var(--favorite-icon-color)
.iconDropdown
display: none
position: absolute
......
......@@ -14,7 +14,7 @@
<ft-tooltip
v-if="tooltip !== ''"
class="selectTooltip"
position="right"
position="bottom"
:tooltip="tooltip"
/>
</label>
......
......@@ -57,6 +57,10 @@ export default Vue.extend({
this.channelLink = this.data.authorUrl
this.playlistLink = this.data.playlistId
this.videoCount = this.data.videoCount
if (this.data.proxyThumbnail === false) {
this.thumbnail = this.data.playlistThumbnail
}
},
parseLocalData: function () {
......
......@@ -161,6 +161,26 @@ export default Vue.extend({
},
hideVideoViews: function () {
return this.$store.getters.getHideVideoViews
},
addWatchedStyle: function () {
return this.watched && !this.inHistory
},
favoritesPlaylist: function () {
return this.$store.getters.getFavorites
},
inFavoritesPlaylist: function () {
const index = this.favoritesPlaylist.videos.findIndex((video) => {
return video.videoId === this.id
})
return index !== -1
},
favoriteIconTheme: function () {
return this.inFavoritesPlaylist ? 'base favorite' : 'base'
}
},
mounted: function () {
......@@ -169,10 +189,11 @@ export default Vue.extend({
},
methods: {
toggleSave: function () {
console.log('TODO: ft-list-video method toggleSave')
this.showToast({
message: this.$t('Saving videos are currently not available. Please wait for a future update')
})
if (this.inFavoritesPlaylist) {
this.removeFromPlaylist()
} else {
this.addToPlaylist()
}
},
handleOptionsClick: function (option) {
......@@ -392,11 +413,54 @@ export default Vue.extend({
this.watched = false
},
addToPlaylist: function () {
const videoData = {
videoId: this.id,
title: this.title,
author: this.channelName,
authorId: this.channelId,