Skip to content

Commit 3d1d4f0

Browse files
committed
Merge branch 'development' into sabr-pr
* development: (24 commits) Add proxy login support (FreeTubeApp#7996) Migrate the History view to the composition API (FreeTubeApp#8064) Fix player button hiding (FreeTubeApp#8065) Translated using Weblate (Norwegian Bokmål) Translated using Weblate (Indonesian) Fix playback rate reset when video ends (FreeTubeApp#7718) Skip Electron binary download in CI as we do not run it (FreeTubeApp#8063) Feat: Display player overlay controls on keyboard interaction (FreeTubeApp#7757) Translated using Weblate (Hungarian) Translated using Weblate (Czech) Translated using Weblate (Estonian) Translated using Weblate (French) Bump electron from 37.4.0 to 38.1.0 (FreeTubeApp#8025) Translated using Weblate (Dutch) Translated using Weblate (Portuguese (Brazil)) Translated using Weblate (German) Translated using Weblate (Breton) Translated using Weblate (Italian) Translated using Weblate (Chinese (Simplified Han script)) Support exporting single playlists as a list of video URLs (FreeTubeApp#8054) ... # Conflicts: # src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js
2 parents c4784ee + 4655d0e commit 3d1d4f0

File tree

33 files changed

+1346
-1136
lines changed

33 files changed

+1346
-1136
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ jobs:
5858
node-version: ${{ matrix.node-version }}
5959
cache: "yarn"
6060
- run: yarn run ci
61+
env:
62+
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
6163
- run: yarn run lint
6264
- name: Get Version Number
6365
id: getPackageInfo

.github/workflows/linter.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ jobs:
2424
node-version: 22.x
2525
cache: "yarn"
2626
- run: yarn run ci
27+
env:
28+
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
2729
- run: yarn run lint
2830
# let's verify that webpack is able to package the project
2931
- run: yarn run pack

.github/workflows/release.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ jobs:
6363
node-version: ${{ matrix.node-version }}
6464
cache: "yarn"
6565
- run: yarn run ci
66+
env:
67+
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
6668
- run: yarn run lint
6769

6870
- name: Get Version Number

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
FreeTube is an open source desktop YouTube player built with privacy in mind.
66
Use YouTube without advertisements and prevent Google from tracking you with their cookies and JavaScript.
7-
Available for Windows (10 and later), Mac (macOS 11 and later) & Linux thanks to Electron.
7+
Available for Windows (10 and later), Mac (macOS 12 and later) & Linux thanks to Electron.
88

99
<p align="center"><a href="https://github.com/FreeTubeApp/FreeTube/releases">Download FreeTube</a></p>
1010
<p align="center">
@@ -96,7 +96,7 @@ RedirectTube, doesn’t automatically open YouTube links in FreeTube. Instead, i
9696
### Official Downloads
9797

9898
> [!CAUTION]
99-
> FreeTube is only supported on Windows 10 and later, macOS 11 and above, and various Linux distributions. Installing it on unsupported systems may result in unexpected issues.
99+
> FreeTube is only supported on Windows 10 and later, macOS 12 and above, and various Linux distributions. Installing it on unsupported systems may result in unexpected issues.
100100
101101
* [GitHub Releases](https://github.com/FreeTubeApp/FreeTube/releases)
102102

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
"copy-webpack-plugin": "^13.0.1",
8787
"css-loader": "^7.1.2",
8888
"css-minimizer-webpack-plugin": "^7.0.2",
89-
"electron": "^37.4.0",
89+
"electron": "^38.1.0",
9090
"electron-builder": "^26.0.20",
9191
"eslint": "^9.35.0",
9292
"eslint-plugin-jsdoc": "^57.0.8",

src/main/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,15 @@ function runApp() {
703703
}
704704
})
705705

