diff --git a/src/renderer/components/download-settings/download-settings.js b/src/renderer/components/download-settings/download-settings.js
new file mode 100644
index 00000000..96c67535
--- /dev/null
+++ b/src/renderer/components/download-settings/download-settings.js
@@ -0,0 +1,49 @@
+import Vue from 'vue'
+import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
+import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue'
+import FtButton from '../ft-button/ft-button.vue'
+import FtInput from '../ft-input/ft-input.vue'
+import { mapActions } from 'vuex'
+import { ipcRenderer } from 'electron'
+import { IpcChannels } from '../../../constants'
+
+export default Vue.extend({
+ name: 'DownloadSettings',
+ components: {
+ 'ft-toggle-switch': FtToggleSwitch,
+ 'ft-flex-box': FtFlexBox,
+ 'ft-button': FtButton,
+ 'ft-input': FtInput
+ },
+ data: function () {
+ return {
+ askForDownloadPath: this.$store.getters.getDownloadFolderPath === ''
+ }
+ },
+ computed: {
+ downloadPath: function() {
+ return this.$store.getters.getDownloadFolderPath
+ }
+ },
+ methods: {
+ handleDownloadingSettingChange: function (value) {
+ this.askForDownloadPath = value
+ if (value === true) {
+ this.updateDownloadFolderPath('')
+ }
+ },
+ chooseDownloadingFolder: async function() {
+ // only use with electron
+ const folder = await ipcRenderer.invoke(
+ IpcChannels.SHOW_OPEN_DIALOG,
+ { properties: ['openDirectory'] }
+ )
+
+ this.updateDownloadFolderPath(folder.filePaths[0])
+ },
+ ...mapActions([
+ 'updateDownloadFolderPath'
+ ])
+ }
+
+})
diff --git a/src/renderer/components/download-settings/download-settings.sass b/src/renderer/components/download-settings/download-settings.sass
new file mode 100644
index 00000000..330bf087
--- /dev/null
+++ b/src/renderer/components/download-settings/download-settings.sass
@@ -0,0 +1,8 @@
+@use "../../sass-partials/settings"
+
+@media only screen and (max-width: 500px)
+ .downloadSettingsFlexBox
+ justify-content: flex-start
+
+.folderDisplay
+ width: 50vh
diff --git a/src/renderer/components/download-settings/download-settings.vue b/src/renderer/components/download-settings/download-settings.vue
new file mode 100644
index 00000000..c55e314e
--- /dev/null
+++ b/src/renderer/components/download-settings/download-settings.vue
@@ -0,0 +1,37 @@
+
+
+
+
+ {{ $t("Settings.Download Settings.Download Settings") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/renderer/components/ft-icon-button/ft-icon-button.js b/src/renderer/components/ft-icon-button/ft-icon-button.js
index b71bc697..88cc6b95 100644
--- a/src/renderer/components/ft-icon-button/ft-icon-button.js
+++ b/src/renderer/components/ft-icon-button/ft-icon-button.js
@@ -47,6 +47,11 @@ export default Vue.extend({
dropdownValues: {
type: Array,
default: () => { return [] }
+ },
+ relatedVideoTitle: {
+ type: String,
+ default: () => { return '' },
+ require: false
}
},
data: function () {
@@ -55,6 +60,18 @@ export default Vue.extend({
id: ''
}
},
+ computed: {
+ filesExtensions: function() {
+ const regex = /\/(\w*)/i
+ return this.dropdownNames.slice().map((el) => {
+ const group = el.match(regex)
+ if (group.length === 0) {
+ return ''
+ }
+ return group[1]
+ })
+ }
+ },
mounted: function () {
this.id = `iconButton${this._uid}`
},
@@ -111,7 +128,12 @@ export default Vue.extend({
},
handleDropdownClick: function (index) {
- this.$emit('click', this.dropdownValues[index])
+ this.$emit('click', {
+ url: this.dropdownValues[index],
+ title: this.relatedVideoTitle,
+ extension: this.filesExtensions[index],
+ folderPath: this.$store.getters.getDownloadFolderPath
+ })
this.focusOut()
}
}
diff --git a/src/renderer/components/ft-toast/ft-toast.js b/src/renderer/components/ft-toast/ft-toast.js
index be0b4e3e..df557a5e 100644
--- a/src/renderer/components/ft-toast/ft-toast.js
+++ b/src/renderer/components/ft-toast/ft-toast.js
@@ -25,7 +25,13 @@ export default Vue.extend({
toast.isOpen = false
},
- open: function (message, action, time) {
+ open: function (message, action, time, translate = false, formatArgs = []) {
+ if (translate) {
+ message = this.$t(message)
+ for (const arg of formatArgs) {
+ message = message.replace('$', arg)
+ }
+ }
const toast = { message: message, action: action || (() => { }), isOpen: false, timeout: null }
toast.timeout = setTimeout(this.close, time || 3000, toast)
setImmediate(() => { toast.isOpen = true })
diff --git a/src/renderer/components/ft-video-player/ft-video-player.js b/src/renderer/components/ft-video-player/ft-video-player.js
index f7414408..9c722bf4 100644
--- a/src/renderer/components/ft-video-player/ft-video-player.js
+++ b/src/renderer/components/ft-video-player/ft-video-player.js
@@ -113,7 +113,6 @@ export default Vue.extend({
'chaptersButton',
'descriptionsButton',
'subsCapsButton',
- 'audioTrackButton',
'pictureInPictureToggle',
'toggleTheatreModeButton',
'fullWindowButton',
diff --git a/src/renderer/components/watch-video-info/watch-video-info.js b/src/renderer/components/watch-video-info/watch-video-info.js
index 95ef955c..995fc08d 100644
--- a/src/renderer/components/watch-video-info/watch-video-info.js
+++ b/src/renderer/components/watch-video-info/watch-video-info.js
@@ -455,7 +455,8 @@ export default Vue.extend({
'updateProfile',
'addVideo',
'removeVideo',
- 'openExternalLink'
+ 'openExternalLink',
+ 'downloadMedia'
])
}
})
diff --git a/src/renderer/components/watch-video-info/watch-video-info.vue b/src/renderer/components/watch-video-info/watch-video-info.vue
index 0f66d444..2db55689 100644
--- a/src/renderer/components/watch-video-info/watch-video-info.vue
+++ b/src/renderer/components/watch-video-info/watch-video-info.vue
@@ -86,7 +86,8 @@
icon="download"
:dropdown-names="downloadLinkNames"
:dropdown-values="downloadLinkValues"
- @click="openExternalLink"
+ :related-video-title="title"
+ @click="downloadMedia"
/>
(lastPercentageNotification + intervalPercentageNotification)) {
+ // mechanism kept for an upcoming download page
+ lastPercentageNotification = percentage
+ }
+ }
+
+ const chunksAll = new Uint8Array(receivedLength)
+ let position = 0
+ for (const chunk of chunks) {
+ chunksAll.set(chunk, position)
+ position += chunk.length
+ }
+
+ // write the file into the hardrive
+ if (!response.ok) {
+ console.error(`"Unable to download ${title}, return status code ${response.status}`)
+ dispatch('showToast', {
+ message: 'Downloading failed', translate: true, formatArgs: [title, response.status]
+ })
+ return
+ }
+ const blobFile = new Blob(chunks)
+ const buffer = await blobFile.arrayBuffer()
+
+ if (usingElectron && !askFolderPath) {
+ fs.writeFile(`${folderPath}/${title}.${extension}`, new DataView(buffer), (err) => {
+ if (err) {
+ console.error(err)
+ dispatch('updateDownloadFolderPath', '')
+ dispatch('downloadMedia', { url: url, title: title, extension: extension, folderPath: '', fallingBackPath: folderPath })
+ } else {
+ dispatch('showToast', {
+ message: successMsg, translate: true, formatArgs: [title]
+ })
+ }
+ })
+ } else if (usingElectron) {
+ fs.writeFile(filePathSelected, new DataView(buffer), (err) => {
+ if (err) {
+ console.error(err)
+ if (filePathSelected === '') {
+ dispatch('showToast', {
+ message: 'Downloading canceled',
+ translate: true
+ })
+ } else {
+ dispatch('showToast', {
+ message: err
+ })
+ }
+ } else {
+ dispatch('showToast', {
+ message: successMsg, translate: true, formatArgs: [title]
+ })
+ }
+ })
+ } else {
+ // Web placeholder
+ }
+ },
+
async getSystemLocale (context) {
const webCbk = () => {
if (navigator && navigator.language) {
@@ -680,7 +790,9 @@ const actions = {
},
showToast (_, payload) {
- FtToastEvents.$emit('toast-open', payload.message, payload.action, payload.time)
+ const formatArgs = 'formatArgs' in payload ? payload.formatArgs : []
+ const translate = 'translate' in payload ? payload.translate : false
+ FtToastEvents.$emit('toast-open', payload.message, payload.action, payload.time, translate, formatArgs)
},
showExternalPlayerUnsupportedActionToast: function ({ dispatch }, payload) {
diff --git a/src/renderer/views/Settings/Settings.js b/src/renderer/views/Settings/Settings.js
index 4cfacc1e..df4a1b02 100644
--- a/src/renderer/views/Settings/Settings.js
+++ b/src/renderer/views/Settings/Settings.js
@@ -6,6 +6,7 @@ import ThemeSettings from '../../components/theme-settings/theme-settings.vue'
import PlayerSettings from '../../components/player-settings/player-settings.vue'
import ExternalPlayerSettings from '../../components/external-player-settings/external-player-settings.vue'
import SubscriptionSettings from '../../components/subscription-settings/subscription-settings.vue'
+import DownloadSettings from '../../components/download-settings/download-settings.vue'
import PrivacySettings from '../../components/privacy-settings/privacy-settings.vue'
import DataSettings from '../../components/data-settings/data-settings.vue'
import DistractionSettings from '../../components/distraction-settings/distraction-settings.vue'
@@ -26,7 +27,8 @@ export default Vue.extend({
'data-settings': DataSettings,
'distraction-settings': DistractionSettings,
'proxy-settings': ProxySettings,
- 'sponsor-block-settings': SponsorBlockSettings
+ 'sponsor-block-settings': SponsorBlockSettings,
+ 'download-settings': DownloadSettings
},
computed: {
usingElectron: function () {
diff --git a/src/renderer/views/Settings/Settings.vue b/src/renderer/views/Settings/Settings.vue
index bd766dce..55e82dd3 100644
--- a/src/renderer/views/Settings/Settings.vue
+++ b/src/renderer/views/Settings/Settings.vue
@@ -19,6 +19,8 @@
+
+
diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml
index b2c12430..f4e090b5 100644
--- a/static/locales/en-US.yaml
+++ b/static/locales/en-US.yaml
@@ -325,6 +325,10 @@ Settings:
Enable SponsorBlock: Enable SponsorBlock
'SponsorBlock API Url (Default is https://sponsor.ajay.app)': SponsorBlock API Url (Default is https://sponsor.ajay.app)
Notify when sponsor segment is skipped: Notify when sponsor segment is skipped
+ Download Settings:
+ Download Settings: Download Settings
+ Ask Download Path: Ask for download path
+ Choose Path: Choose Path
About:
#On About page
About: About
@@ -709,6 +713,11 @@ Default Invidious instance has been cleared: Default Invidious instance has been
'The playlist has ended. Enable loop to continue playing': 'The playlist has ended. Enable
loop to continue playing'
External link opening has been disabled in the general settings: 'External link opening has been disabled in the general settings'
+Downloading has completed: 'Downloading "$" has completed'
+Starting download: 'Downloading "$" has started'
+Downloading failed: 'Unable to download "$", return http request status code $'
+Downloading canceled: The dowload is canceled by the user
+Download folder does not exist: The download directory "$" doesn't exist. Falling back to "ask folder" mode.
Yes: Yes
No: No
diff --git a/static/locales/fr-FR.yaml b/static/locales/fr-FR.yaml
index 3e91f8ef..a29cb2e2 100644
--- a/static/locales/fr-FR.yaml
+++ b/static/locales/fr-FR.yaml
@@ -377,6 +377,10 @@ Settings:
External Player Settings: Paramètres du lecteur externe
Custom External Player Arguments: Arguments personnalisés du lecteur externe
Custom External Player Executable: Exécutable de lecteur externe personnalisé
+ Download Settings:
+ Download Settings: 'Paramètres de téléchargement'
+ Ask Download Path: 'Demander l'emplacement de téléchargement'
+ Choose Path: 'Choisir l'emplacement de téléchargement'
About:
#On About page
About: 'À propos'
@@ -836,3 +840,9 @@ Search Bar:
Are you sure you want to open this link?: Êtes-vous sûr(e) de vouloir ouvrir ce lien ?
External link opening has been disabled in the general settings: L'ouverture des liens
externes a été désactivée dans les paramètres généraux
+Downloading has completed: 'Le téléchargement de "$" est fini'
+Starting download: 'Le téléchargement de "$" a commencé'
+Downloading failed: 'Incapable de téléchargement "$", retourne l'erreur http $'
+Downloading canceled: 'Le téléchargement est annulé par l'utilisateur'
+Download folder does not exist: 'Le répertoire "$" de téléchargement n'existe pas. Mode sans répertoire activé'
+