diff --git a/.all-contributorsrc b/.all-contributorsrc index 182e3657e..b79ce3ba5 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1,7 +1,5 @@ { - "files": [ - "README.md" - ], + "files": ["README.md"], "imageSize": 100, "commit": false, "contributors": [ @@ -10,73 +8,56 @@ "name": "cyacedev", "avatar_url": "https://avatars0.githubusercontent.com/u/46712905?v=4", "profile": "https://github.com/cyacedev", - "contributions": [ - "code", - "doc" - ] + "contributions": ["code", "doc"] }, { "login": "ckVendrix", "name": "Vendrix", "avatar_url": "https://avatars2.githubusercontent.com/u/51775140?v=4", "profile": "https://github.com/ckVendrix", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "Mennaruuk", "name": "Mennaruuk", "avatar_url": "https://avatars.githubusercontent.com/u/52135169?v=4", "profile": "https://github.com/Mennaruuk", - "contributions": [ - "doc" - ] + "contributions": ["doc"] }, { "login": "mizzunet", "name": "Missu", "avatar_url": "https://avatars.githubusercontent.com/u/10193999?v=4", "profile": "https://github.com/mizzunet", - "contributions": [ - "doc" - ] + "contributions": ["doc"] }, { "login": "beardeddude", "name": "beardeddude", "avatar_url": "https://avatars.githubusercontent.com/u/36680638?v=4", "profile": "https://github.com/beardeddude", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "mattfbacon", "name": "Matt Fellenz", "avatar_url": "https://avatars.githubusercontent.com/u/58113890?v=4", "profile": "http://matt.felle.nz", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "themisir", "name": "Misir", "avatar_url": "https://avatars.githubusercontent.com/u/29130291?v=4", "profile": "http://themisir.com", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "alvanrahimli", "name": "Alvan Rahimli", "avatar_url": "https://avatars.githubusercontent.com/u/41202771?v=4", "profile": "https://www.rahim.li", - "contributions": [ - "code" - ] + "contributions": ["code"] }, { "login": "chonsser", diff --git a/.dockerignore b/.dockerignore index a7da65b36..d11ba8cc4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -30,3 +30,5 @@ client/.output client/.nuxt server/node_modules server/dist +shared/node_modules +shared/dist \ No newline at end of file diff --git a/.github/workflows/build-workflow.yml b/.github/workflows/build-workflow.yml index be6a92e80..db4cc2755 100644 --- a/.github/workflows/build-workflow.yml +++ b/.github/workflows/build-workflow.yml @@ -38,7 +38,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v2 with: - version: 8 + version: 9 run_install: false - name: Generate build metadata diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index af79f1c22..80077e41a 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -8,7 +8,7 @@ jobs: pr-job: strategy: matrix: - command: ['build', 'test', 'lint:client', 'lint:server'] + command: ['build', 'lint'] name: ${{ matrix.command }} timeout-minutes: 60 @@ -26,7 +26,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v2 with: - version: 8 + version: 9 run_install: false - name: Get pnpm store directory @@ -45,6 +45,9 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Prepare .nuxt directory + run: pnpm run nuxt:prepare + - name: Generate build metadata run: | pnpm build:metadata @@ -68,7 +71,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v2 with: - version: 8 + version: 9 run_install: false - name: Install e2e test dependencies diff --git a/.github/workflows/test-workflow.yml b/.github/workflows/test-workflow.yml index f9f8bc66d..f7ca4b458 100644 --- a/.github/workflows/test-workflow.yml +++ b/.github/workflows/test-workflow.yml @@ -23,7 +23,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v2 with: - version: 8 + version: 9 run_install: false - name: Install e2e test dependencies diff --git a/.gitignore b/.gitignore index f52324aae..d131d2dbf 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,7 @@ sw.* out/ out/* -data/* +data !/server/data output/* .vs/* @@ -44,6 +44,7 @@ dump.rdb tests/cypress/screenshots tests/cypress/videos tests/cypress/downloads +tests/cypress/environment/data Dockerfile.nodejs-mongodb .vscode/.ropeproject/ @@ -55,3 +56,4 @@ client/buildMetadata.json **/cache generated.txt original.txt +screenshots diff --git a/.npmrc b/.npmrc deleted file mode 100644 index c483022c0..000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -shamefully-hoist=true \ No newline at end of file diff --git a/.pnpmfile.cjs b/.pnpmfile.cjs deleted file mode 100644 index ec7e9f136..000000000 --- a/.pnpmfile.cjs +++ /dev/null @@ -1,13 +0,0 @@ -function readPackage(pkg, context) { - if (process.env.CI === 'true') { - delete pkg?.scripts?.prepare; - } - - return pkg; -} - -module.exports = { - hooks: { - readPackage - } -}; diff --git a/.prettierignore b/.prettierignore index fdca7354a..12256171e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,9 +12,13 @@ client/assets/fonts server/data server/node_modules server/dist +server/src/metadata.ts # Shared shared/node_modules +shared/src/api.schema.ts # Tests tests/node_modules + +.all-contributorsrc diff --git a/.prettierrc.json b/.prettierrc.json index 7565478df..38070c202 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -9,5 +9,6 @@ "endOfLine": "lf", "printWidth": 100, "vueIndentScriptAndStyle": false, - "quoteProps": "as-needed" + "quoteProps": "as-needed", + "plugins": ["prettier-plugin-organize-imports"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index c6e2e98f6..b1bb3af5c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "prettier.prettierPath": "node_modules/prettier/index.cjs", "typescript.tsdk": "node_modules/typescript/lib", - "githubPullRequests.ignoredPullRequestBranches": ["development"] + "githubPullRequests.ignoredPullRequestBranches": ["development"], + "eslint.experimental.useFlatConfig": true } diff --git a/CHANGELOG.md b/CHANGELOG.md index b9db35ab3..75e8fd522 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ This file is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.16.0] + +### Added + +- Brand new video player (Checkout the wiki: https://viewtube.wiki/features/videoplayer) [#2388] + ## [0.15.4] ### Fixed @@ -86,7 +92,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking ⚠️ - Disable clustering by default [#2377](https://github.com/ViewTube/viewtube/pull/2377) - - Clustering is now disabled by default. If you want to use clustering, you have to set the `VIEWTUBE_CLUSTERED` environment variable to `true`. + - Clustering is controlled by the `VIEWTUBE_CLUSTERED` environment variable [viewtube.wiki > advanced configuration](https://viewtube.wiki/installation/configuration#advanced-configuration). + If you have disabled clustering previously, you don't need to change anything. + If you want to keep clustering enabled, you need to set the `VIEWTUBE_CLUSTERED` environment variable to `true`. ### Added @@ -104,6 +112,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix "window is not defined" when logged in and "Rewrite YouTube URLs" is enabled [#2469](https://github.com/ViewTube/viewtube/pull/2469) - Fix certain search terms not working [#2493](https://github.com/ViewTube/viewtube/pull/2493) +## Removed + +- Remove unused VIEWTUBE_YOUTUBE_IDENTIFIER environment variable [#2388](https://github.com/ViewTube/viewtube/pull/2388) + ## [0.13.1] ### Fixed @@ -427,7 +439,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Initial release -[unreleased]: https://github.com/viewtube/viewtube/compare/v0.15.4...development +[unreleased]: https://github.com/viewtube/viewtube/compare/v0.16.0...development +[0.16.0]: https://github.com/viewtube/viewtube/compare/v0.15.4...v0.16.0 [0.15.4]: https://github.com/viewtube/viewtube/compare/v0.15.3...v0.15.4 [0.15.3]: https://github.com/viewtube/viewtube/compare/v0.15.2...v0.15.3 [0.15.2]: https://github.com/viewtube/viewtube/compare/v0.15.1...v0.15.2 diff --git a/Dockerfile b/Dockerfile index 26d6d6bf4..03b91f3db 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,17 @@ FROM node:20-bookworm as build WORKDIR /home/build -ENV NUXT_BUILD=true - -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY server/package.json ./server/ -COPY client/package.json ./client/ COPY shared/package.json ./shared/ +COPY client/package.json ./client/ +COPY client/scripts ./client/scripts COPY patches ./patches -RUN npm install -g pnpm@8.15.7 +RUN npm install -g pnpm@9.1.3 -RUN pnpm --filter=./server --filter=./client install --frozen-lockfile +RUN pnpm install --frozen-lockfile COPY . . @@ -30,12 +29,15 @@ ENV NODE_ENV=production COPY --from=build /home/build/package.json ./ COPY --from=build /home/build/client/package.json ./client/ COPY --from=build /home/build/server/package.json ./server/ +COPY --from=build /home/build/shared/package.json ./shared/ COPY --from=build /home/build/node_modules ./node_modules COPY --from=build /home/build/server/node_modules ./server/node_modules COPY --from=build /home/build/server/dist ./server/dist/ +COPY --from=build /home/build/shared/dist ./shared/dist/ + COPY --from=build /home/build/client/.output ./client/.output/ RUN \ @@ -48,4 +50,4 @@ ENV VIEWTUBE_BASE_DIR=/home/app HEALTHCHECK --interval=30s --timeout=20s --start-period=60s CMD wget --no-verbose --tries=3 --spider http://localhost:8066/ || exit 1 EXPOSE 8066 -CMD ["node", "/home/app/server/dist/main.cjs"] +CMD ["node", "/home/app/server/dist/main.js"] diff --git a/README.md b/README.md index 18719edbd..f09936b69 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@ ViewTube is an alternative YouTube frontend that lets you watch, search and disc It's built using [Nuxt](https://nuxt.com/) and [Nest](https://nestjs.com/). > [!NOTE] -> You can find the documentation at [viewtube.wiki](https://viewtube.wiki) -Check [uptime.m-o.dev/status](https://uptime.m-o.dev/status) for updates and downtime status. +> You can find the documentation at [viewtube.wiki](https://viewtube.wiki). +> Check [uptime.m-o.dev/status](https://uptime.m-o.dev/status) for updates and downtime status.

diff --git a/client/.eslintignore b/client/.eslintignore deleted file mode 100644 index 9366975b1..000000000 --- a/client/.eslintignore +++ /dev/null @@ -1,6 +0,0 @@ -dist/* -.nuxt/* -.output/* -node_modules/* - -public/notifications-sw.js diff --git a/client/.eslintrc.js b/client/.eslintrc.js deleted file mode 100644 index 8fab0807f..000000000 --- a/client/.eslintrc.js +++ /dev/null @@ -1,25 +0,0 @@ -module.exports = { - env: { - browser: true, - es2022: true, - node: true - }, - - extends: ['@nuxt/eslint-config', 'prettier'], - - rules: { - 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', - 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', - 'space-before-function-paren': 'off', - 'no-unused-vars': 'off', - 'vue/multi-word-component-names': 'off', - 'vue/no-v-html': 'off', - '@typescript-eslint/no-unused-vars': [ - 'warn', - { - argsIgnorePattern: '^_', - varsIgnorePattern: '^_' - } - ] - } -}; diff --git a/client/assets/styles/global/variables.scss b/client/assets/styles/global/variables.scss index e3e90c6aa..ee7a99956 100644 --- a/client/assets/styles/global/variables.scss +++ b/client/assets/styles/global/variables.scss @@ -3,7 +3,7 @@ $bottom-overlay-height: 60px; $video-seekbar-height: 20px; $video-seekbar-line-height: 3px; $video-controls-height: $video-seekbar-height - $video-seekbar-line-height; -$video-player-gradient: linear-gradient(to bottom, #000000da, #0000); +$video-player-gradient: linear-gradient(to bottom, rgba(0, 0, 0, 0.94), transparent); $home-offset: 200px; @@ -14,9 +14,15 @@ $watch-break-width: 380px; $header-font: 'expletus-bold', 'Open Sans', sans-serif; $default-font: 'noto-sans', Arial, sans-serif; -$max-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22); -$medium-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); -$low-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); +$max-shadow: + 0 14px 28px rgba(0, 0, 0, 0.25), + 0 10px 10px rgba(0, 0, 0, 0.22); +$medium-shadow: + 0 3px 6px rgba(0, 0, 0, 0.16), + 0 3px 6px rgba(0, 0, 0, 0.23); +$low-shadow: + 0 1px 3px rgba(0, 0, 0, 0.12), + 0 1px 2px rgba(0, 0, 0, 0.24); $intro-easing: cubic-bezier(0.31, 0.65, 0.26, 0.97); $outro-easing: cubic-bezier(0.71, 0.21, 1, 0.46); diff --git a/client/assets/styles/layout.scss b/client/assets/styles/layout.scss new file mode 100644 index 000000000..7a9aadff4 --- /dev/null +++ b/client/assets/styles/layout.scss @@ -0,0 +1,207 @@ +.x-ripple-js__ripple-button { + position: relative; + overflow: hidden; + display: block; +} + +.x-ripple-js__ripple-button > span.x-ripple { + position: absolute; + border-radius: 50%; + transform: scale(0); + animation: x-ripple-animation 800ms cubic-bezier(0.17, 0.67, 0.57, 0.94) forwards; + transition: opacity 400ms linear; + background-color: rgba(255, 255, 255, 0.7); + filter: blur(40px); +} + +.x-ripple-js__ripple-button > span.x-ripple.x-ripple-release { + opacity: 0; +} + +@keyframes x-ripple-animation { + to { + transform: scale(3); + } +} + +.blur-enter-active, +.blur-leave-active { + transition: all 0.4s; +} +.blur-enter-from, +.blur-leave-to { + opacity: 0; + filter: blur(1rem); +} + +vite-error-overlay { + z-index: 10000; +} +#app { + font-family: 'noto-sans', Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: var(--title-color); + background-color: var(--bgcolor-main); + + .progress-bar-margin { + top: $header-height - 3px !important; + } + + .content { + width: 100%; + box-sizing: border-box; + } +} +* { + scrollbar-color: var(--theme-color) transparent; + scrollbar-width: thin; + touch-action: manipulation; + -webkit-tap-highlight-color: transparent; + + &:focus-visible { + outline: none; + } + + &::selection { + background: var(--theme-color); + color: var(--title-color); + } + + &::-moz-selection { + background: var(--theme-color); + } + + &::-webkit-scrollbar { + height: 6px; + width: 6px; + background: var(--bgcolor-main); + } + + &::-webkit-scrollbar-thumb { + background: var(--theme-color); + border-radius: 0; + -webkit-border-radius: 0; + } + + &::-webkit-scrollbar-corner { + background: var(--bgcolor-main); + } +} + +.ripple * { + pointer-events: none !important; +} +body.transition-all * { + transition: + background-color 300ms ease, + color 300ms ease, + opacity 300ms ease; +} +p, +h1, +h2, +h3, +h4, +h5, +h6, +a { + margin: 0; +} +div.links, +pre.links, +span.links { + a { + text-decoration: none; + color: var(--theme-color-alt); + position: relative; + transition: + background-size 300ms $dynamic-easing, + color 300ms $intro-easing; + background-image: $theme-color-primary-gradient; + background-size: 0% 2px; + background-position: 0 100%; + background-repeat: no-repeat; + + &:hover { + color: var(--theme-color); + background-size: 100% 2px; + } + } +} +html, +body, +#__layout, +#__nuxt { + margin: 0; + padding: 0; + overflow-x: hidden; + background-color: var(--bgcolor-main); + color: var(--title-color); +} +div, +p { + font-family: noto-sans, Arial, sans-serif; +} + +.centered { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +a, +button, +.selectable { + position: relative; + &::after { + box-shadow: 0 0 0 2px transparent; + transition: box-shadow 100ms linear; + content: ''; + position: absolute; + left: 2px; + top: 2px; + width: calc(100% - 4px); + height: calc(100% - 4px); + pointer-events: none; + user-select: none; + z-index: +1; + border-radius: inherit; + } + + &:focus { + &::after { + box-shadow: 0 0 0 2px var(--theme-color); + } + outline: none; + } +} + +input:focus { + outline: solid 2px var(--theme-color); +} + +textarea:focus { + outline: none; +} + +a { + text-decoration: none; + color: unset; +} +.monospace { + display: inline-block; + background-color: #0000004d; + padding: 2px 5px; + font-family: monospace; + border-radius: 4px; +} +input:-webkit-autofill, +input:-webkit-autofill:hover, +input:-webkit-autofill:focus, +input:-webkit-autofill:active { + background-clip: text; + color: var(--title-color); + -webkit-text-fill-color: var(--title-color); +} diff --git a/client/assets/styles/popup.scss b/client/assets/styles/popup.scss index 2c6be2faf..a8f24d3d9 100644 --- a/client/assets/styles/popup.scss +++ b/client/assets/styles/popup.scss @@ -28,7 +28,7 @@ max-height: 80%; background-color: var(--bgcolor-alt); box-shadow: $medium-shadow; - border-radius: 3px; + border-radius: 5px; display: flex; flex-direction: column; align-items: flex-start; diff --git a/client/components/Comment.vue b/client/components/Comment.vue index fae1d900e..a929ff2d5 100644 --- a/client/components/Comment.vue +++ b/client/components/Comment.vue @@ -1,6 +1,7 @@