706+
app.on('login', async (event, webContents, request, authInfo, callback) => {
707+
if (authInfo.isProxy) {
708+
event.preventDefault()
709+
const proxyUsername = (await baseHandlers.settings._findOne('proxyUsername'))?.value
710+
const proxyPassword = (await baseHandlers.settings._findOne('proxyPassword'))?.value
711+
callback(proxyUsername, proxyPassword)
712+
}
713+
})
714+
706715
function trayClick(window, close = false) {
707716
if (!close) {
708717
if (window.id in trayMaximizedWindows) {

src/renderer/components/PlaylistInfo/PlaylistInfo.vue

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@
177177
:title="$t('User Playlists.Export Playlist')"
178178
:icon="['fas', 'file-arrow-down']"
179179
theme="secondary"
180-
@click="handlePlaylistExport"
180+
@click="showExportPrompt = true"
181181
/>
182182
<FtIconButton
183183
v-if="!editMode && userPlaylistDuplicateItemCount > 0"
@@ -251,6 +251,13 @@
251251
is-first-option-destructive
252252
@click="handleRemoveDuplicateVideosPromptAnswer"
253253
/>
254+
<FtPrompt
255+
v-if="showExportPrompt"
256+
:label="t('Settings.Data Settings.Select Export Type')"
257+
:option-names="exportNames"
258+
:option-values="EXPORT_VALUES"
259+
@click="handleExport"
260+
/>
254261
</div>
255262
</div>
256263
</template>
@@ -323,6 +330,10 @@ const props = defineProps({
323330
type: Array,
324331
required: true
325332
},
333+
sortedVideos: {
334+
type: Array,
335+
required: true
336+
},
326337
viewCount: {
327338
type: Number,
328339
required: true,
@@ -370,6 +381,7 @@ const editMode = ref(false)
370381
const showDeletePlaylistPrompt = ref(false)
371382
const showRemoveVideosOnWatchPrompt = ref(false)
372383
const showRemoveDuplicateVideosPrompt = ref(false)
384+
const showExportPrompt = ref(false)
373385
const newTitle = ref(props.title)
374386
const newDescription = ref(props.description)
375387
@@ -532,6 +544,7 @@ const playlistPersistenceDisabled = computed(() => {
532544
533545
watch(showDeletePlaylistPrompt, handlePromptToggle)
534546
watch(showRemoveVideosOnWatchPrompt, handlePromptToggle)
547+
watch(showExportPrompt, handlePromptToggle)
535548
536549
/**
537550
* @param {boolean} shown
@@ -617,10 +630,43 @@ function handlePlaylistDeleteDisabledClick() {
617630
showToast(playlistDeletionDisabledLabel.value)
618631
}
619632
620-
async function handlePlaylistExport() {
633+
const EXPORT_VALUES = [
634+
'database',
635+
'urls',
636+
'close'
637+
]
638+
639+
const exportNames = computed(() => [
640+
`${t('Settings.Data Settings.Export FreeTube')} (.db)`,
641+
`${t('User Playlists.Export list of URLs')} (.txt)`,
642+
t('Close')
643+
])
644+
645+
/**
646+
* @param {'database' | 'urls' | null} value
647+
*/
648+
function handleExport(value) {
649+
showExportPrompt.value = false
650+
651+
if (value === 'database') {
652+
exportAsFreeTubeDatabase()
653+
} else if (value === 'urls') {
654+
exportAsListOfUrls()
655+
}
656+
}
657+
658+
/**
659+
* @param {string} title
660+
* @param {string} extension
661+
*/
662+
function getExportFilename(title, extension) {
621663
const dateStr = getTodayDateStrLocalTimezone()
622-
const title = selectedUserPlaylist.value.playlistName.replaceAll(/[ "%*/:<>?\\|]/g, '_')
623-
const exportFileName = 'freetube-playlist-' + title + '-' + dateStr + '.db'
664+
const sanitisedTitle = title.replaceAll(/[ "%*/:<>?\\|]/g, '_')
665+
return `freetube-playlist-${sanitisedTitle}-${dateStr}.${extension}`
666+
}
667+
668+
async function exportAsFreeTubeDatabase() {
669+
const exportFileName = getExportFilename(selectedUserPlaylist.value.playlistName, 'db')
624670
625671
const data = JSON.stringify(selectedUserPlaylist.value) + '\n'
626672
@@ -646,6 +692,35 @@ async function handlePlaylistExport() {
646692
}
647693
}
648694
695+
async function exportAsListOfUrls() {
696+
const exportFileName = getExportFilename(props.title, 'txt')
697+
698+
const data = props.sortedVideos.map((video) => {
699+
return `https://www.youtube.com/watch?v=${video.videoId}`
700+
}).join('\n') + '\n'
701+
702+
// See DataSettings.vue `promptAndWriteToFile`
703+
704+
try {
705+
const response = await writeFileWithPicker(
706+
exportFileName,
707+
data,
708+
'',
709+
'text/plain',
710+
'.txt',
711+
'single-playlist-export',
712+
'downloads'
713+
)
714+
715+
if (response) {
716+
showToast(t('User Playlists.The playlist has been successfully exported'))
717+
}
718+
} catch (error) {
719+
const message = t('Settings.Data Settings.Unable to write file')
720+
showToast(`${message}: ${error}`)
721+
}
722+
}
723+
649724
function exitEditMode() {
650725
editMode.value = false
651726

src/renderer/components/ProxySettings/ProxySettings.vue

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,24 @@
5353
@keydown.enter.native="testProxy"
5454
/>
5555
</FtFlexBox>
56+
<FtFlexBox>
57+
<FtInput
58+
:placeholder="$t('Settings.Proxy Settings.Proxy Username')"
59+
:show-action-button="false"
60+
show-label
61+
:value="proxyUsername"
62+
@input="handleUpdateProxyUsername"
63+
@keydown.enter.native="testProxy"
64+
/>
65+
<FtInput
66+
:placeholder="$t('Settings.Proxy Settings.Proxy Password')"
67+
:show-action-button="false"
68+
show-label
69+
:value="proxyPassword"
70+
@input="handleUpdateProxyPassword"
71+
@keydown.enter.native="testProxy"
72+
/>
73+
</FtFlexBox>
5674
<p
5775
class="center"
5876
>
@@ -151,6 +169,16 @@ const proxyPort = computed(() => {
151169
return store.getters.getProxyPort
152170
})
153171
172+
/** @type {import('vue').ComputedRef<string>} */
173+
const proxyUsername = computed(() => {
174+
return store.getters.getProxyUsername
175+
})
176+
177+
/** @type {import('vue').ComputedRef<string>} */
178+
const proxyPassword = computed(() => {
179+
return store.getters.getProxyPassword
180+
})
181+
154182
const proxyUrl = computed(() => {
155183
return `${proxyProtocol.value}://${proxyHostname.value}:${proxyPort.value}`
156184
})
@@ -230,6 +258,28 @@ function handleUpdateProxyPort(value) {
230258
store.dispatch('updateProxyPort', value)
231259
}
232260
261+
/**
262+
* @param {string} value
263+
*/
264+
function handleUpdateProxyUsername(value) {
265+
if (useProxy.value) {
266+
debouncedEnableProxy()
267+
}
268+
269+
store.dispatch('updateProxyUsername', value)
270+
}
271+
272+
/**
273+
* @param {string} value
274+
*/
275+
function handleUpdateProxyPassword(value) {
276+
if (useProxy.value) {
277+
debouncedEnableProxy()
278+
}
279+
280+
store.dispatch('updateProxyPassword', value)
281+
}
282+
233283
function enableProxy() {
234284
if (process.env.IS_ELECTRON) {
235285
window.ftElectron.enableProxy(proxyUrl.value)

0 commit comments

Comments
 (0)