diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 3ae7187b45c45..3ee6b6eaf9889 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -90,16 +90,18 @@ body: - Flathub - .pacman - Portable - - .rpm + - .rpm - .zip - .apk (Android, FreeTubeCordova Unofficial) - AUR (Unofficial) - Chocolatey (Unofficial) + - Homebrew (Unofficial) - MPR (Unofficial) - Nix (Unofficial) - PortableApps (Unofficial) - Scoop (Unofficial) - Snapcraft (Unofficial) + - WAPT (Unofficial) - winget (Unofficial) - other validations: diff --git a/.github/issue-labeler.yml b/.github/issue-labeler.yml index 2bd615782823c..1d08f2788ff0b 100644 --- a/.github/issue-labeler.yml +++ b/.github/issue-labeler.yml @@ -2,10 +2,10 @@ - '(visual bug)' 'B: Unofficial Download': - - '(AUR \(Unofficial\)|Chocolatey \(Unofficial\)|\.apk \(Android, FreeTubeCordova Unofficial\)|PortableApps \(Unofficial\)|winget \(Unofficial\)|Scoop \(Unofficial\)|Snapcraft \(Unofficial\)|MPR \(Unofficial\)|Nix \(Unofficial\))' + - '(AUR \(Unofficial\)|Chocolatey \(Unofficial\)|\.apk \(Android, FreeTubeCordova Unofficial\)|Homebrew \(Unofficial\)|PortableApps \(Unofficial\)|WAPT \(Unofficial\)|winget \(Unofficial\)|Scoop \(Unofficial\)|Snapcraft \(Unofficial\)|MPR \(Unofficial\)|Nix \(Unofficial\))' 'B: keyboard control': - - '(keyboard control not working)' + - '(keyboard control not working)' 'B: text/string': - '(text/string issue)' diff --git a/.github/pr-labeler.yml b/.github/pr-labeler.yml index dff087046453f..a6f19b9ca8c3c 100644 --- a/.github/pr-labeler.yml +++ b/.github/pr-labeler.yml @@ -1,19 +1,8 @@ 'PR: waiting for review': - - '*' - - '.babelrc' - - '.editorconfig' - - '.eslintignore' - - '.eslintrc.js' - - '.gitignore' - - '.prettierrc' - - '.whitesource' - - '.github/**/*' - - '.vscode/**/*' - - '_icons/**/*' - - '_scripts/**/*' - - 'src/**/*' - - 'static/**/*' +- changed-files: + - any-glob-to-any-file: '**' 'PR: dependencies': - - 'yarn.lock' - - 'package.json' +- any: + - changed-files: + - any-glob-to-any-file: ['yarn.lock', 'package.json'] diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 500b94b7d3239..521082d630464 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -31,7 +31,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -45,7 +45,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -58,6 +58,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/label-pr.yml b/.github/workflows/label-pr.yml index 9df23f2053b03..5c9c9927693d3 100644 --- a/.github/workflows/label-pr.yml +++ b/.github/workflows/label-pr.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest if: ${{ !github.event.pull_request.draft }} steps: - - uses: actions/labeler@v4 + - uses: actions/labeler@v5 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" configuration-path: .github/pr-labeler.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 5fd8db421addb..6945419318d9a 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -11,7 +11,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v8 + - uses: actions/stale@v9 with: stale-issue-message: 'This issue is stale because it has been open 28 days with no activity. Remove stale label or comment or this will be closed in 7 days.' stale-pr-message: 'This PR is stale because it has been open 28 days with no activity. Remove stale label or comment or this will be closed in 14 days.' diff --git a/README.md b/README.md index c5b532b939a85..0d478b917c78c 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,8 @@ These builds are maintained by the community. While they should be safe, downloa * FreeTubeCordova (FreeTube port for Android and PWA): [Download](https://github.com/MarmadileManteater/FreeTubeCordova/releases) and [Source Code](https://github.com/MarmadileManteater/FreeTubeCordova) +* Homebrew Formulae (Mac only): [Download](https://formulae.brew.sh/cask/freetube) + * makedeb Package Repository (MPR): [Download](https://mpr.makedeb.org/packages/freetube-bin) * Nix Packages: [Download](https://search.nixos.org/packages?query=freetube) @@ -99,6 +101,8 @@ These builds are maintained by the community. While they should be safe, downloa * Snap: [Download](https://snapcraft.io/freetube) and [Source Code](https://git.launchpad.net/freetube) +* WAPT: [Download](https://wapt.tranquil.it/store/tis-freetube) + * Windows Package Manager (winget): [Usage](https://docs.microsoft.com/en-us/windows/package-manager/winget/) ## Contributing diff --git a/_scripts/build.js b/_scripts/build.js index 035f986c97dbe..bee33667fbeae 100644 --- a/_scripts/build.js +++ b/_scripts/build.js @@ -1,9 +1,9 @@ const os = require('os') const builder = require('electron-builder') +const config = require('./ebuilder.config.js') const Platform = builder.Platform const Arch = builder.Arch -const { name, productName } = require('../package.json') const args = process.argv let targets @@ -39,97 +39,6 @@ if (platform === 'darwin') { targets = Platform.LINUX.createTarget(['deb', 'zip', '7z', 'apk', 'rpm', 'AppImage', 'pacman'], arch) } -const config = { - appId: `io.freetubeapp.${name}`, - copyright: 'Copyleft © 2020-2023 freetubeapp@protonmail.com', - // asar: false, - // compression: 'store', - productName, - directories: { - output: './build/', - }, - protocols: [ - { - name: "FreeTube", - schemes: [ - "freetube" - ] - } - ], - files: [ - '_icons/iconColor.*', - 'icon.svg', - './dist/**/*', - '!dist/web/*', - '!node_modules/**/*', - ], - dmg: { - contents: [ - { - path: '/Applications', - type: 'link', - x: 410, - y: 230, - }, - { - type: 'file', - x: 130, - y: 230, - }, - ], - window: { - height: 380, - width: 540, - } - }, - linux: { - category: 'Network', - icon: '_icons/icon.svg', - target: ['deb', 'zip', '7z', 'apk', 'rpm', 'AppImage', 'pacman'], - }, - // See the following issues for more information - // https://github.com/jordansissel/fpm/issues/1503 - // https://github.com/jgraph/drawio-desktop/issues/259 - rpm: { - fpm: [`--rpm-rpmbuild-define=_build_id_links none`] - }, - deb: { - depends: [ - "libgtk-3-0", - "libnotify4", - "libnss3", - "libxss1", - "libxtst6", - "xdg-utils", - "libatspi2.0-0", - "libuuid1", - "libsecret-1-0" - ] - }, - mac: { - category: 'public.app-category.utilities', - icon: '_icons/iconMac.icns', - target: ['dmg', 'zip', '7z'], - type: 'distribution', - extendInfo: { - CFBundleURLTypes: [ - 'freetube' - ], - CFBundleURLSchemes: [ - 'freetube' - ] - } - }, - win: { - icon: '_icons/icon.ico', - target: ['nsis', 'zip', '7z', 'portable'], - }, - nsis: { - allowToChangeInstallationDirectory: true, - oneClick: false, - }, -} - builder .build({ targets, diff --git a/_scripts/ebuilder.config.js b/_scripts/ebuilder.config.js new file mode 100644 index 0000000000000..5b79d96185685 --- /dev/null +++ b/_scripts/ebuilder.config.js @@ -0,0 +1,94 @@ +const { name, productName } = require('../package.json') + +const config = { + appId: `io.freetubeapp.${name}`, + copyright: 'Copyleft © 2020-2024 freetubeapp@protonmail.com', + // asar: false, + // compression: 'store', + productName, + directories: { + output: './build/', + }, + protocols: [ + { + name: "FreeTube", + schemes: [ + "freetube" + ] + } + ], + files: [ + '_icons/iconColor.*', + 'icon.svg', + './dist/**/*', + '!dist/web/*', + '!node_modules/**/*', + ], + dmg: { + contents: [ + { + path: '/Applications', + type: 'link', + x: 410, + y: 230, + }, + { + type: 'file', + x: 130, + y: 230, + }, + ], + window: { + height: 380, + width: 540, + } + }, + linux: { + category: 'Network', + icon: '_icons/icon.svg', + target: ['deb', 'zip', '7z', 'apk', 'rpm', 'AppImage', 'pacman'], + }, + // See the following issues for more information + // https://github.com/jordansissel/fpm/issues/1503 + // https://github.com/jgraph/drawio-desktop/issues/259 + rpm: { + fpm: [`--rpm-rpmbuild-define=_build_id_links none`] + }, + deb: { + depends: [ + "libgtk-3-0", + "libnotify4", + "libnss3", + "libxss1", + "libxtst6", + "xdg-utils", + "libatspi2.0-0", + "libuuid1", + "libsecret-1-0" + ] + }, + mac: { + category: 'public.app-category.utilities', + icon: '_icons/iconMac.icns', + target: ['dmg', 'zip', '7z'], + type: 'distribution', + extendInfo: { + CFBundleURLTypes: [ + 'freetube' + ], + CFBundleURLSchemes: [ + 'freetube' + ] + } + }, + win: { + icon: '_icons/icon.ico', + target: ['nsis', 'zip', '7z', 'portable'], + }, + nsis: { + allowToChangeInstallationDirectory: true, + oneClick: false, + }, +} + +module.exports = config diff --git a/_scripts/webpack.renderer.config.js b/_scripts/webpack.renderer.config.js index fc6495f3ad34a..057be25f4a65b 100644 --- a/_scripts/webpack.renderer.config.js +++ b/_scripts/webpack.renderer.config.js @@ -7,6 +7,7 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin') const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') const ProcessLocalesPlugin = require('./ProcessLocalesPlugin') const WatchExternalFilesPlugin = require('webpack-watch-external-files-plugin') +const CopyWebpackPlugin = require('copy-webpack-plugin') const isDevMode = process.env.NODE_ENV === 'development' @@ -131,6 +132,18 @@ const config = { filename: isDevMode ? '[name].css' : '[name].[contenthash].css', chunkFilename: isDevMode ? '[id].css' : '[id].[contenthash].css', }), + new CopyWebpackPlugin({ + patterns: [ + { + from: path.join(__dirname, '../node_modules/swiper/modules/{a11y,navigation,pagination}-element.css').replaceAll('\\', '/'), + to: 'swiper.css', + context: path.join(__dirname, '../node_modules/swiper/modules'), + transformAll: (assets) => { + return Buffer.concat(assets.map(asset => asset.data)) + } + } + ] + }) ], resolve: { alias: { diff --git a/_scripts/webpack.web.config.js b/_scripts/webpack.web.config.js index 420d350482ea4..9dd0e20f1a22c 100644 --- a/_scripts/webpack.web.config.js +++ b/_scripts/webpack.web.config.js @@ -140,6 +140,18 @@ const config = { new MiniCssExtractPlugin({ filename: isDevMode ? '[name].css' : '[name].[contenthash].css', chunkFilename: isDevMode ? '[id].css' : '[id].[contenthash].css', + }), + new CopyWebpackPlugin({ + patterns: [ + { + from: path.join(__dirname, '../node_modules/swiper/modules/{a11y,navigation,pagination}-element.css').replaceAll('\\', '/'), + to: 'swiper.css', + context: path.join(__dirname, '../node_modules/swiper/modules'), + transformAll: (assets) => { + return Buffer.concat(assets.map(asset => asset.data)) + } + } + ] }) ], resolve: { diff --git a/package.json b/package.json index 2aa6c85e6bfae..7c82397281ce2 100644 --- a/package.json +++ b/package.json @@ -53,73 +53,73 @@ "ci": "yarn install --silent --frozen-lockfile" }, "dependencies": { - "@fortawesome/fontawesome-svg-core": "^6.4.2", - "@fortawesome/free-brands-svg-icons": "^6.4.2", - "@fortawesome/free-solid-svg-icons": "^6.4.2", + "@fortawesome/fontawesome-svg-core": "^6.5.1", + "@fortawesome/free-brands-svg-icons": "^6.5.1", + "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/vue-fontawesome": "^2.0.10", - "@seald-io/nedb": "^4.0.2", + "@seald-io/nedb": "^4.0.3", "@silvermine/videojs-quality-selector": "^1.3.1", "autolinker": "^4.0.0", "electron-context-menu": "^3.6.1", "lodash.debounce": "^4.0.8", - "marked": "^10.0.0", + "marked": "^11.1.1", "path-browserify": "^1.0.1", "process": "^0.11.10", + "swiper": "^11.0.5", "video.js": "7.21.5", "videojs-contrib-quality-levels": "^3.0.0", "videojs-http-source-selector": "^1.1.6", "videojs-mobile-ui": "^0.8.0", "videojs-overlay": "^3.1.0", "videojs-vtt-thumbnails-freetube": "0.0.15", - "vue": "^2.7.15", + "vue": "^2.7.16", "vue-i18n": "^8.28.2", "vue-observe-visibility": "^1.0.0", "vue-router": "^3.6.5", - "vue-tiny-slider": "^0.1.39", "vuex": "^3.6.2", - "youtubei.js": "^7.0.0" + "youtubei.js": "^8.1.0" }, "devDependencies": { - "@babel/core": "^7.23.3", + "@babel/core": "^7.23.7", "@babel/eslint-parser": "^7.23.3", "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/preset-env": "^7.23.3", - "@double-great/stylelint-a11y": "^2.0.2", + "@babel/preset-env": "^7.23.7", + "@double-great/stylelint-a11y": "^3.0.0", "babel-loader": "^9.1.3", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.8.1", "css-minimizer-webpack-plugin": "^5.0.1", - "electron": "^27.1.0", - "electron-builder": "^24.6.4", - "eslint": "^8.54.0", - "eslint-config-prettier": "^9.0.0", + "electron": "^28.1.0", + "electron-builder": "^24.9.1", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.29.0", - "eslint-plugin-jsonc": "^2.10.0", - "eslint-plugin-n": "^16.3.1", - "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jsonc": "^2.11.2", + "eslint-plugin-n": "^16.6.0", + "eslint-plugin-prettier": "^5.1.2", "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-unicorn": "^49.0.0", - "eslint-plugin-vue": "^9.18.1", + "eslint-plugin-unicorn": "^50.0.1", + "eslint-plugin-vue": "^9.19.2", "eslint-plugin-vuejs-accessibility": "^2.2.0", - "eslint-plugin-yml": "^1.10.0", - "html-webpack-plugin": "^5.5.3", + "eslint-plugin-yml": "^1.11.0", + "html-webpack-plugin": "^5.6.0", "js-yaml": "^4.1.0", "json-minimizer-webpack-plugin": "^4.0.0", - "lefthook": "^1.5.2", + "lefthook": "^1.5.5", "mini-css-extract-plugin": "^2.7.6", "npm-run-all": "^4.1.5", - "postcss": "^8.4.31", + "postcss": "^8.4.32", "postcss-scss": "^4.0.9", "prettier": "^2.8.8", "rimraf": "^5.0.5", - "sass": "^1.69.5", - "sass-loader": "^13.3.2", - "stylelint": "^15.11.0", - "stylelint-config-sass-guidelines": "^10.0.0", - "stylelint-config-standard": "^34.0.0", - "stylelint-high-performance-animation": "^1.9.0", - "stylelint-use-logical-spec": "^5.0.0", + "sass": "^1.69.6", + "sass-loader": "^13.3.3", + "stylelint": "^16.1.0", + "stylelint-config-sass-guidelines": "^11.0.0", + "stylelint-config-standard": "^36.0.0", + "stylelint-high-performance-animation": "^1.10.0", + "stylelint-use-logical-spec": "^5.0.1", "tree-kill": "1.2.2", "vue-devtools": "^5.1.4", "vue-eslint-parser": "^9.3.2", diff --git a/src/constants.js b/src/constants.js index bd80da2ffdc17..f1d68b592466a 100644 --- a/src/constants.js +++ b/src/constants.js @@ -44,7 +44,7 @@ const DBActions = { PLAYLISTS: { UPSERT_VIDEO: 'db-action-playlists-upsert-video-by-playlist-name', - UPSERT_VIDEO_IDS: 'db-action-playlists-upsert-video-ids-by-playlist-id', + UPSERT_VIDEOS: 'db-action-playlists-upsert-videos-by-playlist-name', DELETE_VIDEO_ID: 'db-action-playlists-delete-video-by-playlist-name', DELETE_VIDEO_IDS: 'db-action-playlists-delete-video-ids', DELETE_ALL_VIDEOS: 'db-action-playlists-delete-all-videos' diff --git a/src/datastores/handlers/base.js b/src/datastores/handlers/base.js index 71e1ca3889682..070ebda8e8e2a 100644 --- a/src/datastores/handlers/base.js +++ b/src/datastores/handlers/base.js @@ -64,8 +64,8 @@ class History { return db.history.updateAsync({ videoId }, { $set: { watchProgress } }, { upsert: true }) } - static updateLastViewedPlaylist(videoId, lastViewedPlaylistId) { - return db.history.updateAsync({ videoId }, { $set: { lastViewedPlaylistId } }, { upsert: true }) + static updateLastViewedPlaylist(videoId, lastViewedPlaylistId, lastViewedPlaylistType, lastViewedPlaylistItemId) { + return db.history.updateAsync({ videoId }, { $set: { lastViewedPlaylistId, lastViewedPlaylistType, lastViewedPlaylistItemId } }, { upsert: true }) } static delete(videoId) { @@ -112,18 +112,22 @@ class Playlists { return db.playlists.findAsync({}) } - static upsertVideoByPlaylistName(playlistName, videoData) { + static upsert(playlist) { + return db.playlists.updateAsync({ _id: playlist._id }, { $set: playlist }, { upsert: true }) + } + + static upsertVideoByPlaylistId(_id, videoData) { return db.playlists.updateAsync( - { playlistName }, + { _id }, { $push: { videos: videoData } }, { upsert: true } ) } - static upsertVideoIdsByPlaylistId(_id, videoIds) { + static upsertVideosByPlaylistId(_id, videos) { return db.playlists.updateAsync( { _id }, - { $push: { videos: { $each: videoIds } } }, + { $push: { videos: { $each: videos } } }, { upsert: true } ) } @@ -132,25 +136,25 @@ class Playlists { return db.playlists.removeAsync({ _id, protected: { $ne: true } }) } - static deleteVideoIdByPlaylistName(playlistName, videoId) { + static deleteVideoIdByPlaylistId(_id, playlistItemId) { return db.playlists.updateAsync( - { playlistName }, - { $pull: { videos: { videoId } } }, + { _id }, + { $pull: { videos: { playlistItemId } } }, { upsert: true } ) } - static deleteVideoIdsByPlaylistName(playlistName, videoIds) { + static deleteVideoIdsByPlaylistId(_id, videoIds) { return db.playlists.updateAsync( - { playlistName }, + { _id }, { $pull: { videos: { $in: videoIds } } }, { upsert: true } ) } - static deleteAllVideosByPlaylistName(playlistName) { + static deleteAllVideosByPlaylistId(_id) { return db.playlists.updateAsync( - { playlistName }, + { _id }, { $set: { videos: [] } }, { upsert: true } ) @@ -161,7 +165,7 @@ class Playlists { } static deleteAll() { - return db.playlists.removeAsync({ protected: { $ne: true } }) + return db.playlists.removeAsync({}, { multi: true }) } static persist() { @@ -174,7 +178,7 @@ function compactAllDatastores() { Settings.persist(), History.persist(), Profiles.persist(), - Playlists.persist() + Playlists.persist(), ]) } @@ -184,7 +188,7 @@ const baseHandlers = { profiles: Profiles, playlists: Playlists, - compactAllDatastores + compactAllDatastores, } export default baseHandlers diff --git a/src/datastores/handlers/electron.js b/src/datastores/handlers/electron.js index ddb90ff82434e..d84ccac13c78d 100644 --- a/src/datastores/handlers/electron.js +++ b/src/datastores/handlers/electron.js @@ -42,12 +42,12 @@ class History { ) } - static updateLastViewedPlaylist(videoId, lastViewedPlaylistId) { + static updateLastViewedPlaylist(videoId, lastViewedPlaylistId, lastViewedPlaylistType, lastViewedPlaylistItemId) { return ipcRenderer.invoke( IpcChannels.DB_HISTORY, { action: DBActions.HISTORY.UPDATE_PLAYLIST, - data: { videoId, lastViewedPlaylistId } + data: { videoId, lastViewedPlaylistId, lastViewedPlaylistType, lastViewedPlaylistItemId } } ) } @@ -126,22 +126,29 @@ class Playlists { ) } - static upsertVideoByPlaylistName(playlistName, videoData) { + static upsert(playlist) { + return ipcRenderer.invoke( + IpcChannels.DB_PLAYLISTS, + { action: DBActions.GENERAL.UPSERT, data: playlist } + ) + } + + static upsertVideoByPlaylistId(_id, videoData) { return ipcRenderer.invoke( IpcChannels.DB_PLAYLISTS, { action: DBActions.PLAYLISTS.UPSERT_VIDEO, - data: { playlistName, videoData } + data: { _id, videoData } } ) } - static upsertVideoIdsByPlaylistId(_id, videoIds) { + static upsertVideosByPlaylistId(_id, videos) { return ipcRenderer.invoke( IpcChannels.DB_PLAYLISTS, { - action: DBActions.PLAYLISTS.UPSERT_VIDEO_IDS, - data: { _id, videoIds } + action: DBActions.PLAYLISTS.UPSERT_VIDEOS, + data: { _id, videos } } ) } @@ -153,32 +160,32 @@ class Playlists { ) } - static deleteVideoIdByPlaylistName(playlistName, videoId) { + static deleteVideoIdByPlaylistId(_id, playlistItemId) { return ipcRenderer.invoke( IpcChannels.DB_PLAYLISTS, { action: DBActions.PLAYLISTS.DELETE_VIDEO_ID, - data: { playlistName, videoId } + data: { _id, playlistItemId } } ) } - static deleteVideoIdsByPlaylistName(playlistName, videoIds) { + static deleteVideoIdsByPlaylistId(_id, videoIds) { return ipcRenderer.invoke( IpcChannels.DB_PLAYLISTS, { action: DBActions.PLAYLISTS.DELETE_VIDEO_IDS, - data: { playlistName, videoIds } + data: { _id, videoIds } } ) } - static deleteAllVideosByPlaylistName(playlistName) { + static deleteAllVideosByPlaylistId(_id) { return ipcRenderer.invoke( IpcChannels.DB_PLAYLISTS, { action: DBActions.PLAYLISTS.DELETE_ALL_VIDEOS, - data: playlistName + data: _id } ) } diff --git a/src/datastores/handlers/web.js b/src/datastores/handlers/web.js index a81eb305d1dd9..103d93d441a2b 100644 --- a/src/datastores/handlers/web.js +++ b/src/datastores/handlers/web.js @@ -33,8 +33,8 @@ class History { return baseHandlers.history.updateWatchProgress(videoId, watchProgress) } - static updateLastViewedPlaylist(videoId, lastViewedPlaylistId) { - return baseHandlers.history.updateLastViewedPlaylist(videoId, lastViewedPlaylistId) + static updateLastViewedPlaylist(videoId, lastViewedPlaylistId, lastViewedPlaylistType, lastViewedPlaylistItemId) { + return baseHandlers.history.updateLastViewedPlaylist(videoId, lastViewedPlaylistId, lastViewedPlaylistType, lastViewedPlaylistItemId) } static delete(videoId) { @@ -81,28 +81,32 @@ class Playlists { return baseHandlers.playlists.find() } - static upsertVideoByPlaylistName(playlistName, videoData) { - return baseHandlers.playlists.upsertVideoByPlaylistName(playlistName, videoData) + static upsert(playlist) { + return baseHandlers.playlists.upsert(playlist) } - static upsertVideoIdsByPlaylistId(_id, videoIds) { - return baseHandlers.playlists.upsertVideoIdsByPlaylistId(_id, videoIds) + static upsertVideoByPlaylistId(_id, videoData) { + return baseHandlers.playlists.upsertVideoByPlaylistId(_id, videoData) + } + + static upsertVideosByPlaylistId(_id, videoData) { + return baseHandlers.playlists.upsertVideosByPlaylistId(_id, videoData) } static delete(_id) { return baseHandlers.playlists.delete(_id) } - static deleteVideoIdByPlaylistName(playlistName, videoId) { - return baseHandlers.playlists.deleteVideoIdByPlaylistName(playlistName, videoId) + static deleteVideoIdByPlaylistId(_id, playlistItemId) { + return baseHandlers.playlists.deleteVideoIdByPlaylistId(_id, playlistItemId) } - static deleteVideoIdsByPlaylistName(playlistName, videoIds) { - return baseHandlers.playlists.deleteVideoIdsByPlaylistName(playlistName, videoIds) + static deleteVideoIdsByPlaylistId(_id, videoIds) { + return baseHandlers.playlists.deleteVideoIdsByPlaylistId(_id, videoIds) } - static deleteAllVideosByPlaylistName(playlistName) { - return baseHandlers.playlists.deleteAllVideosByPlaylistName(playlistName) + static deleteAllVideosByPlaylistId(_id) { + return baseHandlers.playlists.deleteAllVideosByPlaylistId(_id) } static deleteMultiple(ids) { diff --git a/src/main/index.js b/src/main/index.js index 63e7abb15d789..0ca9917a3c86d 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -170,6 +170,10 @@ function runApp() { app.commandLine.appendSwitch('enable-file-cookies') app.commandLine.appendSwitch('ignore-gpu-blacklist') + // Work around for context menus in the devtools being displayed behind the window + // https://github.com/electron/electron/issues/38790 + app.commandLine.appendSwitch('disable-features', 'WidgetLayering') + // command line switches need to be added before the app ready event first // that means we can't use the normal settings system as that is asynchronous, // doing it synchronously ensures that we add it before the event fires @@ -843,7 +847,7 @@ function runApp() { return null case DBActions.HISTORY.UPDATE_PLAYLIST: - await baseHandlers.history.updateLastViewedPlaylist(data.videoId, data.lastViewedPlaylistId) + await baseHandlers.history.updateLastViewedPlaylist(data.videoId, data.lastViewedPlaylistId, data.lastViewedPlaylistType, data.lastViewedPlaylistItemId) syncOtherWindows( IpcChannels.SYNC_HISTORY, event, @@ -944,15 +948,27 @@ function runApp() { switch (action) { case DBActions.GENERAL.CREATE: await baseHandlers.playlists.create(data) - // TODO: Syncing (implement only when it starts being used) - // syncOtherWindows(IpcChannels.SYNC_PLAYLISTS, event, { event: '_', data }) + syncOtherWindows( + IpcChannels.SYNC_PLAYLISTS, + event, + { event: SyncEvents.GENERAL.CREATE, data } + ) return null case DBActions.GENERAL.FIND: return await baseHandlers.playlists.find() + case DBActions.GENERAL.UPSERT: + await baseHandlers.playlists.upsert(data) + syncOtherWindows( + IpcChannels.SYNC_PLAYLISTS, + event, + { event: SyncEvents.GENERAL.UPSERT, data } + ) + return null + case DBActions.PLAYLISTS.UPSERT_VIDEO: - await baseHandlers.playlists.upsertVideoByPlaylistName(data.playlistName, data.videoData) + await baseHandlers.playlists.upsertVideoByPlaylistId(data._id, data.videoData) syncOtherWindows( IpcChannels.SYNC_PLAYLISTS, event, @@ -960,20 +976,26 @@ function runApp() { ) return null - case DBActions.PLAYLISTS.UPSERT_VIDEO_IDS: - await baseHandlers.playlists.upsertVideoIdsByPlaylistId(data._id, data.videoIds) - // TODO: Syncing (implement only when it starts being used) - // syncOtherWindows(IpcChannels.SYNC_PLAYLISTS, event, { event: '_', data }) + case DBActions.PLAYLISTS.UPSERT_VIDEOS: + await baseHandlers.playlists.upsertVideosByPlaylistId(data._id, data.videos) + syncOtherWindows( + IpcChannels.SYNC_PLAYLISTS, + event, + { event: SyncEvents.PLAYLISTS.UPSERT_VIDEOS, data } + ) return null case DBActions.GENERAL.DELETE: await baseHandlers.playlists.delete(data) - // TODO: Syncing (implement only when it starts being used) - // syncOtherWindows(IpcChannels.SYNC_PLAYLISTS, event, { event: '_', data }) + syncOtherWindows( + IpcChannels.SYNC_PLAYLISTS, + event, + { event: SyncEvents.GENERAL.DELETE, data } + ) return null case DBActions.PLAYLISTS.DELETE_VIDEO_ID: - await baseHandlers.playlists.deleteVideoIdByPlaylistName(data.playlistName, data.videoId) + await baseHandlers.playlists.deleteVideoIdByPlaylistId(data._id, data.playlistItemId) syncOtherWindows( IpcChannels.SYNC_PLAYLISTS, event, @@ -982,13 +1004,13 @@ function runApp() { return null case DBActions.PLAYLISTS.DELETE_VIDEO_IDS: - await baseHandlers.playlists.deleteVideoIdsByPlaylistName(data.playlistName, data.videoIds) + await baseHandlers.playlists.deleteVideoIdsByPlaylistId(data._id, data.videoIds) // TODO: Syncing (implement only when it starts being used) // syncOtherWindows(IpcChannels.SYNC_PLAYLISTS, event, { event: '_', data }) return null case DBActions.PLAYLISTS.DELETE_ALL_VIDEOS: - await baseHandlers.playlists.deleteAllVideosByPlaylistName(data) + await baseHandlers.playlists.deleteAllVideosByPlaylistId(data) // TODO: Syncing (implement only when it starts being used) // syncOtherWindows(IpcChannels.SYNC_PLAYLISTS, event, { event: '_', data }) return null @@ -1330,6 +1352,13 @@ function runApp() { }, type: 'normal' }, + { + label: 'Profile Manager', + click: (_menuItem, browserWindow, _event) => { + navigateTo('/settings/profile/', browserWindow) + }, + type: 'normal' + }, ].filter((v) => v !== false), }, { diff --git a/src/renderer/App.js b/src/renderer/App.js index ea50ffe27bc2b..e152ea8fb2d27 100644 --- a/src/renderer/App.js +++ b/src/renderer/App.js @@ -9,6 +9,8 @@ import FtPrompt from './components/ft-prompt/ft-prompt.vue' import FtButton from './components/ft-button/ft-button.vue' import FtToast from './components/ft-toast/ft-toast.vue' import FtProgressBar from './components/ft-progress-bar/ft-progress-bar.vue' +import FtPlaylistAddVideoPrompt from './components/ft-playlist-add-video-prompt/ft-playlist-add-video-prompt.vue' +import FtCreatePlaylistPrompt from './components/ft-create-playlist-prompt/ft-create-playlist-prompt.vue' import { marked } from 'marked' import { IpcChannels } from '../constants' import packageDetails from '../../package.json' @@ -28,7 +30,9 @@ export default defineComponent({ FtPrompt, FtButton, FtToast, - FtProgressBar + FtProgressBar, + FtPlaylistAddVideoPrompt, + FtCreatePlaylistPrompt, }, data: function () { return { @@ -66,6 +70,12 @@ export default defineComponent({ checkForBlogPosts: function () { return this.$store.getters.getCheckForBlogPosts }, + showAddToPlaylistPrompt: function () { + return this.$store.getters.getShowAddToPlaylistPrompt + }, + showCreatePlaylistPrompt: function () { + return this.$store.getters.getShowCreatePlaylistPrompt + }, windowTitle: function () { const routeTitle = this.$route.meta.title if (routeTitle !== 'Channel' && routeTitle !== 'Watch' && routeTitle !== 'Hashtag') { @@ -183,8 +193,8 @@ export default defineComponent({ }) this.$router.onReady(() => { - if (this.$router.currentRoute.path !== this.landingPage && this.landingPage !== '/subscriptions') { - this.$router.push({ path: this.landingPage }) + if (this.$router.currentRoute.path === '/') { + this.$router.replace({ path: this.landingPage }) } }) }) diff --git a/src/renderer/App.vue b/src/renderer/App.vue index e0394fae9711b..cb563b887b91f 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -74,6 +74,12 @@ :option-values="externalLinkOpeningPromptValues" @click="handleExternalLinkOpeningPromptAnswer" /> + +