Compare commits

..

63 Commits

Author SHA1 Message Date
Tzahi12345
e2baa30d9a updated ngx-avatars 2025-08-16 00:48:42 -04:00
Tzahi12345
a74bca05cc Improved download fail backup method 2024-09-21 23:08:42 -04:00
Tzahi12345
7d3458ea41 Testing using npm registry mirror 2023-12-16 20:16:56 -05:00
Tzahi12345
6a7c1c9d0b Set docker npm install timeout to 60s 2023-12-16 19:48:27 -05:00
Tzahi12345
e2e3dd280a Added back python and added flag to pip to force install pycryptodomex 2023-12-16 02:18:32 -05:00
Tzahi12345
25bf7a6fdd Trying removal of python 2023-12-15 22:24:57 -05:00
Tzahi12345
c56987ddd5 Downgraded ubuntu to 23.04 2023-12-15 22:17:57 -05:00
Tzahi12345
72399b09e4 Removed libicu70 from fetch-twitch-downloader 2023-12-15 22:12:33 -05:00
Tzahi12345
4258b82040 Removed libicu70 2023-12-15 22:10:15 -05:00
Tzahi12345
7ac6a50b41 Updated UID/GID to 1001 2023-12-15 22:08:38 -05:00
Tzahi12345
fb92975b73 Updated docker Ubuntu to 24.04 2023-12-15 21:45:00 -05:00
Tzahi12345
b4cf1e39b9 Removed version from docker npm install 2023-12-15 21:44:25 -05:00
Tzahi12345
026f24a327 Updated npm version in Dockerfile 2023-12-09 02:06:49 -05:00
Tzahi12345
1bf348f481 Cleaned up pm2 installcommand 2023-12-09 00:40:10 -05:00
Tzahi12345
eb8cd3fd06 Remove curl install from pm2 2023-12-09 00:22:38 -05:00
Tzahi12345
f96ffab530 Install pm2 without npm 2023-12-09 00:18:35 -05:00
Tzahi12345
dcb53691e3 mocha is now a backend dev dependency 2023-12-08 22:49:08 -05:00
Tzahi12345
2cf21541bb Merge branch 'master' of https://github.com/Tzahi12345/YoutubeDL-Material into angular-17 2023-12-06 20:46:14 -05:00
Tzahi12345
13e46397e9 Merge pull request #1006 from Tzahi12345/dependabot/github_actions/dot-github/workflows/actions/checkout-4
Bump actions/checkout from 3 to 4 in /.github/workflows
2023-12-06 20:44:52 -05:00
Tzahi12345
7f079c56d0 Merge pull request #1008 from Tzahi12345/dependabot/github_actions/dot-github/workflows/docker/metadata-action-5
Bump docker/metadata-action from 4 to 5 in /.github/workflows
2023-12-06 20:44:39 -05:00
Tzahi12345
e082919cd0 Merge pull request #1009 from Tzahi12345/dependabot/github_actions/dot-github/workflows/docker/build-push-action-5
Bump docker/build-push-action from 4 to 5 in /.github/workflows
2023-12-06 20:44:29 -05:00
Tzahi12345
a89378b99f Merge pull request #1010 from Tzahi12345/dependabot/github_actions/dot-github/workflows/docker/login-action-3
Bump docker/login-action from 2 to 3 in /.github/workflows
2023-12-06 20:44:17 -05:00
Tzahi12345
4dc899439e Merge pull request #954 from weblate/weblate-youtubedl-material-ytdl-material
Translations update from Hosted Weblate
2023-12-06 20:43:57 -05:00
Tzahi12345
9b38c56528 Reverted using production as defaultConfiguration in angular.json 2023-12-05 22:15:35 -05:00
Tzahi12345
0644b194d0 Merge pull request #1039 from Tzahi12345/video-info-bug-fix
Video info bug fix
2023-12-03 21:26:42 -05:00
Isaac Abadi
344d959c05 Fixed issue where video info could not be retrieved
If youtube-dl update fails, error will show and server won't crash
2023-12-03 21:22:08 -05:00
Isaac Abadi
3912655912 Merge branch 'master' of https://github.com/Tzahi12345/YoutubeDL-Material into angular-17 2023-12-03 20:02:27 -05:00
Tzahi12345
cdf82abf3f Merge pull request #1038 from Tzahi12345/codespaces-support
Added codespaces support
2023-12-03 19:04:41 -05:00
Isaac Abadi
84464db0e0 Frontend dev environment now uses non-prod mode by default 2023-12-03 18:51:37 -05:00
Tzahi12345
4bf03bfd1a Added new Angular configuration for codespaces
Added CORS control support for codespaces
2023-12-03 19:11:21 +00:00
Tzahi12345
75cbe4d5d0 Added codespaces json 2023-12-03 07:39:53 +00:00
Isaac Abadi
9556f9c94f Updated docker frontend build node version to 18 2023-12-03 02:02:38 -05:00
Isaac Abadi
4a97fa4ef5 Updates node version in CI 2023-12-02 15:58:32 -05:00
Isaac Abadi
2c155b74a9 Updates version info/requirements in README 2023-12-02 15:57:19 -05:00
Isaac Abadi
25e4c114e8 Updated templates to new Angular control flow 2023-12-02 15:50:56 -05:00
Isaac Abadi
6152df3486 Added missing saveAs imports 2023-12-02 03:11:22 -05:00
Isaac Abadi
7cf5d86fc3 Updated styles.scss to match new Angular syntax
Added back ngx-avatars

Made required dependency updates
2023-12-02 03:10:06 -05:00
Isaac Abadi
f57e0ab187 Updated Angular Material to v17 2023-12-02 01:35:08 -05:00
Isaac Abadi
517c9e169d Updated to Angular 17 2023-12-02 01:30:40 -05:00
Isaac Abadi
69d8751484 Updated Angular Material to v16 2023-12-02 01:23:15 -05:00
Isaac Abadi
c3c8f50a92 Updated to Angular 16 2023-12-02 01:17:40 -05:00
Isaac Abadi
caadf4f9d2 Temporarily removed ngx-avatars 2023-12-02 01:06:48 -05:00
Isaac Abadi
d10401cead Force update ngx-avatars 2023-12-01 16:35:31 -05:00
Isaac Abadi
d02d100001 Force ngx-avatars to use angular 16 2023-12-01 16:32:33 -05:00
Tzahi12345
6b59446a37 Merge pull request #1032 from Tzahi12345/bug-fixes
Various bug fixes
2023-12-01 16:02:43 -05:00
Tzahi12345
d30c338189 Merge pull request #1035 from martadinata666/npm-cache-path
Fix `.npm` cache path
2023-12-01 10:23:15 -05:00
Dedy Martadinata S
509e996107 Fix .npm
Somehow on npm startup it created directory at  `/.npm` rather than `/app/.npm`, explicitly define `.npm` folder path
2023-12-01 18:56:03 +07:00
Maite Guix
353c35cd8d Translated using Weblate (Catalan)
Currently translated at 81.4% (394 of 484 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/ca/
2023-11-16 08:27:38 +01:00
Frankie McEyes
169a057c37 Translated using Weblate (Italian)
Currently translated at 100.0% (484 of 484 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/it/
2023-10-19 04:13:08 +00:00
dependabot[bot]
ab6d0f199e Bump docker/login-action from 2 to 3 in /.github/workflows
Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-12 11:44:38 +00:00
dependabot[bot]
ae48a4c195 Bump docker/build-push-action from 4 to 5 in /.github/workflows
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-12 11:44:35 +00:00
dependabot[bot]
241473b99d Bump docker/metadata-action from 4 to 5 in /.github/workflows
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4 to 5.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md)
- [Commits](https://github.com/docker/metadata-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-12 11:44:30 +00:00
dependabot[bot]
ba98548662 Bump actions/checkout from 3 to 4 in /.github/workflows
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-05 11:16:19 +00:00
Azurite
72419d7be9 Translated using Weblate (Japanese)
Currently translated at 99.7% (483 of 484 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/ja/
2023-09-04 15:54:36 +02:00
Reza Almanda
cc0fa03aca Translated using Weblate (Indonesian)
Currently translated at 100.0% (484 of 484 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/id/
2023-07-30 02:06:34 +02:00
Kachelkaiser
477cba93cd Translated using Weblate (German)
Currently translated at 100.0% (484 of 484 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/de/
2023-07-20 18:05:49 +02:00
Kachelkaiser
eda3dfcac7 Translated using Weblate (German)
Currently translated at 96.2% (466 of 484 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/de/
2023-07-19 17:07:51 +02:00
YMisterXY
188876e383 Translated using Weblate (Polish)
Currently translated at 94.8% (459 of 484 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/pl/
2023-07-04 21:18:03 +02:00
maboroshin
2c70e1367d Translated using Weblate (Japanese)
Currently translated at 57.0% (276 of 484 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/ja/
2023-06-28 15:52:07 +02:00
Fernando Alves
7012524c61 Translated using Weblate (Portuguese (Brazil))
Currently translated at 22.3% (108 of 484 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/pt_BR/
2023-06-28 15:52:06 +02:00
Matrix
cc6dfbf928 Translated using Weblate (Dutch)
Currently translated at 100.0% (484 of 484 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/nl/
2023-06-28 15:52:04 +02:00
Quy
6ebda81225 Added translation using Weblate (Vietnamese) 2023-06-23 11:42:14 +02:00
Ettore Atalan
a50476ac58 Translated using Weblate (German)
Currently translated at 95.8% (464 of 484 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/de/
2023-06-04 21:55:21 +02:00
84 changed files with 18917 additions and 9552 deletions

View File

@@ -0,0 +1,39 @@
// README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node
{
"name": "Node.js",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/javascript-node:0-18-bullseye",
"features": {
"ghcr.io/devcontainers-contrib/features/jshint:2": {},
"ghcr.io/devcontainers-contrib/features/angular-cli:2": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [4200, 17442],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "npm install && cd backend && npm install",
// Configure tool-specific properties.
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"ms-python.python",
"Angular.ng-template",
"dbaeumer.vscode-eslint",
"waderyan.gitblame",
"42Crunch.vscode-openapi",
"christian-kohler.npm-intellisense",
"redhat.vscode-yaml",
"hbenl.vscode-mocha-test-adapter",
"DavidAnson.vscode-markdownlint"
]
}
}
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

View File

@@ -13,11 +13,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: checkout code - name: checkout code
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: setup node - name: setup node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: '16' node-version: '18'
cache: 'npm' cache: 'npm'
- name: install dependencies - name: install dependencies
run: | run: |
@@ -65,7 +65,7 @@ jobs:
if: contains(github.ref, '/tags/v') if: contains(github.ref, '/tags/v')
steps: steps:
- name: checkout code - name: checkout code
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: create release - name: create release
id: create_release id: create_release
uses: actions/create-release@v1 uses: actions/create-release@v1

View File

@@ -30,7 +30,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
# We must fetch at least the immediate parents so that if this is # We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head. # a pull request then we can checkout the head.

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: checkout code - name: checkout code
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Set hash - name: Set hash
id: vars id: vars
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
@@ -28,7 +28,7 @@ jobs:
- name: setup multi-arch docker build - name: setup multi-arch docker build
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
- name: build & push images - name: build & push images
uses: docker/build-push-action@v4 uses: docker/build-push-action@v5
with: with:
context: . context: .
file: ./Dockerfile file: ./Dockerfile

View File

@@ -15,7 +15,7 @@ jobs:
steps: steps:
- name: checkout code - name: checkout code
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Set hash - name: Set hash
id: vars id: vars
@@ -47,7 +47,7 @@ jobs:
- name: Generate Docker image metadata - name: Generate Docker image metadata
id: docker-meta id: docker-meta
uses: docker/metadata-action@v4 uses: docker/metadata-action@v5
with: with:
images: | images: |
${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }} ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }}
@@ -63,20 +63,20 @@ jobs:
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: build & push images - name: build & push images
uses: docker/build-push-action@v4 uses: docker/build-push-action@v5
with: with:
context: . context: .
file: ./Dockerfile file: ./Dockerfile

View File

@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: checkout code - name: checkout code
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Set hash - name: Set hash
id: vars id: vars
@@ -48,7 +48,7 @@ jobs:
- name: Generate Docker image metadata - name: Generate Docker image metadata
id: docker-meta id: docker-meta
uses: docker/metadata-action@v4 uses: docker/metadata-action@v5
# Defaults: # Defaults:
# DOCKERHUB_USERNAME : tzahi12345 # DOCKERHUB_USERNAME : tzahi12345
# DOCKERHUB_REPO : youtubedl-material # DOCKERHUB_REPO : youtubedl-material
@@ -63,20 +63,20 @@ jobs:
type=sha,prefix=sha-,format=short type=sha,prefix=sha-,format=short
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: build & push images - name: build & push images
uses: docker/build-push-action@v4 uses: docker/build-push-action@v5
with: with:
context: . context: .
file: ./Dockerfile file: ./Dockerfile

View File

@@ -16,12 +16,12 @@ jobs:
strategy: strategy:
matrix: matrix:
node: node:
- 16 - 18
steps: steps:
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: '${{ matrix.node }}' node-version: '${{ matrix.node }}'
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: 'Cache node_modules' - name: 'Cache node_modules'
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
@@ -33,7 +33,7 @@ jobs:
- uses: FedericoCarboni/setup-ffmpeg@v2 - uses: FedericoCarboni/setup-ffmpeg@v2
id: setup-ffmpeg id: setup-ffmpeg
- name: Install Dependencies - name: Install Dependencies
run: npm install run: npm install --dev
working-directory: ./backend working-directory: ./backend
- name: Run All Node.js Tests - name: Run All Node.js Tests
run: npm run test run: npm run test

View File

@@ -1,5 +1,5 @@
# Fetching our utils # Fetching our utils
FROM ubuntu:22.04 AS utils FROM ubuntu:23.04 AS utils
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
# Use script due local build compability # Use script due local build compability
COPY docker-utils/*.sh . COPY docker-utils/*.sh .
@@ -8,23 +8,23 @@ RUN sh ./ffmpeg-fetch.sh
RUN sh ./fetch-twitchdownloader.sh RUN sh ./fetch-twitchdownloader.sh
# Create our Ubuntu 22.04 with node 16.14.2 (that specific version is required as per: https://stackoverflow.com/a/72855258/8088021) # Create our Ubuntu 22.04 with node 18.19.0
# Go to 20.04 FROM ubuntu:23.04 AS base
FROM ubuntu:22.04 AS base
ARG TARGETPLATFORM ARG TARGETPLATFORM
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
ENV UID=1000 ENV UID=1001
ENV GID=1000 ENV GID=1001
ENV USER=youtube ENV USER=youtube
ENV NO_UPDATE_NOTIFIER=true ENV NO_UPDATE_NOTIFIER=true
ENV PM2_HOME=/app/pm2 ENV PM2_HOME=/app/pm2
ENV ALLOW_CONFIG_MUTATIONS=true ENV ALLOW_CONFIG_MUTATIONS=true
ENV npm_config_cache=/app/.npm
# Use NVM to get specific node version # Use NVM to get specific node version
ENV NODE_VERSION=16.14.2 ENV NODE_VERSION=18.19.0
RUN groupadd -g $GID $USER && useradd --system -m -g $USER --uid $UID $USER && \ RUN groupadd -g $GID $USER && useradd --system -m -g $USER --uid $UID $USER && \
apt update && \ apt update && \
apt install -y --no-install-recommends curl ca-certificates tzdata libicu70 libatomic1 && \ apt install -y --no-install-recommends curl ca-certificates tzdata libatomic1 && \
apt clean && \ apt clean && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
@@ -36,9 +36,11 @@ RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION}
RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION} RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION}
RUN . "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION} RUN . "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION}
RUN npm install -g npm
# Build frontend # Build frontend
ARG BUILDPLATFORM ARG BUILDPLATFORM
FROM --platform=${BUILDPLATFORM} node:16 as frontend FROM --platform=${BUILDPLATFORM} node:18 as frontend
RUN npm install -g @angular/cli RUN npm install -g @angular/cli
WORKDIR /build WORKDIR /build
COPY [ "package.json", "package-lock.json", "angular.json", "tsconfig.json", "/build/" ] COPY [ "package.json", "package-lock.json", "angular.json", "tsconfig.json", "/build/" ]
@@ -55,6 +57,8 @@ FROM base as backend
WORKDIR /app WORKDIR /app
COPY [ "backend/","/app/" ] COPY [ "backend/","/app/" ]
RUN npm config set strict-ssl false && \ RUN npm config set strict-ssl false && \
npm config set registry https://registry.npm.taobao.org && \
npm config set fetch-retry-maxtimeout 60000 && \
npm install --prod && \ npm install --prod && \
ls -al ls -al
@@ -71,10 +75,10 @@ RUN npm config set strict-ssl false && \
# Final image # Final image
FROM base FROM base
RUN npm install -g pm2 && \ RUN apt update && \
apt update && \ curl -sL https://raw.githubusercontent.com/Unitech/pm2/master/packager/setup.deb.sh | bash && \
apt install -y --no-install-recommends gosu python3-minimal python-is-python3 python3-pip atomicparsley build-essential && \ apt install -y --no-install-recommends gosu python3-minimal python-is-python3 python3-pip atomicparsley build-essential && \
pip install pycryptodomex && \ pip install pycryptodomex --break-system-packages && \
apt remove -y --purge build-essential && \ apt remove -y --purge build-essential && \
apt autoremove -y --purge && \ apt autoremove -y --purge && \
apt clean && \ apt clean && \

View File

@@ -6,7 +6,7 @@
[![GitHub issues badge](https://img.shields.io/github/issues/Tzahi12345/YoutubeDL-Material)](https://github.com/Tzahi12345/YoutubeDL-Material/issues) [![GitHub issues badge](https://img.shields.io/github/issues/Tzahi12345/YoutubeDL-Material)](https://github.com/Tzahi12345/YoutubeDL-Material/issues)
[![License badge](https://img.shields.io/github/license/Tzahi12345/YoutubeDL-Material)](https://github.com/Tzahi12345/YoutubeDL-Material/blob/master/LICENSE.md) [![License badge](https://img.shields.io/github/license/Tzahi12345/YoutubeDL-Material)](https://github.com/Tzahi12345/YoutubeDL-Material/blob/master/LICENSE.md)
YoutubeDL-Material is a Material Design frontend for [youtube-dl](https://rg3.github.io/youtube-dl/). It's coded using [Angular 15](https://angular.io/) for the frontend, and [Node.js](https://nodejs.org/) on the backend. YoutubeDL-Material is a Material Design frontend for [youtube-dl](https://rg3.github.io/youtube-dl/). It's coded using [Angular 17](https://angular.io/) for the frontend, and [Node.js](https://nodejs.org/) on the backend.
Now with [Docker](#Docker) support! Now with [Docker](#Docker) support!
@@ -30,7 +30,7 @@ NOTE: If you would like to use Docker, you can skip down to the [Docker](#Docker
Required dependencies: Required dependencies:
* Node.js 16 * Node.js 18
* Python * Python
Optional dependencies: Optional dependencies:
@@ -42,7 +42,7 @@ Optional dependencies:
<summary>Debian/Ubuntu</summary> <summary>Debian/Ubuntu</summary>
```bash ```bash
curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install nodejs youtube-dl ffmpeg unzip python npm sudo apt-get install nodejs youtube-dl ffmpeg unzip python npm
``` ```
@@ -57,7 +57,7 @@ sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfu
sudo yum install centos-release-scl-rh sudo yum install centos-release-scl-rh
sudo yum install rh-nodejs12 sudo yum install rh-nodejs12
scl enable rh-nodejs12 bash scl enable rh-nodejs12 bash
curl -fsSL https://rpm.nodesource.com/setup_16.x | sudo bash - curl -fsSL https://rpm.nodesource.com/setup_18.x | sudo bash -
sudo yum install nodejs youtube-dl ffmpeg ffmpeg-devel sudo yum install nodejs youtube-dl ffmpeg ffmpeg-devel
``` ```

View File

@@ -66,6 +66,14 @@
} }
] ]
}, },
"codespaces": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.codespaces.ts"
}
]
},
"es": { "es": {
"localize": ["es"] "localize": ["es"]
} }
@@ -75,21 +83,24 @@
"serve": { "serve": {
"builder": "@angular-devkit/build-angular:dev-server", "builder": "@angular-devkit/build-angular:dev-server",
"options": { "options": {
"browserTarget": "youtube-dl-material:build" "buildTarget": "youtube-dl-material:build"
}, },
"configurations": { "configurations": {
"production": { "production": {
"browserTarget": "youtube-dl-material:build:production" "buildTarget": "youtube-dl-material:build:production"
}, },
"es": { "es": {
"browserTarget": "youtube-dl-material:build:es" "buildTarget": "youtube-dl-material:build:es"
},
"codespaces": {
"buildTarget": "youtube-dl-material:build:codespaces"
} }
} }
}, },
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular-devkit/build-angular:extract-i18n",
"options": { "options": {
"browserTarget": "youtube-dl-material:build" "buildTarget": "youtube-dl-material:build"
} }
}, },
"serve-electron": { "serve-electron": {

View File

@@ -565,6 +565,7 @@ function loadConfigValues() {
} }
function getOrigin() { function getOrigin() {
if (process.env.CODESPACES) return `https://${process.env.CODESPACE_NAME}-4200.${process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}`;
return url_domain.origin; return url_domain.origin;
} }
@@ -589,17 +590,6 @@ function generateEnvVarConfigItem(key) {
return {key: key, value: process['env'][key]}; return {key: key, value: process['env'][key]};
} }
// currently only works for single urls
async function getUrlInfos(url) {
const {parsed_output, err} = await youtubedl_api.runYoutubeDL(url, ['--dump-json']);
if (!parsed_output || parsed_output.length !== 1) {
logger.error(`Failed to retrieve available formats for url: ${url}`);
if (err) logger.error(err);
return null;
}
return parsed_output[0];
}
// youtube-dl functions // youtube-dl functions
async function startYoutubeDL() { async function startYoutubeDL() {
@@ -1870,11 +1860,11 @@ app.post('/api/clearAllLogs', optionalJwt, async function(req, res) {
}); });
app.post('/api/getFileFormats', optionalJwt, async (req, res) => { app.post('/api/getFileFormats', optionalJwt, async (req, res) => {
let url = req.body.url; const url = req.body.url;
let result = await getUrlInfos(url); const result = await downloader_api.getVideoInfoByURL(url);
res.send({ res.send({
result: result, result: result && result.length === 1 ? result[0] : null,
success: !!result success: result && result.length === 0
}) })
}); });

View File

@@ -521,6 +521,8 @@ exports.generateArgs = async (url, type, options, user_uid = null, simulated = f
downloadConfig.push('--write-thumbnail'); downloadConfig.push('--write-thumbnail');
} }
downloadConfig.push('-i');
if (globalArgs && globalArgs !== '') { if (globalArgs && globalArgs !== '') {
// adds global args // adds global args
if (downloadConfig.indexOf('-o') !== -1 && globalArgs.split(',,').indexOf('-o') !== -1) { if (downloadConfig.indexOf('-o') !== -1 && globalArgs.split(',,').indexOf('-o') !== -1) {
@@ -571,8 +573,8 @@ exports.getVideoInfoByURL = async (url, args = [], download_uid = null) => {
let {callback} = await youtubedl_api.runYoutubeDL(url, new_args); let {callback} = await youtubedl_api.runYoutubeDL(url, new_args);
const {parsed_output, err} = await callback; const {parsed_output, err} = await callback;
if (!parsed_output || parsed_output.length === 0) { if (!parsed_output || parsed_output.length === 0) {
let error_message = `Error while retrieving info on video with URL ${url} with the following message: ${err}`; let error_message = `Error while retrieving info on video with URL ${url}`;
if (err.stderr) error_message += `\n\n${err.stderr}`; if (err.stderr) error_message += ` with the following message: \n${err.stderr}`;
logger.error(error_message); logger.error(error_message);
if (download_uid) { if (download_uid) {
await handleDownloadError(download_uid, error_message, 'info_retrieve_failed'); await handleDownloadError(download_uid, error_message, 'info_retrieve_failed');

View File

@@ -30,7 +30,6 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lowdb": "^1.0.0", "lowdb": "^1.0.0",
"md5": "^2.2.1", "md5": "^2.2.1",
"mocha": "^9.2.2",
"moment": "^2.29.4", "moment": "^2.29.4",
"mongodb": "^3.6.9", "mongodb": "^3.6.9",
"multer": "1.4.5-lts.1", "multer": "1.4.5-lts.1",
@@ -54,6 +53,9 @@
"winston": "^3.7.2", "winston": "^3.7.2",
"xmlbuilder2": "^3.0.2" "xmlbuilder2": "^3.0.2"
}, },
"devDependencies": {
"mocha": "^10.2.0"
},
"engines": { "engines": {
"node": "^16", "node": "^16",
"npm": "6.14.4" "npm": "6.14.4"
@@ -363,11 +365,6 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@ungap/promise-all-settled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
"integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q=="
},
"node_modules/@vladfrangu/async_event_emitter": { "node_modules/@vladfrangu/async_event_emitter": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.1.tgz", "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.1.tgz",
@@ -413,6 +410,7 @@
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
"integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
"dev": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@@ -421,6 +419,7 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -429,6 +428,7 @@
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"dependencies": { "dependencies": {
"color-convert": "^2.0.1" "color-convert": "^2.0.1"
}, },
@@ -443,6 +443,7 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"dependencies": { "dependencies": {
"color-name": "~1.1.4" "color-name": "~1.1.4"
}, },
@@ -453,7 +454,8 @@
"node_modules/ansi-styles/node_modules/color-name": { "node_modules/ansi-styles/node_modules/color-name": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
}, },
"node_modules/any-promise": { "node_modules/any-promise": {
"version": "1.3.0", "version": "1.3.0",
@@ -464,6 +466,7 @@
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"dev": true,
"dependencies": { "dependencies": {
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
"picomatch": "^2.0.4" "picomatch": "^2.0.4"
@@ -531,7 +534,8 @@
"node_modules/argparse": { "node_modules/argparse": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
}, },
"node_modules/array-buffer-byte-length": { "node_modules/array-buffer-byte-length": {
"version": "1.0.0", "version": "1.0.0",
@@ -707,6 +711,7 @@
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -770,6 +775,7 @@
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"dependencies": { "dependencies": {
"fill-range": "^7.0.1" "fill-range": "^7.0.1"
}, },
@@ -780,7 +786,8 @@
"node_modules/browser-stdout": { "node_modules/browser-stdout": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
"dev": true
}, },
"node_modules/bson": { "node_modules/bson": {
"version": "1.1.6", "version": "1.1.6",
@@ -935,6 +942,7 @@
"version": "7.0.4", "version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dev": true,
"dependencies": { "dependencies": {
"string-width": "^4.2.0", "string-width": "^4.2.0",
"strip-ansi": "^6.0.0", "strip-ansi": "^6.0.0",
@@ -945,6 +953,7 @@
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": { "dependencies": {
"ansi-regex": "^5.0.1" "ansi-regex": "^5.0.1"
}, },
@@ -1253,6 +1262,7 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
"integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
"dev": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@@ -1331,6 +1341,7 @@
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
"integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
"dev": true,
"engines": { "engines": {
"node": ">=0.3.1" "node": ">=0.3.1"
} }
@@ -1524,6 +1535,7 @@
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"dev": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@@ -1537,6 +1549,7 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@@ -1797,6 +1810,7 @@
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"dependencies": { "dependencies": {
"to-regex-range": "^5.0.1" "to-regex-range": "^5.0.1"
}, },
@@ -1825,6 +1839,7 @@
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
"dependencies": { "dependencies": {
"locate-path": "^6.0.0", "locate-path": "^6.0.0",
"path-exists": "^4.0.0" "path-exists": "^4.0.0"
@@ -1840,6 +1855,7 @@
"version": "5.0.2", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
"integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
"dev": true,
"bin": { "bin": {
"flat": "cli.js" "flat": "cli.js"
} }
@@ -1964,6 +1980,7 @@
"version": "2.3.2", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"optional": true, "optional": true,
"os": [ "os": [
@@ -2021,6 +2038,7 @@
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"engines": { "engines": {
"node": "6.* || 8.* || >= 10.*" "node": "6.* || 8.* || >= 10.*"
} }
@@ -2095,6 +2113,7 @@
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"dependencies": { "dependencies": {
"is-glob": "^4.0.1" "is-glob": "^4.0.1"
}, },
@@ -2181,14 +2200,6 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
}, },
"node_modules/growl": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"engines": {
"node": ">=4.x"
}
},
"node_modules/har-schema": { "node_modules/har-schema": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -2280,6 +2291,7 @@
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true,
"bin": { "bin": {
"he": "bin/he" "he": "bin/he"
} }
@@ -2450,6 +2462,7 @@
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"dependencies": { "dependencies": {
"binary-extensions": "^2.0.0" "binary-extensions": "^2.0.0"
}, },
@@ -2506,6 +2519,7 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -2514,6 +2528,7 @@
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
"dev": true,
"dependencies": { "dependencies": {
"is-extglob": "^2.1.1" "is-extglob": "^2.1.1"
}, },
@@ -2551,6 +2566,7 @@
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"engines": { "engines": {
"node": ">=0.12.0" "node": ">=0.12.0"
} }
@@ -2573,6 +2589,7 @@
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -2671,6 +2688,7 @@
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
"dev": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@@ -2708,6 +2726,7 @@
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"dependencies": { "dependencies": {
"argparse": "^2.0.1" "argparse": "^2.0.1"
}, },
@@ -2905,6 +2924,7 @@
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"dependencies": { "dependencies": {
"p-locate": "^5.0.0" "p-locate": "^5.0.0"
}, },
@@ -2949,6 +2969,7 @@
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
"integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
"dev": true,
"dependencies": { "dependencies": {
"chalk": "^4.1.0", "chalk": "^4.1.0",
"is-unicode-supported": "^0.1.0" "is-unicode-supported": "^0.1.0"
@@ -2964,6 +2985,7 @@
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"dependencies": { "dependencies": {
"ansi-styles": "^4.1.0", "ansi-styles": "^4.1.0",
"supports-color": "^7.1.0" "supports-color": "^7.1.0"
@@ -2979,6 +3001,7 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -2987,6 +3010,7 @@
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"dependencies": { "dependencies": {
"has-flag": "^4.0.0" "has-flag": "^4.0.0"
}, },
@@ -3178,41 +3202,39 @@
} }
}, },
"node_modules/mocha": { "node_modules/mocha": {
"version": "9.2.2", "version": "10.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz",
"integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==",
"dev": true,
"dependencies": { "dependencies": {
"@ungap/promise-all-settled": "1.1.2",
"ansi-colors": "4.1.1", "ansi-colors": "4.1.1",
"browser-stdout": "1.3.1", "browser-stdout": "1.3.1",
"chokidar": "3.5.3", "chokidar": "3.5.3",
"debug": "4.3.3", "debug": "4.3.4",
"diff": "5.0.0", "diff": "5.0.0",
"escape-string-regexp": "4.0.0", "escape-string-regexp": "4.0.0",
"find-up": "5.0.0", "find-up": "5.0.0",
"glob": "7.2.0", "glob": "7.2.0",
"growl": "1.10.5",
"he": "1.2.0", "he": "1.2.0",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"log-symbols": "4.1.0", "log-symbols": "4.1.0",
"minimatch": "4.2.1", "minimatch": "5.0.1",
"ms": "2.1.3", "ms": "2.1.3",
"nanoid": "3.3.1", "nanoid": "3.3.3",
"serialize-javascript": "6.0.0", "serialize-javascript": "6.0.0",
"strip-json-comments": "3.1.1", "strip-json-comments": "3.1.1",
"supports-color": "8.1.1", "supports-color": "8.1.1",
"which": "2.0.2", "workerpool": "6.2.1",
"workerpool": "6.2.0",
"yargs": "16.2.0", "yargs": "16.2.0",
"yargs-parser": "20.2.4", "yargs-parser": "20.2.4",
"yargs-unparser": "2.0.0" "yargs-unparser": "2.0.0"
}, },
"bin": { "bin": {
"_mocha": "bin/_mocha", "_mocha": "bin/_mocha",
"mocha": "bin/mocha" "mocha": "bin/mocha.js"
}, },
"engines": { "engines": {
"node": ">= 12.0.0" "node": ">= 14.0.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@@ -3223,6 +3245,7 @@
"version": "3.5.3", "version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "individual", "type": "individual",
@@ -3246,9 +3269,10 @@
} }
}, },
"node_modules/mocha/node_modules/debug": { "node_modules/mocha/node_modules/debug": {
"version": "4.3.3", "version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": { "dependencies": {
"ms": "2.1.2" "ms": "2.1.2"
}, },
@@ -3264,12 +3288,14 @@
"node_modules/mocha/node_modules/debug/node_modules/ms": { "node_modules/mocha/node_modules/debug/node_modules/ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
}, },
"node_modules/mocha/node_modules/glob": { "node_modules/mocha/node_modules/glob": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
"dev": true,
"dependencies": { "dependencies": {
"fs.realpath": "^1.0.0", "fs.realpath": "^1.0.0",
"inflight": "^1.0.4", "inflight": "^1.0.4",
@@ -3289,6 +3315,7 @@
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": { "dependencies": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
}, },
@@ -3300,30 +3327,43 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/mocha/node_modules/minimatch": { "node_modules/mocha/node_modules/minimatch": {
"version": "4.2.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
"integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
"dev": true,
"dependencies": { "dependencies": {
"brace-expansion": "^1.1.7" "brace-expansion": "^2.0.1"
}, },
"engines": { "engines": {
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/mocha/node_modules/ms": { "node_modules/mocha/node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
}, },
"node_modules/mocha/node_modules/nanoid": { "node_modules/mocha/node_modules/nanoid": {
"version": "3.3.1", "version": "3.3.3",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
"integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
"dev": true,
"bin": { "bin": {
"nanoid": "bin/nanoid.cjs" "nanoid": "bin/nanoid.cjs"
}, },
@@ -3335,6 +3375,7 @@
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"dependencies": { "dependencies": {
"picomatch": "^2.2.1" "picomatch": "^2.2.1"
}, },
@@ -3346,6 +3387,7 @@
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
}, },
@@ -3357,6 +3399,7 @@
"version": "8.1.1", "version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"dependencies": { "dependencies": {
"has-flag": "^4.0.0" "has-flag": "^4.0.0"
}, },
@@ -3761,6 +3804,7 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"dependencies": { "dependencies": {
"yocto-queue": "^0.1.0" "yocto-queue": "^0.1.0"
}, },
@@ -3775,6 +3819,7 @@
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"dependencies": { "dependencies": {
"p-limit": "^3.0.2" "p-limit": "^3.0.2"
}, },
@@ -3884,6 +3929,7 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -3935,6 +3981,7 @@
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
"dev": true,
"engines": { "engines": {
"node": ">=8.6" "node": ">=8.6"
}, },
@@ -4039,6 +4086,7 @@
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"dependencies": { "dependencies": {
"safe-buffer": "^5.1.0" "safe-buffer": "^5.1.0"
} }
@@ -4245,6 +4293,7 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -4391,6 +4440,7 @@
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
"integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
"dev": true,
"dependencies": { "dependencies": {
"randombytes": "^2.1.0" "randombytes": "^2.1.0"
} }
@@ -4568,6 +4618,7 @@
"version": "4.2.2", "version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"dev": true,
"dependencies": { "dependencies": {
"emoji-regex": "^8.0.0", "emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0", "is-fullwidth-code-point": "^3.0.0",
@@ -4580,12 +4631,14 @@
"node_modules/string-width/node_modules/emoji-regex": { "node_modules/string-width/node_modules/emoji-regex": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
}, },
"node_modules/string-width/node_modules/is-fullwidth-code-point": { "node_modules/string-width/node_modules/is-fullwidth-code-point": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -4594,6 +4647,7 @@
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dev": true,
"dependencies": { "dependencies": {
"ansi-regex": "^5.0.0" "ansi-regex": "^5.0.0"
}, },
@@ -4726,6 +4780,7 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"dependencies": { "dependencies": {
"is-number": "^7.0.0" "is-number": "^7.0.0"
}, },
@@ -5106,14 +5161,16 @@
} }
}, },
"node_modules/workerpool": { "node_modules/workerpool": {
"version": "6.2.0", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
"integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==" "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
"dev": true
}, },
"node_modules/wrap-ansi": { "node_modules/wrap-ansi": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"dependencies": { "dependencies": {
"ansi-styles": "^4.0.0", "ansi-styles": "^4.0.0",
"string-width": "^4.1.0", "string-width": "^4.1.0",
@@ -5130,6 +5187,7 @@
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"dependencies": { "dependencies": {
"ansi-regex": "^5.0.1" "ansi-regex": "^5.0.1"
}, },
@@ -5220,6 +5278,7 @@
"version": "5.0.8", "version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
} }
@@ -5233,6 +5292,7 @@
"version": "16.2.0", "version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"dev": true,
"dependencies": { "dependencies": {
"cliui": "^7.0.2", "cliui": "^7.0.2",
"escalade": "^3.1.1", "escalade": "^3.1.1",
@@ -5250,6 +5310,7 @@
"version": "20.2.4", "version": "20.2.4",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
"integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
"dev": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
} }
@@ -5258,6 +5319,7 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
"integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
"dev": true,
"dependencies": { "dependencies": {
"camelcase": "^6.0.0", "camelcase": "^6.0.0",
"decamelize": "^4.0.0", "decamelize": "^4.0.0",
@@ -5272,6 +5334,7 @@
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
"dev": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@@ -5283,6 +5346,7 @@
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },

View File

@@ -44,7 +44,6 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lowdb": "^1.0.0", "lowdb": "^1.0.0",
"md5": "^2.2.1", "md5": "^2.2.1",
"mocha": "^9.2.2",
"moment": "^2.29.4", "moment": "^2.29.4",
"mongodb": "^3.6.9", "mongodb": "^3.6.9",
"multer": "1.4.5-lts.1", "multer": "1.4.5-lts.1",
@@ -67,5 +66,8 @@
"uuid": "^9.0.1", "uuid": "^9.0.1",
"winston": "^3.7.2", "winston": "^3.7.2",
"xmlbuilder2": "^3.0.2" "xmlbuilder2": "^3.0.2"
},
"devDependencies": {
"mocha": "^10.2.0"
} }
} }

View File

@@ -529,7 +529,9 @@ exports.parseOutputJSON = (output, err) => {
let split_output = []; let split_output = [];
// const output_jsons = []; // const output_jsons = [];
if (err && !output) { if (err && !output) {
if (!err.stderr.includes('This video is unavailable') && !err.stderr.includes('Private video')) { const attempt_backup_errs = ['This video is unavailable', 'Private video', 'unavailable video'];
const attempt_backup = err.stderr ? attempt_backup_errs.some(err_msg => err.stderr.includes(err_msg)) : false;
if (!attempt_backup) {
return null; return null;
} }
logger.info('An error was encountered with at least one video, backup method will be used.') logger.info('An error was encountered with at least one video, backup method will be used.')

View File

@@ -67,7 +67,9 @@ const runYoutubeDLProcess = async (url, args, youtubedl_fork = config_api.getCon
const parsed_output = utils.parseOutputJSON(stdout.trim().split(/\r?\n/), stderr); const parsed_output = utils.parseOutputJSON(stdout.trim().split(/\r?\n/), stderr);
resolve({parsed_output, err: stderr}); resolve({parsed_output, err: stderr});
} catch (e) { } catch (e) {
resolve({parsed_output: null, err: e}) // Attempt to not fail
const parsed_output = utils.parseOutputJSON(e && e.stdout && e.stdout.trim().split(/\r?\n/), e && e.stderr);
resolve({parsed_output: parsed_output, err: parsed_output ? null : e});
} }
}); });
return {child_process, callback} return {child_process, callback}
@@ -118,10 +120,16 @@ async function downloadLatestYoutubeDLBinaryGeneric(youtubedl_fork, new_version,
const download_url = `${exports.youtubedl_forks[youtubedl_fork]['download_url']}${file_ext}`; const download_url = `${exports.youtubedl_forks[youtubedl_fork]['download_url']}${file_ext}`;
const output_path = custom_output_path || getYoutubeDLPath(youtubedl_fork); const output_path = custom_output_path || getYoutubeDLPath(youtubedl_fork);
try {
await utils.fetchFile(download_url, output_path, `${youtubedl_fork} ${new_version}`); await utils.fetchFile(download_url, output_path, `${youtubedl_fork} ${new_version}`);
fs.chmod(output_path, 0o777); fs.chmod(output_path, 0o777);
updateDetailsJSON(new_version, youtubedl_fork, output_path); updateDetailsJSON(new_version, youtubedl_fork, output_path);
} catch (e) {
logger.error(`Failed to download new ${youtubedl_fork} version: ${new_version}`);
logger.error(e);
return;
}
} }
exports.getLatestUpdateVersion = async (youtubedl_fork) => { exports.getLatestUpdateVersion = async (youtubedl_fork) => {

View File

@@ -22,7 +22,7 @@ esac
echo "(INFO) Architecture detected: $ARCH" echo "(INFO) Architecture detected: $ARCH"
echo "(1/5) READY - Install unzip" echo "(1/5) READY - Install unzip"
apt-get update && apt-get -y install unzip curl jq libicu70 apt-get update && apt-get -y install unzip curl jq
VERSION=$(curl --silent "https://api.github.com/repos/lay295/TwitchDownloader/releases" | jq -r --arg arch "$ARCH" '[.[] | select(.assets | length > 0) | select(.assets[].name | contains("CLI") and contains($arch))] | max_by(.published_at) | .tag_name') VERSION=$(curl --silent "https://api.github.com/repos/lay295/TwitchDownloader/releases" | jq -r --arg arch "$ARCH" '[.[] | select(.assets | length > 0) | select(.assets[].name | contains("CLI") and contains($arch))] | max_by(.published_at) | .tag_name')
echo "(2/5) DOWNLOAD - Acquire twitchdownloader" echo "(2/5) DOWNLOAD - Acquire twitchdownloader"
curl -o twitchdownloader.zip \ curl -o twitchdownloader.zip \

13585
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
"codespaces": "ng serve --configuration=codespaces",
"build": "ng build --configuration production", "build": "ng build --configuration production",
"prebuild": "node src/postbuild.mjs", "prebuild": "node src/postbuild.mjs",
"heroku-postbuild": "npm install --prefix backend", "heroku-postbuild": "npm install --prefix backend",
@@ -16,23 +17,23 @@
"i18n-source": "ng extract-i18n --output-path=src/assets/i18n --out-file=messages.en.xlf" "i18n-source": "ng extract-i18n --output-path=src/assets/i18n --out-file=messages.en.xlf"
}, },
"engines": { "engines": {
"node": "12.3.1", "node": "18.19.0",
"npm": "6.10.3" "npm": "10.2.3"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular-devkit/core": "^15.0.1", "@angular-devkit/core": "^17.0.5",
"@angular/animations": "^15.0.1", "@angular/animations": "^17.0.5",
"@angular/cdk": "^15.0.0", "@angular/cdk": "^17.0.2",
"@angular/common": "^15.0.1", "@angular/common": "^17.0.5",
"@angular/compiler": "^15.0.1", "@angular/compiler": "^17.0.5",
"@angular/core": "^15.0.1", "@angular/core": "^17.0.5",
"@angular/forms": "^15.0.1", "@angular/forms": "^17.0.5",
"@angular/localize": "^15.0.1", "@angular/localize": "^17.0.5",
"@angular/material": "^15.0.0", "@angular/material": "^17.0.2",
"@angular/platform-browser": "^15.0.1", "@angular/platform-browser": "^17.0.5",
"@angular/platform-browser-dynamic": "^15.0.1", "@angular/platform-browser-dynamic": "^17.0.5",
"@angular/router": "^15.0.1", "@angular/router": "^17.0.5",
"@fontsource/material-icons": "^4.5.4", "@fontsource/material-icons": "^4.5.4",
"@ngneat/content-loader": "^7.0.0", "@ngneat/content-loader": "^7.0.0",
"@videogular/ngx-videogular": "^6.0.0", "@videogular/ngx-videogular": "^6.0.0",
@@ -43,20 +44,19 @@
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"material-icons": "^1.10.8", "material-icons": "^1.10.8",
"nan": "^2.14.1", "nan": "^2.14.1",
"ngx-avatars": "^1.4.1", "ngx-avatars": "^1.10.0",
"ngx-file-drop": "^15.0.0", "ngx-file-drop": "^15.0.0",
"rxjs": "^6.6.3", "rxjs": "^6.6.3",
"rxjs-compat": "^6.6.7", "rxjs-compat": "^6.6.7",
"tslib": "^2.0.0", "tslib": "^2.0.0",
"typescript": "~4.8.4",
"xliff-to-json": "^1.0.4", "xliff-to-json": "^1.0.4",
"zone.js": "~0.11.4" "zone.js": "~0.14.2"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^15.0.1", "@angular-devkit/build-angular": "^17.0.5",
"@angular/cli": "^15.0.1", "@angular/cli": "^17.0.5",
"@angular/compiler-cli": "^15.0.1", "@angular/compiler-cli": "^17.0.5",
"@angular/language-service": "^15.0.1", "@angular/language-service": "^17.0.5",
"@types/core-js": "^2.5.2", "@types/core-js": "^2.5.2",
"@types/file-saver": "^2.0.1", "@types/file-saver": "^2.0.1",
"@types/jasmine": "^4.3.1", "@types/jasmine": "^4.3.1",
@@ -66,7 +66,7 @@
"ajv": "^7.2.4", "ajv": "^7.2.4",
"codelyzer": "^6.0.0", "codelyzer": "^6.0.0",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"jasmine-core": "~3.6.0", "jasmine-core": "~3.8.0",
"jasmine-spec-reporter": "~5.0.0", "jasmine-spec-reporter": "~5.0.0",
"karma": "~6.4.2", "karma": "~6.4.2",
"karma-chrome-launcher": "~3.1.0", "karma-chrome-launcher": "~3.1.0",
@@ -77,12 +77,13 @@
"openapi-typescript-codegen": "^0.23.0", "openapi-typescript-codegen": "^0.23.0",
"protractor": "~7.0.0", "protractor": "~7.0.0",
"ts-node": "~3.0.4", "ts-node": "~3.0.4",
"tslint": "~6.1.0" "tslint": "~6.1.0",
"typescript": "~5.2.0"
}, },
"overrides": { "overrides": {
"ngx-avatars": { "ngx-avatars": {
"@angular/common": "15.0.1", "@angular/common": "^17.0.0",
"@angular/core": "15.0.1" "@angular/core": "^17.0.0"
} }
} }
} }

View File

@@ -5,13 +5,23 @@
<div class="row" width="100%" height="100%"> <div class="row" width="100%" height="100%">
<div class="col-6" style="text-align: left; margin-top: 1px;"> <div class="col-6" style="text-align: left; margin-top: 1px;">
<div style="display: flex; align-items: center;"> <div style="display: flex; align-items: center;">
<button #hamburgerMenu style="outline: none" *ngIf="router.url.split(';')[0] !== '/player'" mat-icon-button aria-label="Toggle side navigation" (click)="toggleSidenav()"><mat-icon>menu</mat-icon></button> @if (router.url.split(';')[0] !== '/player') {
<button (click)="goBack()" *ngIf="router.url.split(';')[0] === '/player'" mat-icon-button><mat-icon>arrow_back</mat-icon></button> <button #hamburgerMenu style="outline: none" mat-icon-button aria-label="Toggle side navigation" (click)="toggleSidenav()"><mat-icon>menu</mat-icon></button>
} @else {
<button (click)="goBack()" mat-icon-button><mat-icon>arrow_back</mat-icon></button>
}
<div style="margin-left: 8px; display: inline-block;"><button mat-icon-button routerLink='/home'><img style="width: 32px;" src="assets/images/logo_128px.png"></button></div> <div style="margin-left: 8px; display: inline-block;"><button mat-icon-button routerLink='/home'><img style="width: 32px;" src="assets/images/logo_128px.png"></button></div>
</div> </div>
</div> </div>
<div class="col-6" style="text-align: right; align-items: flex-end; display: inline-block"> <div class="col-6" style="text-align: right; align-items: flex-end; display: inline-block">
<button *ngIf="postsService.config?.Extra.enable_notifications" [matMenuTriggerFor]="notificationsMenu" (menuOpened)="notificationMenuOpened()" mat-icon-button><mat-icon [matBadge]="notification_count" matBadgeColor="warn" matBadgeSize="small" *ngIf="notification_count > 0">notifications</mat-icon><mat-icon *ngIf="notification_count === 0">notifications_none</mat-icon></button> @if (postsService.config?.Extra.enable_notifications) {
<button [matMenuTriggerFor]="notificationsMenu" (menuOpened)="notificationMenuOpened()" mat-icon-button>
@if (notification_count > 0) {
<mat-icon [matBadge]="notification_count" matBadgeColor="warn" matBadgeSize="small">notifications</mat-icon>
} @else {
<mat-icon>notifications_none</mat-icon>
}</button>
}
<mat-menu [classList]="'notifications-menu'" (close)="notificationMenuClosed()" #notificationsMenu="matMenu"> <mat-menu [classList]="'notifications-menu'" (close)="notificationMenuClosed()" #notificationsMenu="matMenu">
<app-notifications #notifications (notificationCount)="notificationCountUpdate($event)" (click)="$event.stopPropagation()"></app-notifications> <app-notifications #notifications (notificationCount)="notificationCountUpdate($event)" (click)="$event.stopPropagation()"></app-notifications>
</mat-menu> </mat-menu>
@@ -21,15 +31,19 @@
<mat-icon>person</mat-icon> <mat-icon>person</mat-icon>
<span i18n="Profile menu label">Profile</span> <span i18n="Profile menu label">Profile</span>
</button> </button>
<button *ngIf="!postsService.config?.Advanced.multi_user_mode || postsService.isLoggedIn" class="top-menu-button" (click)="openArchivesDialog()" mat-menu-item> @if (!postsService.config?.Advanced.multi_user_mode || postsService.isLoggedIn) {
<button class="top-menu-button" (click)="openArchivesDialog()" mat-menu-item>
<mat-icon>topic</mat-icon> <mat-icon>topic</mat-icon>
<span i18n="Archives menu label">Archives</span> <span i18n="Archives menu label">Archives</span>
</button> </button>
<button class="top-menu-button" (click)="themeMenuItemClicked($event)" *ngIf="allowThemeChange" mat-menu-item> }
@if (allowThemeChange) {
<button class="top-menu-button" (click)="themeMenuItemClicked($event)" mat-menu-item>
<mat-icon>{{(postsService.theme.key === 'default') ? 'brightness_5' : 'brightness_2'}}</mat-icon> <mat-icon>{{(postsService.theme.key === 'default') ? 'brightness_5' : 'brightness_2'}}</mat-icon>
<span i18n="Dark mode toggle label">Dark</span> <span i18n="Dark mode toggle label">Dark</span>
<mat-slide-toggle class="theme-slide-toggle" [checked]="postsService.theme.key === 'dark'"></mat-slide-toggle> <mat-slide-toggle class="theme-slide-toggle" [checked]="postsService.theme.key === 'dark'"></mat-slide-toggle>
</button> </button>
}
<button class="top-menu-button" (click)="openAboutDialog()" mat-menu-item> <button class="top-menu-button" (click)="openAboutDialog()" mat-menu-item>
<mat-icon>info</mat-icon> <mat-icon>info</mat-icon>
<span i18n="About menu label">About</span> <span i18n="About menu label">About</span>
@@ -44,19 +58,33 @@
<mat-sidenav-container style="height: 100%"> <mat-sidenav-container style="height: 100%">
<mat-sidenav [opened]="postsService.sidepanel_mode === 'side' && !window.location.href.includes('/player')" [mode]="postsService.sidepanel_mode" #sidenav> <mat-sidenav [opened]="postsService.sidepanel_mode === 'side' && !window.location.href.includes('/player')" [mode]="postsService.sidepanel_mode" #sidenav>
<mat-nav-list> <mat-nav-list>
<a *ngIf="postsService.config && (!postsService.config.Advanced.multi_user_mode || postsService.isLoggedIn)" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/home'><ng-container i18n="Navigation menu Home Page title">Home</ng-container></a> @if (postsService.config && (!postsService.config.Advanced.multi_user_mode || postsService.isLoggedIn)) {
<a *ngIf="postsService.config && postsService.config.Advanced.multi_user_mode && !postsService.isLoggedIn" mat-list-item (click)="sidenav.close()" routerLink='/login'><ng-container i18n="Navigation menu Login Page title">Login</ng-container></a> <a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/home'><ng-container i18n="Navigation menu Home Page title">Home</ng-container></a>
<a *ngIf="postsService.config && allowSubscriptions && postsService.hasPermission('subscriptions')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/subscriptions'><ng-container i18n="Navigation menu Subscriptions Page title">Subscriptions</ng-container></a> }
<a *ngIf="postsService.config && enableDownloadsManager && postsService.hasPermission('downloads_manager')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/downloads'><ng-container i18n="Navigation menu Downloads Page title">Downloads</ng-container></a> @if (postsService.config && postsService.config.Advanced.multi_user_mode && !postsService.isLoggedIn) {
<a *ngIf="postsService.config && postsService.hasPermission('tasks_manager')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/tasks'><ng-container i18n="Navigation menu Tasks Page title">Tasks</ng-container></a> <a mat-list-item (click)="sidenav.close()" routerLink='/login'><ng-container i18n="Navigation menu Login Page title">Login</ng-container></a>
<ng-container *ngIf="postsService.config && postsService.hasPermission('settings')"> }
@if (postsService.config && allowSubscriptions && postsService.hasPermission('subscriptions')) {
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/subscriptions'><ng-container i18n="Navigation menu Subscriptions Page title">Subscriptions</ng-container></a>
}
@if (postsService.config && enableDownloadsManager && postsService.hasPermission('downloads_manager')) {
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/downloads'><ng-container i18n="Navigation menu Downloads Page title">Downloads</ng-container></a>
}
@if (postsService.config && postsService.hasPermission('tasks_manager')) {
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/tasks'><ng-container i18n="Navigation menu Tasks Page title">Tasks</ng-container></a>
}
@if (postsService.config && postsService.hasPermission('settings')) {
<mat-divider></mat-divider> <mat-divider></mat-divider>
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/settings'><ng-container i18n="Settings menu label">Settings</ng-container></a> <a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/settings'><ng-container i18n="Settings menu label">Settings</ng-container></a>
</ng-container> }
<ng-container *ngIf="postsService.config && allowSubscriptions && postsService.subscriptions && postsService.hasPermission('subscriptions')"> @if (postsService.config && allowSubscriptions && postsService.subscriptions && postsService.hasPermission('subscriptions')) {
<mat-divider *ngIf="postsService.subscriptions.length > 0"></mat-divider> @if (postsService.subscriptions.length > 0) {
<a *ngFor="let subscription of postsService.subscriptions" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" [routerLink]="['/subscription', { id: subscription.id }]"><ngx-avatars [style.display]="'inline-block'" [style.margin-right]="'10px'" size="32" [name]="subscription.name"></ngx-avatars>{{subscription.name}}</a> <mat-divider></mat-divider>
</ng-container> }
@for (subscription of postsService.subscriptions; track subscription) {
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" [routerLink]="['/subscription', { id: subscription.id }]"><ngx-avatars [style.display]="'inline-block'" [style.margin-right]="'10px'" size="32" [name]="subscription.name"></ngx-avatars>{{subscription.name}}</a>
}
}
</mat-nav-list> </mat-nav-list>
</mat-sidenav> </mat-sidenav>
<mat-sidenav-content [style.background]="postsService.theme ? postsService.theme.background_color : null"> <mat-sidenav-content [style.background]="postsService.theme ? postsService.theme.background_color : null">

View File

@@ -3,11 +3,9 @@
<mat-label i18n="Filter">Filter</mat-label> <mat-label i18n="Filter">Filter</mat-label>
<input matInput [(ngModel)]="text_filter" (keyup)="applyFilter($event)" #input> <input matInput [(ngModel)]="text_filter" (keyup)="applyFilter($event)" #input>
</mat-form-field> </mat-form-field>
<div [hidden]="!(archives && archives.length > 0)"> <div [hidden]="!(archives && archives.length > 0)">
<div class="mat-elevation-z8"> <div class="mat-elevation-z8">
<mat-table matSort [dataSource]="dataSource"> <mat-table matSort [dataSource]="dataSource">
<!-- Select Column --> <!-- Select Column -->
<!-- Checkbox Column --> <!-- Checkbox Column -->
<ng-container matColumnDef="select"> <ng-container matColumnDef="select">
@@ -25,13 +23,11 @@
<mat-icon class="audio-video-icon">{{(row.type === 'audio') ? 'audiotrack' : 'movie'}}</mat-icon> <mat-icon class="audio-video-icon">{{(row.type === 'audio') ? 'audiotrack' : 'movie'}}</mat-icon>
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<!-- Date Column --> <!-- Date Column -->
<ng-container matColumnDef="timestamp"> <ng-container matColumnDef="timestamp">
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Date">Date</ng-container> </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Date">Date</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.timestamp*1000 | date: 'short'}} </mat-cell> <mat-cell *matCellDef="let element"> {{element.timestamp*1000 | date: 'short'}} </mat-cell>
</ng-container> </ng-container>
<!-- Title Column --> <!-- Title Column -->
<ng-container matColumnDef="title"> <ng-container matColumnDef="title">
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Title">Title</ng-container> </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Title">Title</ng-container> </mat-header-cell>
@@ -41,7 +37,6 @@
</span> </span>
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<!-- ID Column --> <!-- ID Column -->
<ng-container matColumnDef="id"> <ng-container matColumnDef="id">
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="ID">ID</ng-container> </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="ID">ID</ng-container> </mat-header-cell>
@@ -51,7 +46,6 @@
</span> </span>
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<!-- Extractor Column --> <!-- Extractor Column -->
<ng-container matColumnDef="extractor"> <ng-container matColumnDef="extractor">
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Extractor">Extractor</ng-container> </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Extractor">Extractor</ng-container> </mat-header-cell>
@@ -61,17 +55,16 @@
</span> </span>
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> <mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row> <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table> </mat-table>
</div> </div>
</div> </div>
@if ((!archives || archives.length === 0)) {
<div *ngIf="(!archives || archives.length === 0)"> <div>
<h4 style="text-align: center; margin-top: 10px;" i18n="Archives empty">Archives empty</h4> <h4 style="text-align: center; margin-top: 10px;" i18n="Archives empty">Archives empty</h4>
</div> </div>
}
<div style="margin: 10px 10px 10px 0px; display: flex;"> <div style="margin: 10px 10px 10px 0px; display: flex;">
<span style="flex-grow: 1;" class="flex-items"> <span style="flex-grow: 1;" class="flex-items">
<button [disabled]="selection.selected.length === 0" color="warn" style="margin: 10px;" mat-stroked-button i18n="Delete selected" (click)="openDeleteSelectedArchivesDialog()">Delete selected</button> <button [disabled]="selection.selected.length === 0" color="warn" style="margin: 10px;" mat-stroked-button i18n="Delete selected" (click)="openDeleteSelectedArchivesDialog()">Delete selected</button>
@@ -82,7 +75,9 @@
<mat-label i18n="Subscription">Subscription</mat-label> <mat-label i18n="Subscription">Subscription</mat-label>
<mat-select [ngModel]="sub_id" (ngModelChange)="subFilterSelectionChanged($event)"> <mat-select [ngModel]="sub_id" (ngModelChange)="subFilterSelectionChanged($event)">
<mat-option [value]="'none'" i18n="None">None</mat-option> <mat-option [value]="'none'" i18n="None">None</mat-option>
<mat-option *ngFor="let sub of postsService.subscriptions" [value]="sub.id">{{sub.name}}</mat-option> @for (sub of postsService.subscriptions; track sub) {
<mat-option [value]="sub.id">{{sub.name}}</mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field style="width: 100px; margin-bottom: -1.25em; margin-left: 10px;"> <mat-form-field style="width: 100px; margin-bottom: -1.25em; margin-left: 10px;">
@@ -95,7 +90,6 @@
</mat-form-field> </mat-form-field>
</span> </span>
</div> </div>
<div class="file-drop-parent"> <div class="file-drop-parent">
<ngx-file-drop [multiple]="false" accept=".txt" dropZoneLabel="Drop file here" (onFileDrop)="dropped($event)"> <ngx-file-drop [multiple]="false" accept=".txt" dropZoneLabel="Drop file here" (onFileDrop)="dropped($event)">
<ng-template class="file-drop" ngx-file-drop-content-tmp let-openFileSelector="openFileSelector"> <ng-template class="file-drop" ngx-file-drop-content-tmp let-openFileSelector="openFileSelector">
@@ -110,11 +104,11 @@
</ng-template> </ng-template>
</ngx-file-drop> </ngx-file-drop>
</div> </div>
<div style="margin-top: 10px; color: white"> <div style="margin-top: 10px; color: white">
<table class="table"> <table class="table">
<tbody class="upload-name-style"> <tbody class="upload-name-style">
<tr *ngFor="let item of files; let i=index"> @for (item of files; track item; let i = $index) {
<tr>
<td style="vertical-align: middle; border-top: unset"> <td style="vertical-align: middle; border-top: unset">
<strong>{{ item.relativePath }}</strong> <strong>{{ item.relativePath }}</strong>
</td> </td>
@@ -124,7 +118,9 @@
<mat-label i18n="Subscription">Subscription</mat-label> <mat-label i18n="Subscription">Subscription</mat-label>
<mat-select [ngModel]="upload_sub_id" (ngModelChange)="subUploadFilterSelectionChanged($event)"> <mat-select [ngModel]="upload_sub_id" (ngModelChange)="subUploadFilterSelectionChanged($event)">
<mat-option [value]="'none'" i18n="None">None</mat-option> <mat-option [value]="'none'" i18n="None">None</mat-option>
<mat-option *ngFor="let sub of postsService.subscriptions" [value]="sub.id">{{sub.name}}</mat-option> @for (sub of postsService.subscriptions; track sub) {
<mat-option [value]="sub.id">{{sub.name}}</mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field style="width: 100px; margin-left: 10px"> <mat-form-field style="width: 100px; margin-left: 10px">
@@ -134,10 +130,15 @@
<mat-option [value]="'audio'" i18n="Audio">Audio</mat-option> <mat-option [value]="'audio'" i18n="Audio">Audio</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<button style="margin-left: 10px" [disabled]="uploading_archive || uploaded_archive" (click)="importArchive()" matTooltip="Upload" i18n-matTooltip="Upload" mat-mini-fab><mat-icon>publish</mat-icon><mat-spinner *ngIf="uploading_archive" class="spinner" [diameter]="38"></mat-spinner></button> <button style="margin-left: 10px" [disabled]="uploading_archive || uploaded_archive" (click)="importArchive()" matTooltip="Upload" i18n-matTooltip="Upload" mat-mini-fab><mat-icon>publish</mat-icon>
@if (uploading_archive) {
<mat-spinner class="spinner" [diameter]="38"></mat-spinner>
}
</button>
</div> </div>
</td> </td>
</tr> </tr>
}
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@@ -8,6 +8,7 @@ import { Archive } from 'api-types/models/Archive';
import { ConfirmDialogComponent } from 'app/dialogs/confirm-dialog/confirm-dialog.component'; import { ConfirmDialogComponent } from 'app/dialogs/confirm-dialog/confirm-dialog.component';
import { PostsService } from 'app/posts.services'; import { PostsService } from 'app/posts.services';
import { NgxFileDropEntry } from 'ngx-file-drop'; import { NgxFileDropEntry } from 'ngx-file-drop';
import { saveAs } from 'file-saver';
@Component({ @Component({
selector: 'app-archive-viewer', selector: 'app-archive-viewer',

View File

@@ -1,6 +1,14 @@
<div class="buttons-container"> <div class="buttons-container">
<button (click)="startWatching()" *ngIf="!watch_together_clicked" mat-flat-button>Watch together</button> @if (!watch_together_clicked) {
<button (click)="startServer()" *ngIf="watch_together_clicked && !started && server_mode && server_already_exists === false" mat-flat-button>Start stream</button> <button (click)="startWatching()" mat-flat-button>Watch together</button>
<button (click)="startClient()" *ngIf="watch_together_clicked && !started && server_already_exists === true" mat-flat-button>Join stream</button> } @else {
<button style="margin-left: 10px;" (click)="stop()" *ngIf="watch_together_clicked" mat-flat-button>Stop</button> @if (!started) {
@if (server_already_exists) {
<button (click)="startClient()" mat-flat-button>Join stream</button>
} @else if (server_mode) {
<button (click)="startServer()" mat-flat-button>Start stream</button>
}
}
<button style="margin-left: 10px;" (click)="stop()" mat-flat-button>Stop</button>
}
</div> </div>

View File

@@ -1,13 +1,18 @@
<div *ngIf="playlists && playlists.length > 0"> @if (playlists) {
<div>
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div *ngFor="let playlist of playlists; let i = index" class="mb-2 mt-2" [ngClass]="[ postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]"> @for (playlist of playlists; track playlist; let i = $index) {
<div class="mb-2 mt-2" [ngClass]="[ postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]">
<app-unified-file-card [index]="i" [card_size]="postsService.card_size" [locale]="postsService.locale" (goToFile)="goToPlaylist($event)" [file_obj]="playlist" [is_playlist]="true" (editPlaylist)="editPlaylistDialog($event)" (deleteFile)="deletePlaylist($event)" [baseStreamPath]="postsService.path" [jwtString]="postsService.isLoggedIn ? this.postsService.token : ''" [loading]="false"></app-unified-file-card> <app-unified-file-card [index]="i" [card_size]="postsService.card_size" [locale]="postsService.locale" (goToFile)="goToPlaylist($event)" [file_obj]="playlist" [is_playlist]="true" (editPlaylist)="editPlaylistDialog($event)" (deleteFile)="deletePlaylist($event)" [baseStreamPath]="postsService.path" [jwtString]="postsService.isLoggedIn ? this.postsService.token : ''" [loading]="false"></app-unified-file-card>
</div> </div>
</div> } @empty {
</div> <div style="text-align: center;">
</div>
<div *ngIf="playlists && playlists.length === 0" style="text-align: center;">
No playlists available. Create one from your downloading files by clicking the blue plus button. No playlists available. Create one from your downloading files by clicking the blue plus button.
</div> </div>
}
</div>
</div>
</div>
}
<div class="add-playlist-button"><button (click)="openCreatePlaylistDialog()" mat-fab><mat-icon>add</mat-icon></button></div> <div class="add-playlist-button"><button (click)="openCreatePlaylistDialog()" mat-fab><mat-icon>add</mat-icon></button></div>

View File

@@ -4,6 +4,7 @@ import { Router } from '@angular/router';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { CreatePlaylistComponent } from 'app/create-playlist/create-playlist.component'; import { CreatePlaylistComponent } from 'app/create-playlist/create-playlist.component';
import { Playlist } from 'api-types'; import { Playlist } from 'api-types';
import { saveAs } from 'file-saver';
@Component({ @Component({
selector: 'app-custom-playlists', selector: 'app-custom-playlists',

View File

@@ -1,13 +1,11 @@
<div [hidden]="!(downloads && downloads.length > 0)"> <div [hidden]="!(downloads && downloads.length > 0)">
<div style="overflow: hidden;" [ngClass]="uids ? 'rounded mat-elevation-z2' : 'mat-elevation-z8'"> <div style="overflow: hidden;" [ngClass]="uids ? 'rounded mat-elevation-z2' : 'mat-elevation-z8'">
<mat-table style="overflow: hidden" [ngClass]="uids ? 'rounded-top' : null" matSort [dataSource]="dataSource"> <mat-table style="overflow: hidden" [ngClass]="uids ? 'rounded-top' : null" matSort [dataSource]="dataSource">
<!-- Date Column --> <!-- Date Column -->
<ng-container matColumnDef="timestamp_start"> <ng-container matColumnDef="timestamp_start">
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Date">Date</ng-container> </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Date">Date</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element"> {{element.timestamp_start | date: 'short'}} </mat-cell> <mat-cell *matCellDef="let element"> {{element.timestamp_start | date: 'short'}} </mat-cell>
</ng-container> </ng-container>
<!-- Title Column --> <!-- Title Column -->
<ng-container matColumnDef="title"> <ng-container matColumnDef="title">
<mat-header-cell *matHeaderCellDef mat-sort-header style="flex: 2"> <ng-container i18n="Title">Title</ng-container> </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header style="flex: 2"> <ng-container i18n="Title">Title</ng-container> </mat-header-cell>
@@ -17,81 +15,88 @@
</span> </span>
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<!-- Subscription Column --> <!-- Subscription Column -->
<ng-container matColumnDef="sub_name"> <ng-container matColumnDef="sub_name">
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Subscription">Subscription</ng-container> </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Subscription">Subscription</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element"> <mat-cell *matCellDef="let element">
<ng-container *ngIf="element.sub_name"> @if (element.sub_name) {
{{element.sub_name}} {{element.sub_name}}
</ng-container> } @else {
<ng-container *ngIf="!element.sub_name"> <ng-container i18n="N/A">N/A</ng-container>
N/A }
</ng-container>
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<!-- Progress Column --> <!-- Progress Column -->
<ng-container matColumnDef="percent_complete"> <ng-container matColumnDef="percent_complete">
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Progress">Progress</ng-container> </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Progress">Progress</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element"> <mat-cell *matCellDef="let element">
<ng-container *ngIf="!element.error && element.step_index !== 2"> @if (!element.error) {
@if (element.step_index !== 2) {
{{STEP_INDEX_TO_LABEL[element.step_index]}} {{STEP_INDEX_TO_LABEL[element.step_index]}}
</ng-container> } @else {
<ng-container *ngIf="!element.error && element.step_index === 2"> @if (element.percent_complete) {
<ng-container *ngIf="element.percent_complete">
{{+(element.percent_complete) > 100 ? '100' : element.percent_complete}}% {{+(element.percent_complete) > 100 ? '100' : element.percent_complete}}%
</ng-container> } @else {
<ng-container *ngIf="!element.percent_complete"> <ng-container i18n="N/A">N/A</ng-container>
N/A }
</ng-container> }
</ng-container> } @else {
<ng-container *ngIf="element.error" i18n="Error">Error</ng-container> <ng-container i18n="Error">Error</ng-container>
}
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<!-- Actions Column --> <!-- Actions Column -->
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<mat-header-cell *matHeaderCellDef [ngStyle]="{flex: actionsFlex}"> <ng-container i18n="Actions">Actions</ng-container> </mat-header-cell> <mat-header-cell *matHeaderCellDef [ngStyle]="{flex: actionsFlex}"> <ng-container i18n="Actions">Actions</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element" [ngStyle]="{flex: actionsFlex}"> <mat-cell *matCellDef="let element" [ngStyle]="{flex: actionsFlex}">
<div *ngIf="!minimizeButtons"> @if (!minimizeButtons) {
<ng-container *ngFor="let downloadAction of downloadActions"> <div>
@for (downloadAction of downloadActions; track downloadAction) {
<span class="button-span"> <span class="button-span">
<mat-spinner [diameter]="28" *ngIf="downloadAction.loading && downloadAction.loading(element)" class="icon-button-spinner"></mat-spinner> @if (downloadAction.loading && downloadAction.loading(element)) {
<button *ngIf="downloadAction.show(element)" (click)="downloadAction.action(element)" [disabled]="downloadAction.loading && downloadAction.loading(element)" [matTooltip]="downloadAction.tooltip" mat-icon-button><mat-icon>{{downloadAction.icon}}</mat-icon></button> <mat-spinner [diameter]="28" class="icon-button-spinner"></mat-spinner>
}
@if (downloadAction.show(element)) {
<button (click)="downloadAction.action(element)" [disabled]="downloadAction.loading && downloadAction.loading(element)" [matTooltip]="downloadAction.tooltip" mat-icon-button><mat-icon>{{downloadAction.icon}}</mat-icon></button>
}
</span> </span>
</ng-container> }
</div> </div>
<div *ngIf="minimizeButtons"> } @else {
<div>
<button [matMenuTriggerFor]="download_actions" mat-icon-button><mat-icon>more_vert</mat-icon></button> <button [matMenuTriggerFor]="download_actions" mat-icon-button><mat-icon>more_vert</mat-icon></button>
<mat-menu #download_actions="matMenu"> <mat-menu #download_actions="matMenu">
<ng-container *ngFor="let downloadAction of downloadActions"> @for (downloadAction of downloadActions; track downloadAction) {
<button *ngIf="downloadAction.show(element)" (click)="downloadAction.action(element)" [disabled]="downloadAction.loading && downloadAction.loading(element)" mat-menu-item> @if (downloadAction.show(element)) {
<button (click)="downloadAction.action(element)" [disabled]="downloadAction.loading && downloadAction.loading(element)" mat-menu-item>
<mat-icon>{{downloadAction.icon}}</mat-icon> <mat-icon>{{downloadAction.icon}}</mat-icon>
<span>{{downloadAction.tooltip}}</span> <span>{{downloadAction.tooltip}}</span>
</button> </button>
</ng-container> }
}
</mat-menu> </mat-menu>
</div> </div>
}
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<mat-header-row [ngClass]="uids ? 'rounded-top' : null" *matHeaderRowDef="displayedColumns"></mat-header-row> <mat-header-row [ngClass]="uids ? 'rounded-top' : null" *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row> <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table> </mat-table>
<mat-paginator [ngClass]="uids ? 'rounded-bottom' : null" [pageSizeOptions]="[5, 10, 20]" <mat-paginator [ngClass]="uids ? 'rounded-bottom' : null" [pageSizeOptions]="[5, 10, 20]"
showFirstLastButtons showFirstLastButtons
aria-label="Select page of downloads"> aria-label="Select page of downloads">
</mat-paginator> </mat-paginator>
</div> </div>
<div *ngIf="!uids" class="downloads-action-button-div"> @if (!uids) {
<div class="downloads-action-button-div">
<button class="downloads-action-button" [disabled]="!running_download_exists" mat-stroked-button (click)="pauseAllDownloads()"><ng-container i18n="Pause all downloads">Pause all downloads</ng-container></button> <button class="downloads-action-button" [disabled]="!running_download_exists" mat-stroked-button (click)="pauseAllDownloads()"><ng-container i18n="Pause all downloads">Pause all downloads</ng-container></button>
<button class="downloads-action-button" [disabled]="!paused_download_exists" mat-stroked-button (click)="resumeAllDownloads()"><ng-container i18n="Resume all downloads">Resume all downloads</ng-container></button> <button class="downloads-action-button" [disabled]="!paused_download_exists" mat-stroked-button (click)="resumeAllDownloads()"><ng-container i18n="Resume all downloads">Resume all downloads</ng-container></button>
<button class="downloads-action-button" color="warn" mat-stroked-button (click)="clearDownloadsByType()"><ng-container i18n="Clear downloads">Clear downloads</ng-container></button> <button class="downloads-action-button" color="warn" mat-stroked-button (click)="clearDownloadsByType()"><ng-container i18n="Clear downloads">Clear downloads</ng-container></button>
</div> </div>
}
</div> </div>
@if ((!downloads || downloads.length === 0) && downloads_retrieved && !uids) {
<div *ngIf="(!downloads || downloads.length === 0) && downloads_retrieved && !uids"> <div>
<h4 style="text-align: center; margin-top: 10px;" i18n="No downloads label">No downloads available!</h4> <h4 style="text-align: center; margin-top: 10px;" i18n="No downloads label">No downloads available!</h4>
</div> </div>
}

View File

@@ -14,7 +14,8 @@
</mat-form-field> </mat-form-field>
</div> </div>
</mat-tab> </mat-tab>
<mat-tab *ngIf="registrationEnabled" label="Register" i18n-label="Register"> @if (registrationEnabled) {
<mat-tab label="Register" i18n-label="Register">
<div style="margin-top: 10px;"> <div style="margin-top: 10px;">
<mat-form-field style="width: 100%"> <mat-form-field style="width: 100%">
<mat-label i18n="User name">User name</mat-label> <mat-label i18n="User name">User name</mat-label>
@@ -34,13 +35,21 @@
</mat-form-field> </mat-form-field>
</div> </div>
</mat-tab> </mat-tab>
}
</mat-tab-group> </mat-tab-group>
<div *ngIf="selectedTabIndex === 0" class="login-button-div"> @if (selectedTabIndex === 0) {
<div class="login-button-div">
<button [disabled]="loggingIn" color="primary" (click)="login()" mat-raised-button><ng-container i18n="Login">Login</ng-container></button> <button [disabled]="loggingIn" color="primary" (click)="login()" mat-raised-button><ng-container i18n="Login">Login</ng-container></button>
<mat-progress-bar *ngIf="loggingIn" class="login-progress-bar" mode="indeterminate"></mat-progress-bar> @if (loggingIn) {
<mat-progress-bar class="login-progress-bar" mode="indeterminate"></mat-progress-bar>
}
</div> </div>
<div *ngIf="selectedTabIndex === 1" class="login-button-div"> } @else {
<div class="login-button-div">
<button [disabled]="registering" color="primary" (click)="register()" mat-raised-button><ng-container i18n="Register">Register</ng-container></button> <button [disabled]="registering" color="primary" (click)="register()" mat-raised-button><ng-container i18n="Register">Register</ng-container></button>
<mat-progress-bar *ngIf="registering" class="login-progress-bar" mode="indeterminate"></mat-progress-bar> @if (registering) {
<mat-progress-bar class="login-progress-bar" mode="indeterminate"></mat-progress-bar>
}
</div> </div>
}
</mat-card> </mat-card>

View File

@@ -1,21 +1,23 @@
<div style="height: 100%;"> <div style="height: 100%;">
<div *ngIf="logs_loading" style="z-index: 999; position: absolute; top: 40%; left: 50%"> @if (logs_loading) {
<div style="z-index: 999; position: absolute; top: 40%; left: 50%">
<mat-spinner [diameter]="32"></mat-spinner> <mat-spinner [diameter]="32"></mat-spinner>
</div> </div>
}
<!-- Virtual mode (fast, select text buggy) --> <!-- Virtual mode (fast, select text buggy) -->
<!--<cdk-virtual-scroll-viewport style="height: 274px;" itemSize="50" class="example-viewport"> <!--<cdk-virtual-scroll-viewport style="height: 274px;" itemSize="50" class="example-viewport">
<div *cdkVirtualFor="let log of logs; let i = index" class="example-item"> <div *cdkVirtualFor="let log of logs; let i = index" class="example-item">
<span [ngStyle]="{'color':log.color}">{{log.text}}</span> <span [ngStyle]="{'color':log.color}">{{log.text}}</span>
</div> </div>
</cdk-virtual-scroll-viewport>--> </cdk-virtual-scroll-viewport>-->
<!-- Non-virtual mode (slow, bug-free) --> <!-- Non-virtual mode (slow, bug-free) -->
<div style="height: 100%; overflow-y: auto"> <div style="height: 100%; overflow-y: auto">
<div *ngFor="let log of logs; let i = index" class="example-item"> @for (log of logs; track log) {
<div class="example-item">
<span [ngStyle]="{'color':log.color}">{{log.text}}</span> <span [ngStyle]="{'color':log.color}">{{log.text}}</span>
</div> </div>
}
</div> </div>
<div> <div>
<button style="position: absolute; right: 0px; top: 12px;" [cdkCopyToClipboard]="logs_text" (click)="copiedLogsToClipboard()" mat-mini-fab color="primary"><mat-icon style="font-size: 22px !important;">content_copy</mat-icon></button> <button style="position: absolute; right: 0px; top: 12px;" [cdkCopyToClipboard]="logs_text" (click)="copiedLogsToClipboard()" mat-mini-fab color="primary"><mat-icon style="font-size: 22px !important;">content_copy</mat-icon></button>
<div style="display: inline-block;"> <div style="display: inline-block;">
@@ -33,5 +35,4 @@
<span class="spacer"></span> <span class="spacer"></span>
<button style="float: right; margin-top: 12px;" (click)="clearLogs()" mat-stroked-button color="warn"><ng-container i18n="Clear logs button">Clear logs</ng-container></button> <button style="float: right; margin-top: 12px;" (click)="clearLogs()" mat-stroked-button color="warn"><ng-container i18n="Clear logs button">Clear logs</ng-container></button>
</div> </div>
</div> </div>

View File

@@ -1,7 +1,8 @@
<h4 *ngIf="role" mat-dialog-title><ng-container i18n="Manage role dialog title">Manage role</ng-container>&nbsp;-&nbsp;{{role.key}}</h4> @if (role) {
<h4 mat-dialog-title><ng-container i18n="Manage role dialog title">Manage role</ng-container>&nbsp;-&nbsp;{{role.key}}</h4>
<mat-dialog-content *ngIf="role"> <mat-dialog-content>
<div *ngFor="let permission of available_permissions"> @for (permission of available_permissions; track permission) {
<div>
<div matListItemTitle>{{permissionToLabel[permission] ? permissionToLabel[permission] : permission}}</div> <div matListItemTitle>{{permissionToLabel[permission] ? permissionToLabel[permission] : permission}}</div>
<div matListItemLine> <div matListItemLine>
<mat-radio-group [disabled]="permission === 'settings' && role.key === 'admin'" (change)="changeRolePermissions($event, permission)" [(ngModel)]="permissions[permission]" [attr.aria-label]="'Give role permission for ' + permission"> <mat-radio-group [disabled]="permission === 'settings' && role.key === 'admin'" (change)="changeRolePermissions($event, permission)" [(ngModel)]="permissions[permission]" [attr.aria-label]="'Give role permission for ' + permission">
@@ -10,8 +11,9 @@
</mat-radio-group> </mat-radio-group>
</div> </div>
</div> </div>
}
</mat-dialog-content> </mat-dialog-content>
}
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close><ng-container i18n="Close">Close</ng-container></button> <button mat-button mat-dialog-close><ng-container i18n="Close">Close</ng-container></button>
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -1,8 +1,7 @@
<h4 *ngIf="user" mat-dialog-title><ng-container i18n="Manage user dialog title">Manage user</ng-container>&nbsp;-&nbsp;{{user.name}}</h4> @if (user) {
<h4 mat-dialog-title><ng-container i18n="Manage user dialog title">Manage user</ng-container>&nbsp;-&nbsp;{{user.name}}</h4>
<mat-dialog-content *ngIf="user"> <mat-dialog-content>
<p><ng-container i18n="User UID">User UID:</ng-container>&nbsp;{{user.uid}}</p> <p><ng-container i18n="User UID">User UID:</ng-container>&nbsp;{{user.uid}}</p>
<div> <div>
<mat-form-field style="margin-right: 15px;"> <mat-form-field style="margin-right: 15px;">
<mat-label i18n="New password">New password</mat-label> <mat-label i18n="New password">New password</mat-label>
@@ -10,9 +9,9 @@
</mat-form-field> </mat-form-field>
<button mat-raised-button color="accent" (click)="setNewPassword()" [disabled]="newPasswordInput.length === 0"><ng-container i18n="Set new password">Set new password</ng-container></button> <button mat-raised-button color="accent" (click)="setNewPassword()" [disabled]="newPasswordInput.length === 0"><ng-container i18n="Set new password">Set new password</ng-container></button>
</div> </div>
<div> <div>
<div *ngFor="let permission of available_permissions"> @for (permission of available_permissions; track permission) {
<div>
<div matListItemTitle>{{permissionToLabel[permission] ? permissionToLabel[permission] : permission}}</div> <div matListItemTitle>{{permissionToLabel[permission] ? permissionToLabel[permission] : permission}}</div>
<div matListItemLine> <div matListItemLine>
<mat-radio-group [disabled]="permission === 'settings' && postsService.user.uid === user.uid" (change)="changeUserPermissions($event, permission)" [(ngModel)]="permissions[permission]" [attr.aria-label]="'Give user permission for ' + permission"> <mat-radio-group [disabled]="permission === 'settings' && postsService.user.uid === user.uid" (change)="changeUserPermissions($event, permission)" [(ngModel)]="permissions[permission]" [attr.aria-label]="'Give user permission for ' + permission">
@@ -22,9 +21,10 @@
</mat-radio-group> </mat-radio-group>
</div> </div>
</div> </div>
}
</div> </div>
</mat-dialog-content> </mat-dialog-content>
}
<mat-dialog-actions> <mat-dialog-actions>
<button style="margin-bottom: 5px;" mat-stroked-button mat-dialog-close><ng-container i18n="Close">Close</ng-container></button> <button style="margin-bottom: 5px;" mat-stroked-button mat-dialog-close><ng-container i18n="Close">Close</ng-container></button>
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -1,4 +1,5 @@
<div *ngIf="dataSource; else loading"> @if (dataSource) {
<div>
<div style="padding: 15px"> <div style="padding: 15px">
<div class="row"> <div class="row">
<div class="table table-responsive pb-4 pt-4"> <div class="table table-responsive pb-4 pt-4">
@@ -8,34 +9,31 @@
<input matInput (keyup)="applyFilter($event)"> <input matInput (keyup)="applyFilter($event)">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="mat-elevation-z8" style="margin-right: 15px;"> <div class="mat-elevation-z8" style="margin-right: 15px;">
<mat-table #table [dataSource]="dataSource" matSort> <mat-table #table [dataSource]="dataSource" matSort>
<!-- Name Column --> <!-- Name Column -->
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef mat-sort-header><ng-container i18n="Username users table header"> User name </ng-container></mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header><ng-container i18n="Username users table header"> User name </ng-container></mat-header-cell>
<mat-cell *matCellDef="let row"> <mat-cell *matCellDef="let row">
<span *ngIf="editObject && editObject.uid === row.uid; else noteditingname"> @if (editObject && editObject.uid === row.uid) {
<span>
<span style="width: 80%;"> <span style="width: 80%;">
<mat-form-field> <mat-form-field>
<input matInput [(ngModel)]="constructedObject['name']" type="text" style="font-size: 12px"> <input matInput [(ngModel)]="constructedObject['name']" type="text" style="font-size: 12px">
</mat-form-field> </mat-form-field>
</span> </span>
</span> </span>
<ng-template #noteditingname> } @else {
{{row.name}} {{row.name}}
</ng-template> }
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<!-- Email Column --> <!-- Email Column -->
<ng-container matColumnDef="role"> <ng-container matColumnDef="role">
<mat-header-cell *matHeaderCellDef mat-sort-header><ng-container i18n="Role users table header"> Role </ng-container></mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header><ng-container i18n="Role users table header"> Role </ng-container></mat-header-cell>
<mat-cell *matCellDef="let row"> <mat-cell *matCellDef="let row">
<span *ngIf="editObject && editObject.uid === row.uid; else noteditingemail"> @if (editObject && editObject.uid === row.uid) {
<span>
<span style="width: 80%;"> <span style="width: 80%;">
<mat-form-field> <mat-form-field>
<mat-select [(ngModel)]="constructedObject['role']"> <mat-select [(ngModel)]="constructedObject['role']">
@@ -45,17 +43,17 @@
</mat-form-field> </mat-form-field>
</span> </span>
</span> </span>
<ng-template #noteditingemail> } @else {
{{row.role}} {{row.role}}
</ng-template> }
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<!-- Actions Column --> <!-- Actions Column -->
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<mat-header-cell *matHeaderCellDef mat-sort-header><ng-container i18n="Actions users table header"> Actions </ng-container></mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header><ng-container i18n="Actions users table header"> Actions </ng-container></mat-header-cell>
<mat-cell *matCellDef="let row"> <mat-cell *matCellDef="let row">
<span *ngIf="editObject && editObject.uid === row.uid; else notediting"> @if (editObject && editObject.uid === row.uid) {
<span>
<button mat-icon-button color="primary" (click)="finishEditing(row.uid)" matTooltip="Save" i18n-matTooltip="save user edit action button tooltip"> <button mat-icon-button color="primary" (click)="finishEditing(row.uid)" matTooltip="Save" i18n-matTooltip="save user edit action button tooltip">
<mat-icon>done</mat-icon> <mat-icon>done</mat-icon>
</button> </button>
@@ -63,11 +61,11 @@
<mat-icon>cancel</mat-icon> <mat-icon>cancel</mat-icon>
</button> </button>
</span> </span>
<ng-template #notediting> } @else {
<button mat-icon-button (click)="enableEditMode(row.uid)" matTooltip="Edit user" i18n-matTooltip="edit user action button tooltip"> <button mat-icon-button (click)="enableEditMode(row.uid)" matTooltip="Edit user" i18n-matTooltip="edit user action button tooltip">
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
</ng-template> }
<button (click)="manageUser(row.uid)" mat-icon-button [disabled]="editObject && editObject.uid === row.uid" matTooltip="Manage user" i18n-matTooltip="manage user action button tooltip"> <button (click)="manageUser(row.uid)" mat-icon-button [disabled]="editObject && editObject.uid === row.uid" matTooltip="Manage user" i18n-matTooltip="manage user action button tooltip">
<mat-icon>settings</mat-icon> <mat-icon>settings</mat-icon>
</button> </button>
@@ -76,17 +74,14 @@
</button> </button>
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"> <mat-row *matRowDef="let row; columns: displayedColumns;">
</mat-row> </mat-row>
</mat-table> </mat-table>
<mat-paginator #paginator [length]="length" <mat-paginator #paginator [length]="length"
[pageSize]="pageSize" [pageSize]="pageSize"
[pageSizeOptions]="pageSizeOptions"> [pageSizeOptions]="pageSizeOptions">
</mat-paginator> </mat-paginator>
<button color="primary" [disabled]="!this.users" mat-raised-button (click)="openAddUserDialog()" style="float: left; top: -45px; left: 15px"> <button color="primary" [disabled]="!this.users" mat-raised-button (click)="openAddUserDialog()" style="float: left; top: -45px; left: 15px">
<ng-container i18n="Add users button">Add Users</ng-container> <ng-container i18n="Add users button">Add Users</ng-container>
</button> </button>
@@ -95,14 +90,14 @@
</div> </div>
<button color="primary" [matMenuTriggerFor]="edit_roles_menu" class="edit-role" mat-raised-button><ng-container i18n="Edit role">Edit Role</ng-container></button> <button color="primary" [matMenuTriggerFor]="edit_roles_menu" class="edit-role" mat-raised-button><ng-container i18n="Edit role">Edit Role</ng-container></button>
<mat-menu #edit_roles_menu="matMenu"> <mat-menu #edit_roles_menu="matMenu">
<button (click)="openModifyRole(role)" mat-menu-item *ngFor="let role of roles">{{role.key}}</button> @for (role of roles; track role) {
<button (click)="openModifyRole(role)" mat-menu-item>{{role.key}}</button>
}
</mat-menu> </mat-menu>
</div> </div>
</div> </div>
} @else {
<div style="position: absolute" class="centered"> <div style="position: absolute" class="centered">
<ng-template #loading>
<mat-spinner></mat-spinner> <mat-spinner></mat-spinner>
</ng-template>
</div> </div>
}

View File

@@ -8,25 +8,31 @@
</div> </div>
</mat-card-subtitle> </mat-card-subtitle>
<mat-card-title> <mat-card-title>
<ng-container *ngIf="NOTIFICATION_PREFIX[notification.type]"> @if (NOTIFICATION_PREFIX[notification.type]) {
{{NOTIFICATION_PREFIX[notification.type]}} {{NOTIFICATION_PREFIX[notification.type]}}
</ng-container> }
</mat-card-title> </mat-card-title>
</mat-card-header> </mat-card-header>
<mat-card-content> <mat-card-content>
<ng-container *ngIf="NOTIFICATION_SUFFIX_KEY[notification.type]"> @if (NOTIFICATION_SUFFIX_KEY[notification.type]) {
<div style="word-break: break-word"> <div style="word-break: break-word">
{{notification['data'][NOTIFICATION_SUFFIX_KEY[notification.type]]}} {{notification['data'][NOTIFICATION_SUFFIX_KEY[notification.type]]}}
</div> </div>
</ng-container> }
</mat-card-content> </mat-card-content>
<mat-card-actions class="notification-actions" *ngIf="notification.actions?.length > 0"> @if (notification.actions?.length > 0) {
<mat-card-actions class="notification-actions">
<button matTooltip="Remove" i18n-matTooltip="Remove" (click)="emitDeleteNotification(notification.uid)" mat-icon-button><mat-icon>close</mat-icon></button> <button matTooltip="Remove" i18n-matTooltip="Remove" (click)="emitDeleteNotification(notification.uid)" mat-icon-button><mat-icon>close</mat-icon></button>
<span *ngFor="let action of notification.actions"> @for (action of notification.actions; track action) {
<span>
<button [matTooltip]="NOTIFICATION_ACTION_TO_STRING[action]" (click)="emitNotificationAction(notification, action)" mat-icon-button><mat-icon>{{NOTIFICATION_ICON[action]}}</mat-icon></button> <button [matTooltip]="NOTIFICATION_ACTION_TO_STRING[action]" (click)="emitNotificationAction(notification, action)" mat-icon-button><mat-icon>{{NOTIFICATION_ICON[action]}}</mat-icon></button>
</span> </span>
}
</mat-card-actions> </mat-card-actions>
<span *ngIf="!notification.read" class="dot"></span> }
@if (!notification.read) {
<span class="dot"></span>
}
</mat-card> </mat-card>
</div> </div>
</cdk-virtual-scroll-viewport> </cdk-virtual-scroll-viewport>

View File

@@ -1,10 +1,16 @@
<div *ngIf="notifications !== null && notifications.length === 0" style="text-align: center; margin: 10px;" i18n="No notifications available">No notifications available</div> @if (notifications !== null && notifications.length === 0) {
<div *ngIf="notifications?.length > 0"> <div style="text-align: center; margin: 10px;" i18n="No notifications available">No notifications available</div>
}
@if (notifications?.length > 0) {
<div>
<div class="notifications-list-parent"> <div class="notifications-list-parent">
<mat-chip-listbox [value]="selectedFilters" [multiple]="true" (change)="selectedFiltersChanged($event)"> <mat-chip-listbox [value]="selectedFilters" [multiple]="true" (change)="selectedFiltersChanged($event)">
<mat-chip-option *ngFor="let filter of notificationFilters | keyvalue: originalOrder" [value]="filter.key" [selected]="selectedFilters.includes(filter.key)" color="accent">{{filter.value.label}}</mat-chip-option> @for (filter of notificationFilters | keyvalue: originalOrder; track filter) {
<mat-chip-option [value]="filter.key" [selected]="selectedFilters.includes(filter.key)" color="accent">{{filter.value.label}}</mat-chip-option>
}
</mat-chip-listbox> </mat-chip-listbox>
<app-notifications-list class="notifications-list" [style.height]="list_height" (notificationAction)="notificationAction($event)" (deleteNotification)="deleteNotification($event)" [notifications]="filtered_notifications"></app-notifications-list> <app-notifications-list class="notifications-list" [style.height]="list_height" (notificationAction)="notificationAction($event)" (deleteNotification)="deleteNotification($event)" [notifications]="filtered_notifications"></app-notifications-list>
</div> </div>
<button style="margin: 10px 0px 2px 10px;" *ngIf="notifications?.length > 0" color="warn" (click)="deleteAllNotifications()" mat-stroked-button>Remove all</button> <button style="margin: 10px 0px 2px 10px;" color="warn" (click)="deleteAllNotifications()" mat-stroked-button>Remove all</button>
</div> </div>
}

View File

@@ -6,8 +6,11 @@
</div> </div>
<!-- Files title --> <!-- Files title -->
<div class="col-12 order-1 col-sm-4 order-sm-2 d-flex justify-content-center"> <div class="col-12 order-1 col-sm-4 order-sm-2 d-flex justify-content-center">
<h4 *ngIf="!customHeader" class="my-videos-title" i18n="My files title">My files</h4> @if (!customHeader) {
<h4 *ngIf="customHeader" class="my-videos-title">{{customHeader}}</h4> <h4 class="my-videos-title" i18n="My files title">My files</h4>
} @else {
<h4 class="my-videos-title">{{customHeader}}</h4>
}
</div> </div>
<!-- Search --> <!-- Search -->
<div class="col-12 order-3 col-sm-4 order-sm-3 d-flex justify-content-center"> <div class="col-12 order-3 col-sm-4 order-sm-3 d-flex justify-content-center">
@@ -21,57 +24,77 @@
<!-- Filters --> <!-- Filters -->
<div class="row justify-content-center"> <div class="row justify-content-center">
<mat-chip-listbox class="filter-list" [value]="selectedFilters" [multiple]="true" (change)="selectedFiltersChanged($event)"> <mat-chip-listbox class="filter-list" [value]="selectedFilters" [multiple]="true" (change)="selectedFiltersChanged($event)">
<mat-chip-option *ngFor="let filter of fileFilters | keyvalue: originalOrder" [value]="filter.key" [selected]="selectedFilters.includes(filter.key)" color="accent">{{filter.value.label}}</mat-chip-option> @for (filter of fileFilters | keyvalue: originalOrder; track filter) {
<mat-chip-option [value]="filter.key" [selected]="selectedFilters.includes(filter.key)" color="accent">{{filter.value.label}}</mat-chip-option>
}
</mat-chip-listbox> </mat-chip-listbox>
</div> </div>
</div> </div>
<div> <div>
<!-- Files --> <!-- Files -->
<div *ngIf="!selectMode" class="container" style="margin-bottom: 16px"> @if (!selectMode) {
<div class="container" style="margin-bottom: 16px">
<div class="row justify-content-center"> <div class="row justify-content-center">
<!-- Real cards --> <!-- Real cards -->
<ng-container *ngIf="normal_files_received && paged_data"> @if (normal_files_received && paged_data) {
<div style="display: flex; align-items: center;" *ngFor="let file of paged_data; let i = index" class="mb-2 mt-2 d-flex justify-content-center" [ngClass]="[ postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]"> @for (file of paged_data; track file; let i = $index) {
<div style="display: flex; align-items: center;" class="mb-2 mt-2 d-flex justify-content-center" [ngClass]="[ postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]">
<app-unified-file-card [ngClass]="downloading_content[file.uid] ? 'blurred' : ''" [index]="i" [card_size]="postsService.card_size" [locale]="postsService.locale" (goToFile)="goToFile($event)" (goToSubscription)="goToSubscription($event)" (toggleFavorite)="toggleFavorite($event)" [file_obj]="file" [use_youtubedl_archive]="postsService.config['Downloader']['use_youtubedl_archive']" [availablePlaylists]="playlists" (addFileToPlaylist)="addFileToPlaylist($event)" [loading]="false" (deleteFile)="deleteFile($event)" [baseStreamPath]="postsService.path" [jwtString]="postsService.isLoggedIn ? this.postsService.token : ''"></app-unified-file-card> <app-unified-file-card [ngClass]="downloading_content[file.uid] ? 'blurred' : ''" [index]="i" [card_size]="postsService.card_size" [locale]="postsService.locale" (goToFile)="goToFile($event)" (goToSubscription)="goToSubscription($event)" (toggleFavorite)="toggleFavorite($event)" [file_obj]="file" [use_youtubedl_archive]="postsService.config['Downloader']['use_youtubedl_archive']" [availablePlaylists]="playlists" (addFileToPlaylist)="addFileToPlaylist($event)" [loading]="false" (deleteFile)="deleteFile($event)" [baseStreamPath]="postsService.path" [jwtString]="postsService.isLoggedIn ? this.postsService.token : ''"></app-unified-file-card>
<mat-spinner *ngIf="downloading_content[file.uid]" class="downloading-spinner" [diameter]="32"></mat-spinner> @if (downloading_content[file.uid]) {
<mat-spinner class="downloading-spinner" [diameter]="32"></mat-spinner>
}
</div> </div>
<div *ngIf="paged_data.length === 0"> } @empty {
<div>
<ng-container i18n="No files found">No files found.</ng-container> <ng-container i18n="No files found">No files found.</ng-container>
</div> </div>
</ng-container> }
}
<!-- Fake cards --> <!-- Fake cards -->
<ng-container> <ng-container>
<div *ngFor="let file of loading_files; let i = index" class="mb-2 mt-2 d-flex justify-content-center" [ngClass]="[normal_files_received ? 'hide' : '', postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]"> @for (file of loading_files; track file; let i = $index) {
<div class="mb-2 mt-2 d-flex justify-content-center" [ngClass]="[normal_files_received ? 'hide' : '', postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]">
<app-unified-file-card [index]="i" [card_size]="postsService.card_size" [locale]="postsService.locale" [loading]="true" [theme]="postsService.theme"></app-unified-file-card> <app-unified-file-card [index]="i" [card_size]="postsService.card_size" [locale]="postsService.locale" [loading]="true" [theme]="postsService.theme"></app-unified-file-card>
</div> </div>
}
</ng-container> </ng-container>
</div> </div>
</div> </div>
} @else {
<div *ngIf="selectMode"> <div>
<!-- If selected files e.g. for creating a playlist --> <!-- If selected files e.g. for creating a playlist -->
<mat-tab-group [(selectedIndex)]="selectedIndex"> <mat-tab-group [(selectedIndex)]="selectedIndex">
<mat-tab label="Order" i18n-label="Order"> <mat-tab label="Order" i18n-label="Order">
<div *ngIf="selected_data.length"> @if (selected_data.length) {
<span *ngIf="reverse_order === false" i18n="Normal order">Normal order&nbsp;</span> <div>
<span *ngIf="reverse_order === true" i18n="Reverse order">Reverse order&nbsp;</span> @if (reverse_order === false) {
<span i18n="Normal order">Normal order&nbsp;</span>
}
@if (reverse_order === true) {
<span i18n="Reverse order">Reverse order&nbsp;</span>
}
<button (click)="toggleSelectionOrder()" mat-icon-button><mat-icon>{{!reverse_order ? 'arrow_downward' : 'arrow_upward'}}</mat-icon></button> <button (click)="toggleSelectionOrder()" mat-icon-button><mat-icon>{{!reverse_order ? 'arrow_downward' : 'arrow_upward'}}</mat-icon></button>
</div> </div>
}
<!-- Selection order --> <!-- Selection order -->
<mat-button-toggle-group *ngIf="selected_data.length" class="media-list" cdkDropList (cdkDropListDropped)="drop($event)" style="width: 80%; left: 9%" vertical #group="matButtonToggleGroup"> @if (selected_data.length) {
<mat-button-toggle-group class="media-list" cdkDropList (cdkDropListDropped)="drop($event)" style="width: 80%; left: 9%" vertical #group="matButtonToggleGroup">
<!-- The following for loop can be optimized but it requires a pipe https://stackoverflow.com/a/35703364/8088021 --> <!-- The following for loop can be optimized but it requires a pipe https://stackoverflow.com/a/35703364/8088021 -->
<mat-button-toggle class="media-box" cdkDrag *ngFor="let file of (reverse_order ? selected_data_objs.slice().reverse() : selected_data_objs); let i = index" [checked]="false"><div><div class="playlist-item-text">{{file.title}}</div> <button (click)="removeSelectedFile(i)" class="remove-item-button" mat-icon-button><mat-icon>cancel</mat-icon></button></div></mat-button-toggle> @for (file of (reverse_order ? selected_data_objs.slice().reverse() : selected_data_objs); track file; let i = $index) {
<mat-button-toggle class="media-box" cdkDrag [checked]="false"><div><div class="playlist-item-text">{{file.title}}</div> <button (click)="removeSelectedFile(i)" class="remove-item-button" mat-icon-button><mat-icon>cancel</mat-icon></button></div></mat-button-toggle>
}
</mat-button-toggle-group> </mat-button-toggle-group>
} @else {
<div style="margin-top: 20px;" *ngIf="!selected_data.length"> <div style="margin-top: 20px;">
<h4 style="text-align: center;">No files selected!</h4> <h4 style="text-align: center;">No files selected!</h4>
</div> </div>
}
</mat-tab> </mat-tab>
<mat-tab label="Select files" i18n-label="Select files"> <mat-tab label="Select files" i18n-label="Select files">
<mat-selection-list *ngIf="normal_files_received" (selectionChange)="fileSelectionChanged($event)"> @if (normal_files_received) {
<mat-list-option [selected]="selected_data.includes(file.uid)" *ngFor="let file of paged_data" [value]="file"> <mat-selection-list (selectionChange)="fileSelectionChanged($event)">
@for (file of paged_data; track file) {
<mat-list-option [selected]="selected_data.includes(file.uid)" [value]="file">
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-10 select-file-title"> <div class="col-10 select-file-title">
@@ -81,25 +104,31 @@
<div class="col-2">{{file.registered | date:'shortDate'}}</div> <div class="col-2">{{file.registered | date:'shortDate'}}</div>
</div> </div>
</div> </div>
</mat-list-option> </mat-list-option>
}
</mat-selection-list> </mat-selection-list>
}
<ng-container *ngIf="!normal_files_received && loading_files && loading_files.length > 0"> @if (!normal_files_received && loading_files && loading_files.length > 0) {
<mat-selection-list *ngIf="!normal_files_received"> @if (!normal_files_received) {
<mat-list-option *ngFor="let file of paged_data"> <mat-selection-list>
@for (file of paged_data; track file) {
<mat-list-option>
<content-loader class="list-ghosts" [backgroundColor]="postsService.theme.ghost_primary" [foregroundColor]="postsService.theme.ghost_secondary" viewBox="0 0 250 8"><svg:rect x="0" y="0" rx="3" ry="3" width="250" height="8" /></content-loader> <content-loader class="list-ghosts" [backgroundColor]="postsService.theme.ghost_primary" [foregroundColor]="postsService.theme.ghost_secondary" viewBox="0 0 250 8"><svg:rect x="0" y="0" rx="3" ry="3" width="250" height="8" /></content-loader>
</mat-list-option> </mat-list-option>
}
</mat-selection-list> </mat-selection-list>
</ng-container> }
}
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>
</div> </div>
}
<div style="position: relative;" *ngIf="usePaginator && selectedIndex > 0"> @if (usePaginator && selectedIndex > 0) {
<div style="position: relative;">
<mat-paginator class="paginator" #paginator (page)="pageChangeEvent($event)" [length]="file_count" <mat-paginator class="paginator" #paginator (page)="pageChangeEvent($event)" [length]="file_count"
[pageSize]="pageSize" [pageSize]="pageSize"
[pageSizeOptions]="[5, 10, 25, 100, this.paged_data && this.paged_data.length > 100 ? this.paged_data.length : 250]"> [pageSizeOptions]="[5, 10, 25, 100, this.paged_data && this.paged_data.length > 100 ? this.paged_data.length : 250]">
</mat-paginator> </mat-paginator>
</div> </div>
}
</div> </div>

View File

@@ -8,6 +8,7 @@ import { distinctUntilChanged } from 'rxjs/operators';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { MatChipListboxChange } from '@angular/material/chips'; import { MatChipListboxChange } from '@angular/material/chips';
import { MatSelectionListChange } from '@angular/material/list'; import { MatSelectionListChange } from '@angular/material/list';
import { saveAs } from 'file-saver';
@Component({ @Component({
selector: 'app-recent-videos', selector: 'app-recent-videos',
@@ -380,8 +381,8 @@ export class RecentVideosComponent implements OnInit {
fileSelectionChanged(event: MatSelectionListChange): void { fileSelectionChanged(event: MatSelectionListChange): void {
// TODO: make sure below line is possible (_selected is private) // TODO: make sure below line is possible (_selected is private)
const adding = event.option['_selected']; const adding = event.options['_selected'];
const value = event.option.value; const value = adding.value;
if (adding) { if (adding) {
this.selected_data.push(value.uid); this.selected_data.push(value.uid);
this.selected_data_objs.push(value); this.selected_data_objs.push(value);

View File

@@ -1,11 +1,14 @@
<span class="text" [ngStyle]="{'-webkit-line-clamp': !see_more_active ? line_limit : null}" [innerHTML]="text | linkify"></span> <span class="text" [ngStyle]="{'-webkit-line-clamp': !see_more_active ? line_limit : null}" [innerHTML]="text | linkify"></span>
<span> <span>
<a [routerLink]="[]" (click)="toggleSeeMore()"> <a [routerLink]="[]" (click)="toggleSeeMore()">
<ng-container *ngIf="!see_more_active" i18n="See more"> @if (!see_more_active) {
<ng-container i18n="See more">
See more. See more.
</ng-container> </ng-container>
<ng-container *ngIf="see_more_active" i18n="See less"> } @else {
<ng-container i18n="See less">
See less. See less.
</ng-container> </ng-container>
}
</a> </a>
</span> </span>

View File

@@ -1 +1,3 @@
<button *ngIf="show_skip_ad_button" (click)="skipAdButtonClicked()" mat-flat-button><ng-container i18n="Skip ad button">Skip ad</ng-container></button> @if (show_skip_ad_button) {
<button (click)="skipAdButtonClicked()" mat-flat-button><ng-container i18n="Skip ad button">Skip ad</ng-container></button>
}

View File

@@ -2,9 +2,11 @@
<div style="display: inline-block;"> <div style="display: inline-block;">
<mat-form-field appearance="outline" style="width: 165px;"> <mat-form-field appearance="outline" style="width: 165px;">
<mat-select [(ngModel)]="this.sortProperty" (selectionChange)="emitSortOptionChanged()"> <mat-select [(ngModel)]="this.sortProperty" (selectionChange)="emitSortOptionChanged()">
<mat-option *ngFor="let sortOption of sortProperties | keyvalue" [value]="sortOption.key"> @for (sortOption of sortProperties | keyvalue; track sortOption) {
<mat-option [value]="sortOption.key">
{{sortOption['value']['label']}} {{sortOption['value']['label']}}
</mat-option> </mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>

View File

@@ -1,7 +1,7 @@
<h4 mat-dialog-title><ng-container i18n="Task settings">Task settings - {{task.title}}</ng-container></h4> <h4 mat-dialog-title><ng-container i18n="Task settings">Task settings - {{task.title}}</ng-container></h4>
<mat-dialog-content> <mat-dialog-content>
<div *ngIf="task_key === 'delete_old_files'"> @if (task_key === 'delete_old_files') {
<div>
<mat-form-field color="accent"> <mat-form-field color="accent">
<mat-label i18n="Delete files older than">Delete files older than</mat-label> <mat-label i18n="Delete files older than">Delete files older than</mat-label>
<input [(ngModel)]="new_options['threshold_days']" matInput onlyNumber required> <input [(ngModel)]="new_options['threshold_days']" matInput onlyNumber required>
@@ -14,16 +14,18 @@
<mat-checkbox [disabled]="new_options['blacklist_files']" [(ngModel)]="new_options['blacklist_subscription_files']" i18n="Blacklist deleted subscription files" placeholder="Archive mode must be enabled" placeholder-i18n>Blacklist deleted subscription files</mat-checkbox> <mat-checkbox [disabled]="new_options['blacklist_files']" [(ngModel)]="new_options['blacklist_subscription_files']" i18n="Blacklist deleted subscription files" placeholder="Archive mode must be enabled" placeholder-i18n>Blacklist deleted subscription files</mat-checkbox>
</div> </div>
</div> </div>
}
<div> <div>
<mat-checkbox [(ngModel)]="new_options['auto_confirm']" i18n="Do not ask for confirmation">Do not ask for confirmation</mat-checkbox> <mat-checkbox [(ngModel)]="new_options['auto_confirm']" i18n="Do not ask for confirmation">Do not ask for confirmation</mat-checkbox>
</div> </div>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close> <button mat-button mat-dialog-close>
<ng-container *ngIf="optionsChanged()" i18n="Task settings cancel button">Cancel</ng-container> @if (optionsChanged()) {
<ng-container *ngIf="!optionsChanged()" i18n="Task settings close button">Close</ng-container> <ng-container i18n="Task settings cancel button">Cancel</ng-container>
} @else {
<ng-container i18n="Task settings close button">Close</ng-container>
}
</button> </button>
<button mat-button [disabled]="!optionsChanged()" (click)="saveSettings()"><ng-container i18n="Save button">Save</ng-container></button> <button mat-button [disabled]="!optionsChanged()" (click)="saveSettings()"><ng-container i18n="Save button">Save</ng-container></button>
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -10,64 +10,74 @@
</span> </span>
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<!-- Last Ran Column --> <!-- Last Ran Column -->
<ng-container matColumnDef="last_ran"> <ng-container matColumnDef="last_ran">
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Last ran">Last ran</ng-container> </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Last ran">Last ran</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element"> <mat-cell *matCellDef="let element">
<ng-container *ngIf="element.last_ran">{{element.last_ran*1000 | date: 'short'}}</ng-container> @if (element.last_ran) {
<ng-container i18n="N/A" *ngIf="!element.last_ran">N/A</ng-container> {{element.last_ran*1000 | date: 'short'}}
} @else {
<ng-container i18n="N/A">N/A</ng-container>
}
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<!-- Last Confirmed Column --> <!-- Last Confirmed Column -->
<ng-container matColumnDef="last_confirmed"> <ng-container matColumnDef="last_confirmed">
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Last confirmed">Last confirmed</ng-container> </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Last confirmed">Last confirmed</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element"> <mat-cell *matCellDef="let element">
<ng-container *ngIf="element.last_confirmed">{{element.last_confirmed*1000 | date: 'short'}}</ng-container> @if (element.last_confirmed) {
<ng-container i18n="N/A" *ngIf="!element.last_confirmed">N/A</ng-container> {{element.last_confirmed*1000 | date: 'short'}}
} @else {
<ng-container i18n="N/A">N/A</ng-container>
}
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<!-- Status Column --> <!-- Status Column -->
<ng-container matColumnDef="status"> <ng-container matColumnDef="status">
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Status">Status</ng-container> </mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Status">Status</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element"> <mat-cell *matCellDef="let element">
<span *ngIf="element.running || element.confirming"><mat-spinner matTooltip="Busy" i18n-matTooltip="Busy" [diameter]="25"></mat-spinner></span> @if (element.running || element.confirming) {
<span *ngIf="!(element.running || element.confirming) && element.schedule" style="display: flex"> <span><mat-spinner matTooltip="Busy" i18n-matTooltip="Busy" [diameter]="25"></mat-spinner></span>
} @else if (element.schedule) {
<span style="display: flex">
<ng-container i18n="Scheduled">Scheduled for</ng-container> <ng-container i18n="Scheduled">Scheduled for</ng-container>
{{element.next_invocation | date: 'short'}}<mat-icon style="font-size: 16px; display: inline-flex; align-items: center; padding-left: 5px; padding-bottom: 6px;" *ngIf="element.schedule.type === 'recurring'">repeat</mat-icon> {{element.next_invocation | date: 'short'}}
@if (element.schedule.type === 'recurring') {
<mat-icon style="font-size: 16px; display: inline-flex; align-items: center; padding-left: 5px; padding-bottom: 6px;">repeat</mat-icon>
}
</span> </span>
<span *ngIf="!(element.running || element.confirming) && !element.schedule"> } @else {
<span>
<ng-container i18n="Not scheduled">Not scheduled</ng-container> <ng-container i18n="Not scheduled">Not scheduled</ng-container>
</span> </span>
}
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<!-- Actions Column --> <!-- Actions Column -->
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<mat-header-cell *matHeaderCellDef> <ng-container i18n="Actions">Actions</ng-container> </mat-header-cell> <mat-header-cell *matHeaderCellDef> <ng-container i18n="Actions">Actions</ng-container> </mat-header-cell>
<mat-cell *matCellDef="let element"> <mat-cell *matCellDef="let element">
<div class="container"> <div class="container">
<div class="row justify-content-start"> <div class="row justify-content-start">
<div *ngIf="element.data?.uids?.length > 0 || (!element.data?.uids && element.data)" class="col-12 mt-2" style="display: flex; justify-content: center;"> @if (element.data?.uids?.length > 0 || (!element.data?.uids && element.data)) {
<div class="col-12 mt-2" style="display: flex; justify-content: center;">
<ng-container> <ng-container>
<button (click)="confirmTask(element.key)" [disabled]="element.running || element.confirming" mat-stroked-button> <button (click)="confirmTask(element.key)" [disabled]="element.running || element.confirming" mat-stroked-button>
<ng-container *ngIf="element.key == 'missing_files_check'"> @switch(element.key) {
@case ('missing_files_check') {
<ng-container i18n="Clear missing files from DB">Clear missing files from DB:</ng-container>{{element.data.uids.length}} <ng-container i18n="Clear missing files from DB">Clear missing files from DB:</ng-container>{{element.data.uids.length}}
</ng-container> } @case ('duplicate_files_check') {
<ng-container *ngIf="element.key == 'duplicate_files_check'">
<ng-container i18n="Clear duplicate files from DB">Clear duplicate files from DB:</ng-container>&nbsp;{{element.data.uids.length}} <ng-container i18n="Clear duplicate files from DB">Clear duplicate files from DB:</ng-container>&nbsp;{{element.data.uids.length}}
</ng-container> } @case ('youtubedl_update_check') {
<ng-container *ngIf="element.key == 'youtubedl_update_check'">
<ng-container i18n="Update binary to">Update binary to:</ng-container>&nbsp;{{element.data}} <ng-container i18n="Update binary to">Update binary to:</ng-container>&nbsp;{{element.data}}
</ng-container> } @case ('delete_old_files') {
<ng-container *ngIf="element.key == 'delete_old_files'">
<ng-container i18n="Delete old files">Delete old files:</ng-container>&nbsp;{{element.data.files_to_remove.length}} <ng-container i18n="Delete old files">Delete old files:</ng-container>&nbsp;{{element.data.files_to_remove.length}}
</ng-container> }
}
</button> </button>
</ng-container> </ng-container>
</div> </div>
}
<div class="col-3"> <div class="col-3">
<button (click)="runTask(element.key)" [disabled]="element.running || element.confirming" mat-icon-button matTooltip="Run" i18n-matTooltip="Run"><mat-icon>play_arrow</mat-icon></button> <button (click)="runTask(element.key)" [disabled]="element.running || element.confirming" mat-icon-button matTooltip="Run" i18n-matTooltip="Run"><mat-icon>play_arrow</mat-icon></button>
</div> </div>
@@ -77,28 +87,28 @@
<div class="col-3"> <div class="col-3">
<button (click)="openTaskSettings(element)" mat-icon-button matTooltip="Settings" i18n-matTooltip="Settings"><mat-icon>settings</mat-icon></button> <button (click)="openTaskSettings(element)" mat-icon-button matTooltip="Settings" i18n-matTooltip="Settings"><mat-icon>settings</mat-icon></button>
</div> </div>
<div *ngIf="element.error" class="col-3"> @if (element.error) {
<div class="col-3">
<button (click)="showError(element)" mat-icon-button matTooltip="Show error" i18n-matTooltip="Show error"><mat-icon>warning</mat-icon></button> <button (click)="showError(element)" mat-icon-button matTooltip="Show error" i18n-matTooltip="Show error"><mat-icon>warning</mat-icon></button>
</div> </div>
}
</div> </div>
</div> </div>
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row> <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table> </mat-table>
<mat-paginator [pageSizeOptions]="[10, 20]" <mat-paginator [pageSizeOptions]="[10, 20]"
showFirstLastButtons showFirstLastButtons
aria-label="Select page of tasks"> aria-label="Select page of tasks">
</mat-paginator> </mat-paginator>
</div> </div>
<button style="margin-top: 10px; margin-left: 5px;" mat-stroked-button (click)="openRestoreDBBackupDialog()" i18n="Restore DB from backup button">Restore DB from backup</button> <button style="margin-top: 10px; margin-left: 5px;" mat-stroked-button (click)="openRestoreDBBackupDialog()" i18n="Restore DB from backup button">Restore DB from backup</button>
<button style="margin-top: 10px; margin-left: 5px;" mat-stroked-button (click)="resetTasks()" color="warn" i18n="Reset tasks button">Reset tasks</button> <button style="margin-top: 10px; margin-left: 5px;" mat-stroked-button (click)="resetTasks()" color="warn" i18n="Reset tasks button">Reset tasks</button>
</div> </div>
@if ((!tasks || tasks.length === 0) && tasks_retrieved) {
<div *ngIf="(!tasks || tasks.length === 0) && tasks_retrieved"> <div>
<h4 style="text-align: center; margin-top: 10px;" i18n="No tasks label">No tasks available!</h4> <h4 style="text-align: center; margin-top: 10px;" i18n="No tasks label">No tasks available!</h4>
</div> </div>
}

View File

@@ -1,12 +1,17 @@
<div class="chat-container" #scrollContainer *ngIf="visible_chat"> @if (visible_chat) {
<div class="chat-container" #scrollContainer>
<div style="width: 250px; text-align: center;"><strong>Twitch Chat</strong></div> <div style="width: 250px; text-align: center;"><strong>Twitch Chat</strong></div>
<div #chat style="max-width: 250px" *ngFor="let chat of visible_chat; let last = last"> @for (chat of visible_chat; track chat; let last = $last) {
<div #chat style="max-width: 250px">
{{chat.timestamp_str}} - <strong [style.color]="chat.user_color ? chat.user_color : null">{{chat.name}}</strong>: {{chat.message}} {{chat.timestamp_str}} - <strong [style.color]="chat.user_color ? chat.user_color : null">{{chat.name}}</strong>: {{chat.message}}
{{last ? scrollToBottom() : ''}} {{last ? scrollToBottom() : ''}}
</div> </div>
}
</div> </div>
}
<ng-container *ngIf="chat_response_received && !full_chat"> @if (chat_response_received && !full_chat) {
<button [disabled]="downloading_chat" (click)="downloadTwitchChat()" class="download-button" mat-raised-button color="accent"><ng-container i18n="Download Twitch Chat button">Download Twitch Chat</ng-container></button> <button [disabled]="downloading_chat" (click)="downloadTwitchChat()" class="download-button" mat-raised-button color="accent"><ng-container i18n="Download Twitch Chat button">Download Twitch Chat</ng-container></button>
<mat-spinner *ngIf="downloading_chat" class="downloading-spinner" [diameter]="30"></mat-spinner> @if (downloading_chat) {
</ng-container> <mat-spinner class="downloading-spinner" [diameter]="30"></mat-spinner>
}
}

View File

@@ -1,73 +1,103 @@
<div (mouseenter)="onMouseOver()" (mouseleave)="onMouseOut()" (contextmenu)="onRightClick($event)" style="position: relative; width: fit-content;"> <div (mouseenter)="onMouseOver()" (mouseleave)="onMouseOut()" (contextmenu)="onRightClick($event)" style="position: relative; width: fit-content;">
<div *ngIf="!loading" class="download-time"> @if (!loading) {
<div class="download-time">
<mat-icon class="audio-video-icon">{{(file_obj.type === 'audio' || file_obj.isAudio) ? 'audiotrack' : 'movie'}}</mat-icon> <mat-icon class="audio-video-icon">{{(file_obj.type === 'audio' || file_obj.isAudio) ? 'audiotrack' : 'movie'}}</mat-icon>
&nbsp;&nbsp; &nbsp;&nbsp;
<ng-container i18n="Auto-generated label" *ngIf="file_obj.auto">Auto-generated</ng-container> @if (file_obj.auto) {
<ng-container *ngIf="!file_obj.auto">{{file_obj.registered | date:'shortDate' : undefined : locale.ngID}}</ng-container> <ng-container i18n="Auto-generated label">Auto-generated</ng-container>
}
@else {
{{file_obj.registered | date:'shortDate' : undefined : locale.ngID}}
}
</div> </div>
<div *ngIf="loading" class="download-time" style="width: 75%; margin-top: 5px;"><content-loader [backgroundColor]="theme.ghost_primary" [foregroundColor]="theme.ghost_secondary" viewBox="0 0 250 30"><svg:rect x="0" y="0" rx="3" ry="3" width="250" height="30" /></content-loader></div> }
@else {
<div class="download-time" style="width: 75%; margin-top: 5px;"><content-loader [backgroundColor]="theme.ghost_primary" [foregroundColor]="theme.ghost_secondary" viewBox="0 0 250 30"><svg:rect x="0" y="0" rx="3" ry="3" width="250" height="30" /></content-loader></div>
}
<!-- The context menu trigger must be kept above the "more info" menu --> <!-- The context menu trigger must be kept above the "more info" menu -->
<div style="visibility: hidden; position: fixed" <div style="visibility: hidden; position: fixed"
[style.left]="contextMenuPosition.x" [style.left]="contextMenuPosition.x"
[style.top]="contextMenuPosition.y" [style.top]="contextMenuPosition.y"
[matMenuTriggerFor]="context_menu"> [matMenuTriggerFor]="context_menu">
</div> </div>
<button *ngIf="!file_obj || !file_obj.auto" [disabled]="loading" [matMenuTriggerFor]="action_menu" class="menuButton" mat-icon-button><mat-icon>more_vert</mat-icon></button> @if (!file_obj || !file_obj.auto) {
<button [disabled]="loading" [matMenuTriggerFor]="action_menu" class="menuButton" mat-icon-button><mat-icon>more_vert</mat-icon></button>
}
<mat-menu #context_menu> <mat-menu #context_menu>
<ng-container *ngIf="!loading"> @if (!loading) {
<button (click)="navigateToFile($event)" mat-menu-item><mat-icon>open_in_browser</mat-icon><ng-container i18n="Open file button">Open file</ng-container></button> <button (click)="navigateToFile($event)" mat-menu-item><mat-icon>open_in_browser</mat-icon><ng-container i18n="Open file button">Open file</ng-container></button>
<button (click)="navigateToFile({ctrlKey: true})" mat-menu-item><mat-icon>open_in_new</mat-icon><ng-container i18n="Open file in new tab">Open file in new tab</ng-container></button> <button (click)="navigateToFile({ctrlKey: true})" mat-menu-item><mat-icon>open_in_new</mat-icon><ng-container i18n="Open file in new tab">Open file in new tab</ng-container></button>
</ng-container> }
</mat-menu> </mat-menu>
<mat-menu #action_menu="matMenu"> <mat-menu #action_menu="matMenu">
<ng-container *ngIf="!is_playlist && !loading"> @if (!is_playlist && !loading) {
<button (click)="emitToggleFavorite()" mat-menu-item> <button (click)="emitToggleFavorite()" mat-menu-item>
<mat-icon>{{file_obj.favorite ? 'favorite_filled' : 'favorite_outline'}}</mat-icon> <mat-icon>{{file_obj.favorite ? 'favorite_filled' : 'favorite_outline'}}</mat-icon>
<ng-container *ngIf="!file_obj.favorite" i18n="Favorite button">Favorite</ng-container> @if (!file_obj.favorite) {
<ng-container *ngIf="file_obj.favorite" i18n="Unfavorite button">Unfavorite</ng-container> <ng-container i18n="Favorite button">Favorite</ng-container>
}
@else {
<ng-container i18n="Unfavorite button">Unfavorite</ng-container>
}
</button> </button>
<button (click)="openFileInfoDialog()" mat-menu-item><mat-icon>info</mat-icon><ng-container i18n="Video info button">Info</ng-container></button> <button (click)="openFileInfoDialog()" mat-menu-item><mat-icon>info</mat-icon><ng-container i18n="Video info button">Info</ng-container></button>
<button (click)="navigateToSubscription()" mat-menu-item *ngIf="file_obj.sub_id"><mat-icon>{{file_obj.isAudio ? 'library_music' : 'video_library'}}</mat-icon>&nbsp;<ng-container i18n="Go to subscription menu item">Go to subscription</ng-container></button> @if (file_obj.sub_id) {
<button (click)="navigateToSubscription()" mat-menu-item><mat-icon>{{file_obj.isAudio ? 'library_music' : 'video_library'}}</mat-icon>&nbsp;<ng-container i18n="Go to subscription menu item">Go to subscription</ng-container></button>
}
<button [disabled]="!availablePlaylists || availablePlaylists.length === 0" [matMenuTriggerFor]="addtoplaylist" mat-menu-item><mat-icon>playlist_add</mat-icon>&nbsp;<ng-container i18n="Add to playlist menu item">Add to playlist</ng-container></button> <button [disabled]="!availablePlaylists || availablePlaylists.length === 0" [matMenuTriggerFor]="addtoplaylist" mat-menu-item><mat-icon>playlist_add</mat-icon>&nbsp;<ng-container i18n="Add to playlist menu item">Add to playlist</ng-container></button>
<mat-menu #addtoplaylist="matMenu"> <mat-menu #addtoplaylist="matMenu">
<ng-container *ngFor="let playlist of availablePlaylists"> @for (playlist of availablePlaylists; track playlist) {
<button *ngIf="(playlist.type === 'audio') === file_obj.isAudio" [disabled]="playlist.uids?.includes(file_obj.uid)" (click)="emitAddFileToPlaylist(playlist.id)" mat-menu-item>{{playlist.name}}</button> @if ((playlist.type === 'audio') === file_obj.isAudio) {
</ng-container> <button [disabled]="playlist.uids?.includes(file_obj.uid)" (click)="emitAddFileToPlaylist(playlist.id)" mat-menu-item>{{playlist.name}}</button>
}
}
</mat-menu> </mat-menu>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<button *ngIf="file_obj.sub_id" (click)="emitDeleteFile()" mat-menu-item><mat-icon>restore</mat-icon><ng-container i18n="Delete and redownload subscription video button">Delete and redownload</ng-container></button> @if (file_obj.sub_id) {
<button *ngIf="!file_obj.sub_id" (click)="emitDeleteFile()" mat-menu-item><mat-icon>delete</mat-icon><ng-container i18n="Delete video button">Delete</ng-container></button> <button (click)="emitDeleteFile()" mat-menu-item><mat-icon>restore</mat-icon><ng-container i18n="Delete and redownload subscription video button">Delete and redownload</ng-container></button>
<button *ngIf="file_obj.sub_id || use_youtubedl_archive" (click)="emitDeleteFile(true)" mat-menu-item><mat-icon>delete_forever</mat-icon><ng-container i18n="Delete and don't download again">Delete and don't download again</ng-container></button> } @else {
</ng-container> <button (click)="emitDeleteFile()" mat-menu-item><mat-icon>delete</mat-icon><ng-container i18n="Delete video button">Delete</ng-container></button>
<ng-container *ngIf="is_playlist && !loading"> }
@if (file_obj.sub_id || use_youtubedl_archive) {
<button (click)="emitDeleteFile(true)" mat-menu-item><mat-icon>delete_forever</mat-icon><ng-container i18n="Delete and don't download again">Delete and don't download again</ng-container></button>
}
}
@if (is_playlist && !loading) {
<button (click)="emitEditPlaylist()" mat-menu-item><mat-icon>edit</mat-icon><ng-container i18n="Playlist edit button">Edit</ng-container></button> <button (click)="emitEditPlaylist()" mat-menu-item><mat-icon>edit</mat-icon><ng-container i18n="Playlist edit button">Edit</ng-container></button>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<button (click)="emitDeleteFile()" mat-menu-item><mat-icon>delete_forever</mat-icon><ng-container i18n="Delete playlist">Delete</ng-container></button> <button (click)="emitDeleteFile()" mat-menu-item><mat-icon>delete_forever</mat-icon><ng-container i18n="Delete playlist">Delete</ng-container></button>
</ng-container> } @else if (loading) {
<ng-container *ngIf="loading">
<button mat-menu-item>Placeholder</button> <button mat-menu-item>Placeholder</button>
</ng-container> }
</mat-menu> </mat-menu>
<mat-card [matTooltip]="null" (click)="navigateToFile($event)" matRipple class="file-mat-card" [ngClass]="{'small-mat-card': card_size === 'small', 'file-mat-card': card_size === 'medium', 'large-mat-card': card_size === 'large', 'mat-elevation-z4': !elevated, 'mat-elevation-z8': elevated}"> <mat-card [matTooltip]="null" (click)="navigateToFile($event)" matRipple class="file-mat-card" [ngClass]="{'small-mat-card': card_size === 'small', 'file-mat-card': card_size === 'medium', 'large-mat-card': card_size === 'large', 'mat-elevation-z4': !elevated, 'mat-elevation-z8': elevated}">
<div style="padding:5px"> <div style="padding:5px">
<div *ngIf="!loading && file_obj.thumbnailURL" class="img-div"> @if (!loading && file_obj.thumbnailURL) {
<div class="img-div">
<div [ngClass]="{'image-small': card_size === 'small', 'image': card_size === 'medium', 'image-large': card_size === 'large'}" style="position: relative"> <div [ngClass]="{'image-small': card_size === 'small', 'image': card_size === 'medium', 'image-large': card_size === 'large'}" style="position: relative">
<img *ngIf="!hide_image || is_playlist || (file_obj.type === 'audio' || file_obj.isAudio)" [ngClass]="{'image-small': card_size === 'small', 'image': card_size === 'medium', 'image-large': card_size === 'large'}" [src]="file_obj.thumbnailPath ? thumbnailBlobURL : file_obj.thumbnailURL" alt="Thumbnail"> @if (!hide_image || is_playlist || (file_obj.type === 'audio' || file_obj.isAudio)) {
<video *ngIf="elevated && !is_playlist && !(file_obj.type === 'audio' || file_obj.isAudio)" autoplay loop muted [muted]="true" [ngClass]="{'video-small': card_size === 'small', 'video': card_size === 'medium', 'video-large': card_size === 'large'}" [src]="streamURL"> <img [ngClass]="{'image-small': card_size === 'small', 'image': card_size === 'medium', 'image-large': card_size === 'large'}" [src]="file_obj.thumbnailPath ? thumbnailBlobURL : file_obj.thumbnailURL" alt="Thumbnail">
}
@if (elevated && !is_playlist && !(file_obj.type === 'audio' || file_obj.isAudio)) {
<video autoplay loop muted [muted]="true" [ngClass]="{'video-small': card_size === 'small', 'video': card_size === 'medium', 'video-large': card_size === 'large'}" [src]="streamURL">
</video> </video>
}
<div class="duration-time"> <div class="duration-time">
{{file_length}} {{file_length}}
</div> </div>
</div> </div>
</div> </div>
}
<div *ngIf="loading" class="img-div"> @if (loading) {
<div class="img-div">
<content-loader [backgroundColor]="theme.ghost_primary" [foregroundColor]="theme.ghost_secondary" viewBox="0 0 100 55"><svg:rect x="0" y="0" rx="0" ry="0" width="100" height="55" /></content-loader> <content-loader [backgroundColor]="theme.ghost_primary" [foregroundColor]="theme.ghost_secondary" viewBox="0 0 100 55"><svg:rect x="0" y="0" rx="0" ry="0" width="100" height="55" /></content-loader>
</div> </div>
} @else {
<span *ngIf="!loading" [ngClass]="{'max-two-lines': card_size !== 'small', 'max-one-line': card_size === 'small' }">{{card_size === 'large' && file_obj.uploader ? file_obj.uploader + ' - ' : ''}}<strong>{{!is_playlist ? file_obj.title : file_obj.name}}</strong></span> <span [ngClass]="{'max-two-lines': card_size !== 'small', 'max-one-line': card_size === 'small' }">{{card_size === 'large' && file_obj.uploader ? file_obj.uploader + ' - ' : ''}}<strong>{{!is_playlist ? file_obj.title : file_obj.name}}</strong></span>
<span *ngIf="loading" class="title-loading"><content-loader [backgroundColor]="theme.ghost_primary" [foregroundColor]="theme.ghost_secondary" viewBox="0 0 250 30"><svg:rect x="0" y="0" rx="3" ry="3" width="250" height="30" /></content-loader></span> }
@if (loading) {
<span class="title-loading"><content-loader [backgroundColor]="theme.ghost_primary" [foregroundColor]="theme.ghost_secondary" viewBox="0 0 250 30"><svg:rect x="0" y="0" rx="3" ry="3" width="250" height="30" /></content-loader></span>
}
</div> </div>
</mat-card> </mat-card>
</div> </div>

View File

@@ -1,9 +1,12 @@
<h4 mat-dialog-title *ngIf="create_mode" ><ng-container i18n="Create a playlist dialog title">Create a playlist</ng-container></h4> @if (create_mode) {
<h4 mat-dialog-title *ngIf="!create_mode"><ng-container i18n="Modify playlist dialog title">Modify playlist</ng-container></h4> <h4 mat-dialog-title ><ng-container i18n="Create a playlist dialog title">Create a playlist</ng-container></h4>
} @else {
<h4 mat-dialog-title><ng-container i18n="Modify playlist dialog title">Modify playlist</ng-container></h4>
}
<mat-dialog-content style="max-height: 85vh;"> <mat-dialog-content style="max-height: 85vh;">
<form> <form>
<div *ngIf="create_mode || playlist"> @if (create_mode || playlist) {
<div>
<div> <div>
<mat-form-field color="accent"> <mat-form-field color="accent">
<mat-label i18n="Playlist name">Name</mat-label> <mat-label i18n="Playlist name">Name</mat-label>
@@ -12,17 +15,21 @@
</div> </div>
<app-recent-videos [selectMode]="true" [defaultSelected]="preselected_files" [customHeader]="'Select files'" (fileSelectionEmitter)="fileSelectionChanged($event)" [selectedIndex]="create_mode ? 1 : 0"></app-recent-videos> <app-recent-videos [selectMode]="true" [defaultSelected]="preselected_files" [customHeader]="'Select files'" (fileSelectionEmitter)="fileSelectionChanged($event)" [selectedIndex]="create_mode ? 1 : 0"></app-recent-videos>
</div> </div>
}
</form> </form>
</mat-dialog-content> </mat-dialog-content>
<div class="spacer"></div> <div class="spacer"></div>
<mat-dialog-actions> <mat-dialog-actions>
<button *ngIf="create_mode" (click)="createPlaylist()" [disabled]="!name || !filesSelect.value || filesSelect.value.length === 0" color="primary" style="float: right" mat-button> @if (create_mode) {
<button (click)="createPlaylist()" [disabled]="!name || !filesSelect.value || filesSelect.value.length === 0" color="primary" style="float: right" mat-button>
<ng-container i18n="Create button">Create</ng-container> <ng-container i18n="Create button">Create</ng-container>
</button> </button>
<button *ngIf="!create_mode" (click)="updatePlaylist()" [disabled]="!name || !playlistChanged()" color="primary" style="float: right" mat-button> } @else {
<button (click)="updatePlaylist()" [disabled]="!name || !playlistChanged()" color="primary" style="float: right" mat-button>
<ng-container i18n="Save button">Save</ng-container> <ng-container i18n="Save button">Save</ng-container>
</button> </button>
<div *ngIf="create_in_progress" style="margin-left: 10px"><mat-spinner [diameter]="25"></mat-spinner></div> }
@if (create_in_progress) {
<div style="margin-left: 10px"><mat-spinner [diameter]="25"></mat-spinner></div>
}
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -17,17 +17,25 @@
<mat-divider></mat-divider> <mat-divider></mat-divider>
<h5 style="margin-top: 10px;">Installation details:</h5> <h5 style="margin-top: 10px;">Installation details:</h5>
<p> <p>
<ng-container i18n="Version label">Installed version:</ng-container>&nbsp;{{current_version_tag}} - <span style="display: inline-block" *ngIf="checking_for_updates"><mat-spinner class="version-spinner" [diameter]="22"></mat-spinner>&nbsp;<ng-container i18n="Checking for updates text">Checking for updates...</ng-container></span> <ng-container i18n="Version label">Installed version:</ng-container>&nbsp;{{current_version_tag}} -
<mat-icon *ngIf="!checking_for_updates" class="version-checked-icon">done</mat-icon>&nbsp;&nbsp;<ng-container *ngIf="!checking_for_updates && latestGithubRelease['tag_name'] !== current_version_tag"><a [href]="latestUpdateLink" target="_blank"><ng-container i18n="View latest update">Update available</ng-container> - {{latestGithubRelease['tag_name']}}</a>. <ng-container i18n="Update through settings menu hint">You can update from the settings menu.</ng-container></ng-container> @if (checking_for_updates) {
<span *ngIf="!checking_for_updates && latestGithubRelease['tag_name'] === current_version_tag">You are up to date.</span> <span style="display: inline-block"><mat-spinner class="version-spinner" [diameter]="22"></mat-spinner>&nbsp;<ng-container i18n="Checking for updates text">Checking for updates...</ng-container></span>
} @else {
<mat-icon class="version-checked-icon">done</mat-icon>&nbsp;&nbsp;
@if (latestGithubRelease['tag_name'] !== current_version_tag) {
<a [href]="latestUpdateLink" target="_blank"><ng-container i18n="View latest update">Update available</ng-container> - {{latestGithubRelease['tag_name']}}</a>. <ng-container i18n="Update through settings menu hint">You can update from the settings menu.</ng-container>
} @else {
<span>You are up to date.</span>
}
}
</p> </p>
<p> <p>
<ng-container i18n="Installation type">Installation type:</ng-container>&nbsp;{{postsService.version_info.type}} <ng-container i18n="Installation type">Installation type:</ng-container>&nbsp;{{postsService.version_info.type}}
<br> <br>
<ng-container *ngIf="postsService.version_info.type === 'docker'"> @if (postsService.version_info.type === 'docker') {
<ng-container i18n="Docker tag">Docker tag:</ng-container>&nbsp;{{postsService.version_info.tag}} <ng-container i18n="Docker tag">Docker tag:</ng-container>&nbsp;{{postsService.version_info.tag}}
<br> <br>
</ng-container> }
<ng-container i18n="Commit hash">Commit hash:</ng-container>&nbsp;{{postsService.version_info.commit}} <ng-container i18n="Commit hash">Commit hash:</ng-container>&nbsp;{{postsService.version_info.commit}}
<br> <br>
<ng-container i18n="Build date">Build date:</ng-container>&nbsp;{{postsService.version_info.date}} <ng-container i18n="Build date">Build date:</ng-container>&nbsp;{{postsService.version_info.date}}
@@ -37,7 +45,7 @@
</p> </p>
</div> </div>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button style="margin-bottom: 5px;" mat-stroked-button mat-dialog-close><ng-container i18n="Close">Close</ng-container></button> <button style="margin-bottom: 5px;" mat-stroked-button mat-dialog-close><ng-container i18n="Close">Close</ng-container></button>
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -1,5 +1,4 @@
<h4 i18n="Modify args title" mat-dialog-title>Modify youtube-dl args</h4> <h4 i18n="Modify args title" mat-dialog-title>Modify youtube-dl args</h4>
<mat-dialog-content> <mat-dialog-content>
<div class="container"> <div class="container">
<div class="row"> <div class="row">
@@ -10,23 +9,28 @@
<mat-chip-grid class="example-chip" #chipList aria-label="Args array" cdkDropList cdkDropListDisabled <mat-chip-grid class="example-chip" #chipList aria-label="Args array" cdkDropList cdkDropListDisabled
cdkDropListOrientation="horizontal" cdkDropListOrientation="horizontal"
(cdkDropListDropped)="drop($event)"> (cdkDropListDropped)="drop($event)">
<mat-chip-row [matTooltip]="argsByKey[arg] ? argsByKey[arg]['description'] : null" *ngFor="let arg of args_array; let i = index;" [removable]="removable" (removed)="remove(i)" cdkDrag> @for (arg of args_array; track arg; let i = $index) {
<mat-chip-row [matTooltip]="argsByKey[arg] ? argsByKey[arg]['description'] : null" [removable]="removable" (removed)="remove(i)" cdkDrag>
{{arg}} {{arg}}
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon> @if (removable) {
<mat-icon matChipRemove>cancel</mat-icon>
}
</mat-chip-row> </mat-chip-row>
}
</mat-chip-grid> </mat-chip-grid>
<mat-form-field style="width: 100%" color="accent"> <mat-form-field style="width: 100%" color="accent">
<input #chipper style="width: 100%;" [formControl]="chipCtrl" matInput [matAutocomplete]="autochip" [matChipInputFor]="chipList" <input #chipper style="width: 100%;" [formControl]="chipCtrl" matInput [matAutocomplete]="autochip" [matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="addOnBlur" [matChipInputAddOnBlur]="addOnBlur"
(matChipInputTokenEnd)="add($event)"> (matChipInputTokenEnd)="add($event)">
</mat-form-field> </mat-form-field>
<mat-autocomplete #autochip="matAutocomplete"> <mat-autocomplete #autochip="matAutocomplete">
<mat-option *ngFor="let arg of filteredChipOptions | async" [value]="arg.key"> @for (arg of filteredChipOptions | async; track arg) {
<mat-option [value]="arg.key">
<span [innerHTML]="arg.key | highlight : chipCtrl.value"></span> <span [innerHTML]="arg.key | highlight : chipCtrl.value"></span>
<button class="info-autocomplete-icon" [matTooltip]="arg.description" mat-icon-button><mat-icon>info</mat-icon></button> <button class="info-autocomplete-icon" [matTooltip]="arg.description" mat-icon-button><mat-icon>info</mat-icon></button>
</mat-option> </mat-option>
}
</mat-autocomplete> </mat-autocomplete>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
@@ -42,37 +46,38 @@
<input matInput [matAutocomplete]="auto" [formControl]="stateCtrl"> <input matInput [matAutocomplete]="auto" [formControl]="stateCtrl">
</mat-form-field> </mat-form-field>
<mat-autocomplete #auto="matAutocomplete"> <mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let arg of filteredOptions | async" [value]="arg.key"> @for (arg of filteredOptions | async; track arg) {
<mat-option [value]="arg.key">
<span [innerHTML]="arg.key | highlight : stateCtrl.value"></span> <span [innerHTML]="arg.key | highlight : stateCtrl.value"></span>
<button class="info-autocomplete-icon" [matTooltip]="arg.description" mat-icon-button><mat-icon>info</mat-icon></button> <button class="info-autocomplete-icon" [matTooltip]="arg.description" mat-icon-button><mat-icon>info</mat-icon></button>
</mat-option> </mat-option>
}
</mat-autocomplete> </mat-autocomplete>
<div> <div>
<mat-menu #argsByCategoryMenu="matMenu"> <mat-menu #argsByCategoryMenu="matMenu">
<ng-container *ngFor="let argsInCategory of argsByCategory | keyvalue"> @for (argsInCategory of argsByCategory | keyvalue; track argsInCategory) {
<button mat-menu-item [matMenuTriggerFor]="subMenu">{{argsInfo[argsInCategory.key].label}}</button> <button mat-menu-item [matMenuTriggerFor]="subMenu">{{argsInfo[argsInCategory.key].label}}</button>
<mat-menu #subMenu="matMenu"> <mat-menu #subMenu="matMenu">
<button mat-menu-item *ngFor="let arg of argsInCategory.value" (click)="setFirstArg(arg.key)"><div style="display: inline-block;">{{arg.key}}</div>&nbsp;&nbsp;<div class="info-menu-icon"><mat-icon [matTooltip]="arg.description">info</mat-icon></div></button> @for (arg of argsInCategory.value; track arg) {
<button mat-menu-item (click)="setFirstArg(arg.key)"><div style="display: inline-block;">{{arg.key}}</div>&nbsp;&nbsp;<div class="info-menu-icon"><mat-icon [matTooltip]="arg.description">info</mat-icon></div></button>
}
</mat-menu> </mat-menu>
</ng-container> }
</mat-menu> </mat-menu>
<button style="margin-bottom: 15px" mat-stroked-button [matMenuTriggerFor]="argsByCategoryMenu"><ng-container i18n="Search args by category button">Search by category</ng-container></button> <button style="margin-bottom: 15px" mat-stroked-button [matMenuTriggerFor]="argsByCategoryMenu"><ng-container i18n="Search args by category button">Search by category</ng-container></button>
</div> </div>
</div> </div>
<div> <div>
<mat-checkbox color="accent" [ngModelOptions]="{standalone: true}" [(ngModel)]="secondArgEnabled"><ng-container i18n="Use arg value checkbox">Use arg value</ng-container></mat-checkbox> <mat-checkbox color="accent" [ngModelOptions]="{standalone: true}" [(ngModel)]="secondArgEnabled"><ng-container i18n="Use arg value checkbox">Use arg value</ng-container></mat-checkbox>
</div> </div>
<div *ngIf="secondArgEnabled"> @if (secondArgEnabled) {
<div>
<mat-form-field style="width: 75%" color="accent"> <mat-form-field style="width: 75%" color="accent">
<mat-label i18n="Arg value">Arg value</mat-label> <mat-label i18n="Arg value">Arg value</mat-label>
<input [ngModelOptions]="{standalone: true}" matInput [disabled]="!secondArgEnabled" [(ngModel)]="secondArg"> <input [ngModelOptions]="{standalone: true}" matInput [disabled]="!secondArgEnabled" [(ngModel)]="secondArg">
</mat-form-field> </mat-form-field>
</div> </div>
}
</form> </form>
<div> <div>
<button (click)="addArg()" [disabled]="!canAddArg()" mat-stroked-button color="accent"><ng-container i18n="Search args by category button">Add arg</ng-container></button> <button (click)="addArg()" [disabled]="!canAddArg()" mat-stroked-button color="accent"><ng-container i18n="Search args by category button">Add arg</ng-container></button>
@@ -82,10 +87,7 @@
</div> </div>
</div> </div>
</div> </div>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button [mat-dialog-close]="null"><ng-container i18n="Arg modifier cancel button">Cancel</ng-container></button> <button mat-button [mat-dialog-close]="null"><ng-container i18n="Arg modifier cancel button">Cancel</ng-container></button>
<button mat-button color="accent" [mat-dialog-close]="modified_args"><ng-container i18n="Arg modifier modify button">Modify</ng-container></button> <button mat-button color="accent" [mat-dialog-close]="modified_args"><ng-container i18n="Arg modifier modify button">Modify</ng-container></button>

View File

@@ -2,24 +2,27 @@
<mat-dialog-content> <mat-dialog-content>
<div style="margin-bottom: 10px;"> <div style="margin-bottom: 10px;">
<!-- We can support text dialogs or dialogs where users must select items from a list --> <!-- We can support text dialogs or dialogs where users must select items from a list -->
<ng-container *ngIf="dialogType === 'text'"> @if (dialogType === 'text') {
{{dialogText}} {{dialogText}}
</ng-container> } @else if (dialogType === 'selection_list') {
<ng-container *ngIf="dialogType === 'selection_list'">
<mat-selection-list [(ngModel)]="selected_items"> <mat-selection-list [(ngModel)]="selected_items">
<mat-list-option *ngFor="let item of list" [value]="item.key"> @for (item of list; track item) {
<mat-list-option [value]="item.key">
{{item.title}} {{item.title}}
</mat-list-option> </mat-list-option>
}
</mat-selection-list> </mat-selection-list>
</ng-container> }
</div> </div>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. --> <!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
<button [disabled]="dialogType === 'selection_list' && selected_items.length === 0" [color]="warnSubmitColor ? 'warn' : 'primary'" mat-flat-button type="submit" (click)="confirmClicked()">{{submitText}}</button> <button [disabled]="dialogType === 'selection_list' && selected_items.length === 0" [color]="warnSubmitColor ? 'warn' : 'primary'" mat-flat-button type="submit" (click)="confirmClicked()">{{submitText}}</button>
<div class="mat-spinner" *ngIf="submitClicked"> @if (submitClicked) {
<div class="mat-spinner">
<mat-spinner [diameter]="25"></mat-spinner> <mat-spinner [diameter]="25"></mat-spinner>
</div> </div>
}
<span class="spacer"></span> <span class="spacer"></span>
<button style="float: right;" mat-stroked-button mat-dialog-close> <button style="float: right;" mat-stroked-button mat-dialog-close>
{{cancelText}} {{cancelText}}

View File

@@ -1,5 +1,4 @@
<h4 mat-dialog-title i18n="Cookies uploader dialog title">Upload new cookies</h4> <h4 mat-dialog-title i18n="Cookies uploader dialog title">Upload new cookies</h4>
<mat-dialog-content> <mat-dialog-content>
<div> <div>
<div class="center"> <div class="center">
@@ -22,19 +21,24 @@
<div style="margin-top: 10px;"> <div style="margin-top: 10px;">
<table class="table"> <table class="table">
<tbody class="upload-name-style"> <tbody class="upload-name-style">
<tr *ngFor="let item of files; let i=index"> @for (item of files; track item; let i = $index) {
<tr>
<td style="vertical-align: middle;"> <td style="vertical-align: middle;">
<strong>{{ item.relativePath }}</strong> <strong>{{ item.relativePath }}</strong>
</td> </td>
<td> <td>
<button [disabled]="uploading || uploaded" (click)="uploadFile()" style="float: right" matTooltip="Upload" i18n-matTooltip="Upload" mat-mini-fab><mat-icon>publish</mat-icon><mat-spinner *ngIf="uploading" class="spinner" [diameter]="38"></mat-spinner></button> <button [disabled]="uploading || uploaded" (click)="uploadFile()" style="float: right" matTooltip="Upload" i18n-matTooltip="Upload" mat-mini-fab><mat-icon>publish</mat-icon>
@if (uploading) {
<mat-spinner class="spinner" [diameter]="38"></mat-spinner>
}
</button>
</td> </td>
</tr> </tr>
}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions><button style="margin-bottom: 5px;" mat-dialog-close mat-stroked-button><ng-container i18n="Close">Close</ng-container></button></mat-dialog-actions> <mat-dialog-actions><button style="margin-bottom: 5px;" mat-dialog-close mat-stroked-button><ng-container i18n="Close">Close</ng-container></button></mat-dialog-actions>

View File

@@ -1,48 +1,47 @@
<h4 mat-dialog-title><ng-container i18n="Editing category dialog title">Editing category</ng-container>&nbsp;{{category['name']}}</h4> <h4 mat-dialog-title><ng-container i18n="Editing category dialog title">Editing category</ng-container>&nbsp;{{category['name']}}</h4>
<mat-dialog-content style="max-height: 50vh"> <mat-dialog-content style="max-height: 50vh">
<mat-form-field style="width: 250px; margin-bottom: 5px;"> <mat-form-field style="width: 250px; margin-bottom: 5px;">
<mat-label i18n="Category name">Name</mat-label> <mat-label i18n="Category name">Name</mat-label>
<input matInput [(ngModel)]="category['name']" required> <input matInput [(ngModel)]="category['name']" required>
</mat-form-field> </mat-form-field>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<h6 style="margin-top: 20px;" i18n="Rules">Rules</h6> <h6 style="margin-top: 20px;" i18n="Rules">Rules</h6>
<mat-list> <mat-list>
<mat-list-item *ngFor="let rule of category['rules']; let i = index"> @for (rule of category['rules']; track rule) {
<mat-form-field [style.visibility]="i === 0 ? 'hidden' : null" class="operator-select"> <mat-list-item>
<mat-select [disabled]="i === 0" [(ngModel)]="rule['preceding_operator']"> <mat-form-field [style.visibility]="$index === 0 ? 'hidden' : null" class="operator-select">
<mat-select [disabled]="$index === 0" [(ngModel)]="rule['preceding_operator']">
<mat-option value="or">OR</mat-option> <mat-option value="or">OR</mat-option>
<mat-option value="and">AND</mat-option> <mat-option value="and">AND</mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field class="property-select"> <mat-form-field class="property-select">
<mat-select [(ngModel)]="rule['property']"> <mat-select [(ngModel)]="rule['property']">
<mat-option *ngFor="let propertyOption of propertyOptions" [value]="propertyOption.value">{{propertyOption.label}}</mat-option> @for (propertyOption of propertyOptions; track propertyOption) {
<mat-option [value]="propertyOption.value">{{propertyOption.label}}</mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field class="comparator-select"> <mat-form-field class="comparator-select">
<mat-select [(ngModel)]="rule['comparator']"> <mat-select [(ngModel)]="rule['comparator']">
<mat-option *ngFor="let comparatorOption of comparatorOptions" [value]="comparatorOption.value">{{comparatorOption.label}}</mat-option> @for (comparatorOption of comparatorOptions; track comparatorOption) {
<mat-option [value]="comparatorOption.value">{{comparatorOption.label}}</mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field class="value-input"> <mat-form-field class="value-input">
<input matInput [(ngModel)]="rule['value']"> <input matInput [(ngModel)]="rule['value']">
</mat-form-field> </mat-form-field>
<span class="rule-buttons"> <span class="rule-buttons">
<button [disabled]="i === category['rules'].length-1" (click)="swapRules(i, i+1)" mat-icon-button><mat-icon>arrow_downward</mat-icon></button> <button [disabled]="$index === category['rules'].length-1" (click)="swapRules($index, $index+1)" mat-icon-button><mat-icon>arrow_downward</mat-icon></button>
<button [disabled]="i === 0" (click)="swapRules(i, i-1)" mat-icon-button><mat-icon>arrow_upward</mat-icon></button> <button [disabled]="$index === 0" (click)="swapRules($index, $index-1)" mat-icon-button><mat-icon>arrow_upward</mat-icon></button>
<button (click)="removeRule(i)" mat-icon-button><mat-icon>cancel</mat-icon></button> <button (click)="removeRule($index)" mat-icon-button><mat-icon>cancel</mat-icon></button>
</span> </span>
</mat-list-item> </mat-list-item>
}
</mat-list> </mat-list>
<button style="margin-bottom: 8px;" mat-icon-button (click)="addNewRule()" matTooltip="Add new rule" i18n-matTooltip="Add new rule tooltip"><mat-icon>add</mat-icon></button> <button style="margin-bottom: 8px;" mat-icon-button (click)="addNewRule()" matTooltip="Add new rule" i18n-matTooltip="Add new rule tooltip"><mat-icon>add</mat-icon></button>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<mat-form-field style="width: 250px; margin-top: 10px;"> <mat-form-field style="width: 250px; margin-top: 10px;">
<mat-label i18n="Custom file output">Custom file output</mat-label> <mat-label i18n="Custom file output">Custom file output</mat-label>
<input matInput [(ngModel)]="category['custom_output']"> <input matInput [(ngModel)]="category['custom_output']">
@@ -53,12 +52,12 @@
</mat-hint> </mat-hint>
</mat-form-field> </mat-form-field>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close><ng-container i18n="Cancel">Cancel</ng-container></button> <button mat-button mat-dialog-close><ng-container i18n="Cancel">Cancel</ng-container></button>
<button mat-button [disabled]="categoryChanged()" type="submit" (click)="saveClicked()"><ng-container i18n="Save button">Save</ng-container></button> <button mat-button [disabled]="categoryChanged()" type="submit" (click)="saveClicked()"><ng-container i18n="Save button">Save</ng-container></button>
<div class="mat-spinner" *ngIf="updating"> @if (updating) {
<div class="mat-spinner">
<mat-spinner [diameter]="25"></mat-spinner> <mat-spinner [diameter]="25"></mat-spinner>
</div> </div>
}
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -1,5 +1,8 @@
<h4 mat-dialog-title><ng-container i18n="Edit subscription dialog title prefix">Editing</ng-container>&nbsp;{{sub.name}}&nbsp;<ng-container *ngIf="sub.paused" i18n="Paused suffix">(Paused)</ng-container></h4> <h4 mat-dialog-title><ng-container i18n="Edit subscription dialog title prefix">Editing</ng-container>&nbsp;{{sub.name}}
@if (sub.paused) {
&nbsp;<ng-container i18n="Paused suffix">(Paused)</ng-container>
}
</h4>
<mat-dialog-content> <mat-dialog-content>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
@@ -9,19 +12,23 @@
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<mat-checkbox (change)="downloadAllToggled()" [(ngModel)]="download_all"><ng-container i18n="Download all uploads subscription setting">Download all uploads</ng-container></mat-checkbox> <mat-checkbox (change)="downloadAllToggled()" [(ngModel)]="download_all"><ng-container i18n="Download all uploads subscription setting">Download all uploads</ng-container></mat-checkbox>
</div> </div>
<div class="col-12" *ngIf="editor_initialized"> @if (editor_initialized) {
<div class="col-12">
<ng-container i18n="Download time range prefix">Download videos uploaded in the last</ng-container> <ng-container i18n="Download time range prefix">Download videos uploaded in the last</ng-container>
<mat-form-field color="accent" class="amount-select"> <mat-form-field color="accent" class="amount-select">
<input type="number" matInput [(ngModel)]="timerange_amount" (ngModelChange)="timerangeChanged($event, false)" [disabled]="download_all"> <input type="number" matInput [(ngModel)]="timerange_amount" (ngModelChange)="timerangeChanged($event, false)" [disabled]="download_all">
</mat-form-field> </mat-form-field>
<mat-form-field class="unit-select"> <mat-form-field class="unit-select">
<mat-select color="accent" [(ngModel)]="timerange_unit" (ngModelChange)="timerangeChanged($event, true)" [disabled]="download_all"> <mat-select color="accent" [(ngModel)]="timerange_unit" (ngModelChange)="timerangeChanged($event, true)" [disabled]="download_all">
<mat-option *ngFor="let time_unit of time_units" [value]="time_unit + (timerange_amount === 1 ? '' : 's')"> @for (time_unit of time_units; track time_unit) {
<mat-option [value]="time_unit + (timerange_amount === 1 ? '' : 's')">
{{time_unit + (timerange_amount === 1 ? '' : 's')}} {{time_unit + (timerange_amount === 1 ? '' : 's')}}
</mat-option> </mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
}
<div class="col-12"> <div class="col-12">
<div> <div>
<mat-checkbox [disabled]="true" [(ngModel)]="audioOnlyMode"><ng-container i18n="Streaming-only mode">Audio-only mode</ng-container></mat-checkbox> <mat-checkbox [disabled]="true" [(ngModel)]="audioOnlyMode"><ng-container i18n="Streaming-only mode">Audio-only mode</ng-container></mat-checkbox>
@@ -31,7 +38,9 @@
<mat-form-field> <mat-form-field>
<mat-label i18n="Max quality">Max quality</mat-label> <mat-label i18n="Max quality">Max quality</mat-label>
<mat-select [disabled]="new_sub.type === 'audio'" [(ngModel)]="new_sub.maxQuality"> <mat-select [disabled]="new_sub.type === 'audio'" [(ngModel)]="new_sub.maxQuality">
<mat-option *ngFor="let available_quality of available_qualities" [value]="available_quality['value']">{{available_quality['label']}}</mat-option> @for (available_quality of available_qualities; track available_quality) {
<mat-option [value]="available_quality['value']">{{available_quality['label']}}</mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
@@ -59,12 +68,13 @@
</div> </div>
</div> </div>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close><ng-container i18n="Subscribe cancel button">Cancel</ng-container></button> <button mat-button mat-dialog-close><ng-container i18n="Subscribe cancel button">Cancel</ng-container></button>
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. --> <!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
<button mat-button [disabled]="updating || !subChanged()" type="submit" (click)="saveClicked()"><ng-container i18n="Save button">Save</ng-container></button> <button mat-button [disabled]="updating || !subChanged()" type="submit" (click)="saveClicked()"><ng-container i18n="Save button">Save</ng-container></button>
<div class="mat-spinner" *ngIf="updating"> @if (updating) {
<div class="mat-spinner">
<mat-spinner [diameter]="25"></mat-spinner> <mat-spinner [diameter]="25"></mat-spinner>
</div> </div>
}
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -1,5 +1,4 @@
<h4 mat-dialog-title><ng-container i18n="Generate RSS URL">Generate RSS URL</ng-container></h4> <h4 mat-dialog-title><ng-container i18n="Generate RSS URL">Generate RSS URL</ng-container></h4>
<mat-dialog-content> <mat-dialog-content>
<div class="container-fluid"> <div class="container-fluid">
<div class="row justify-content-center"> <div class="row justify-content-center">
@@ -25,7 +24,9 @@
<mat-label><ng-container i18n="User">User</ng-container></mat-label> <mat-label><ng-container i18n="User">User</ng-container></mat-label>
<mat-select color="accent" [(ngModel)]="userFilter" (selectionChange)="rebuildURL()" [disabled]="!usersList"> <mat-select color="accent" [(ngModel)]="userFilter" (selectionChange)="rebuildURL()" [disabled]="!usersList">
<mat-option [value]="''"><ng-container i18n="None">None</ng-container></mat-option> <mat-option [value]="''"><ng-container i18n="None">None</ng-container></mat-option>
<mat-option *ngFor="let user of usersList" [value]="user.uid"><ng-container>{{user.name}}</ng-container></mat-option> @for (user of usersList; track user) {
<mat-option [value]="user.uid"><ng-container>{{user.name}}</ng-container></mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
@@ -34,7 +35,9 @@
<mat-label><ng-container i18n="Subscription">Subscription</ng-container></mat-label> <mat-label><ng-container i18n="Subscription">Subscription</ng-container></mat-label>
<mat-select color="accent" [(ngModel)]="subscriptionFilter" (selectionChange)="rebuildURL()"> <mat-select color="accent" [(ngModel)]="subscriptionFilter" (selectionChange)="rebuildURL()">
<mat-option [value]="''"><ng-container i18n="None">None</ng-container></mat-option> <mat-option [value]="''"><ng-container i18n="None">None</ng-container></mat-option>
<mat-option *ngFor="let sub of postsService.subscriptions" [value]="sub.id"><ng-container>{{sub.name}}</ng-container></mat-option> @for (sub of postsService.subscriptions; track sub) {
<mat-option [value]="sub.id"><ng-container>{{sub.name}}</ng-container></mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
@@ -52,7 +55,6 @@
</div> </div>
</div> </div>
</div> </div>
<mat-form-field style="width: 100%;"> <mat-form-field style="width: 100%;">
<mat-label i18n="URL">URL</mat-label> <mat-label i18n="URL">URL</mat-label>
<input readonly [(ngModel)]="url" matInput> <input readonly [(ngModel)]="url" matInput>
@@ -61,7 +63,6 @@
</button> </button>
</mat-form-field> </mat-form-field>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close><ng-container i18n="Close">Close</ng-container></button> <button mat-button mat-dialog-close><ng-container i18n="Close">Close</ng-container></button>
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -1,8 +1,8 @@
<h4 mat-dialog-title><ng-container i18n="Restore DB from backup">Restore DB from backup</ng-container></h4> <h4 mat-dialog-title><ng-container i18n="Restore DB from backup">Restore DB from backup</ng-container></h4>
<mat-dialog-content> <mat-dialog-content>
<mat-selection-list [multiple]="false" [(ngModel)]="selected_backup"> <mat-selection-list [multiple]="false" [(ngModel)]="selected_backup">
<mat-list-option *ngFor="let db_backup of db_backups" [value]="db_backup.name" [matTooltip]="db_backup.name"> @for (db_backup of db_backups; track db_backup) {
<mat-list-option [value]="db_backup.name" [matTooltip]="db_backup.name">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-4"> <div class="col-4">
@@ -17,13 +17,15 @@
</div> </div>
</div> </div>
</mat-list-option> </mat-list-option>
}
</mat-selection-list> </mat-selection-list>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close><ng-container i18n="Restore DB cancel button">Cancel</ng-container></button> <button mat-button mat-dialog-close><ng-container i18n="Restore DB cancel button">Cancel</ng-container></button>
<button mat-button [disabled]="restoring" (click)="restoreClicked()" [disabled]="!selected_backup || selected_backup.length !== 1"><ng-container i18n="Restore button">Restore</ng-container></button> <button mat-button [disabled]="restoring" (click)="restoreClicked()" [disabled]="!selected_backup || selected_backup.length !== 1"><ng-container i18n="Restore button">Restore</ng-container></button>
<div class="mat-spinner" *ngIf="restoring"> @if (restoring) {
<div class="mat-spinner">
<mat-spinner [diameter]="25"></mat-spinner> <mat-spinner [diameter]="25"></mat-spinner>
</div> </div>
}
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -1,5 +1,4 @@
<h4 mat-dialog-title><ng-container i18n="Create admin account dialog title">Create admin account</ng-container></h4> <h4 mat-dialog-title><ng-container i18n="Create admin account dialog title">Create admin account</ng-container></h4>
<mat-dialog-content> <mat-dialog-content>
<div> <div>
<p i18n="No default admin detected explanation">No default admin account detected. This will create and set the password for an admin account with the user name as 'admin'.</p> <p i18n="No default admin detected explanation">No default admin account detected. This will create and set the password for an admin account with the user name as 'admin'.</p>
@@ -13,8 +12,11 @@
</div> </div>
</div> </div>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button [disabled]="input.length === 0" color="accent" style="margin-bottom: 12px;" (click)="create()" mat-raised-button><ng-container i18n="Create">Create</ng-container></button> <button [disabled]="input.length === 0" color="accent" style="margin-bottom: 12px;" (click)="create()" mat-raised-button><ng-container i18n="Create">Create</ng-container></button>
<div class="spinner-div"><mat-spinner [diameter]="25" *ngIf="creating"></mat-spinner></div> <div class="spinner-div">
@if (creating) {
<mat-spinner [diameter]="25"></mat-spinner>
}
</div>
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -1,8 +1,10 @@
<h4 mat-dialog-title> <h4 mat-dialog-title>
<ng-container *ngIf="is_playlist" i18n="Share playlist dialog title">Share playlist</ng-container> @if (is_playlist) {
<ng-container *ngIf="!is_playlist" i18n="Share video dialog title">Share file</ng-container> <ng-container i18n="Share playlist dialog title">Share playlist</ng-container>
} @else {
<ng-container i18n="Share video dialog title">Share file</ng-container>
}
</h4> </h4>
<mat-dialog-content> <mat-dialog-content>
<div> <div>
<div> <div>
@@ -25,7 +27,6 @@
</div> </div>
</div> </div>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close><ng-container i18n="Close button">Close</ng-container></button> <button mat-button mat-dialog-close><ng-container i18n="Close button">Close</ng-container></button>
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -1,5 +1,4 @@
<h4 mat-dialog-title i18n="Subscribe dialog title">Subscribe to playlist or channel</h4> <h4 mat-dialog-title i18n="Subscribe dialog title">Subscribe to playlist or channel</h4>
<mat-dialog-content> <mat-dialog-content>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
@@ -32,9 +31,11 @@
</mat-form-field> </mat-form-field>
<mat-form-field class="unit-select"> <mat-form-field class="unit-select">
<mat-select color="accent" [(ngModel)]="timerange_unit" [disabled]="download_all"> <mat-select color="accent" [(ngModel)]="timerange_unit" [disabled]="download_all">
<mat-option *ngFor="let time_unit of time_units" [value]="time_unit + (timerange_amount === 1 ? '' : 's')"> @for (time_unit of time_units; track time_unit) {
<mat-option [value]="time_unit + (timerange_amount === 1 ? '' : 's')">
{{time_unit + (timerange_amount === 1 ? '' : 's')}} {{time_unit + (timerange_amount === 1 ? '' : 's')}}
</mat-option> </mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
@@ -43,7 +44,9 @@
<mat-form-field> <mat-form-field>
<mat-label i18n="Max quality">Max quality</mat-label> <mat-label i18n="Max quality">Max quality</mat-label>
<mat-select [disabled]="audioOnlyMode" [(ngModel)]="maxQuality"> <mat-select [disabled]="audioOnlyMode" [(ngModel)]="maxQuality">
<mat-option *ngFor="let available_quality of available_qualities" [value]="available_quality['value']">{{available_quality['label']}}</mat-option> @for (available_quality of available_qualities; track available_quality) {
<mat-option [value]="available_quality['value']">{{available_quality['label']}}</mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
@@ -76,12 +79,13 @@
</div> </div>
</div> </div>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close><ng-container i18n="Subscribe cancel button">Cancel</ng-container></button> <button mat-button mat-dialog-close><ng-container i18n="Subscribe cancel button">Cancel</ng-container></button>
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. --> <!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
<button mat-button [disabled]="!url" type="submit" (click)="subscribeClicked()"><ng-container i18n="Subscribe button">Subscribe</ng-container></button> <button mat-button [disabled]="!url" type="submit" (click)="subscribeClicked()"><ng-container i18n="Subscribe button">Subscribe</ng-container></button>
<div class="mat-spinner" *ngIf="subscribing"> @if (subscribing) {
<div class="mat-spinner">
<mat-spinner [diameter]="25"></mat-spinner> <mat-spinner [diameter]="25"></mat-spinner>
</div> </div>
}
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -1,5 +1,8 @@
<h4 mat-dialog-title>{{sub.name}}&nbsp;<ng-container *ngIf="sub.paused" i18n="Paused suffix">(Paused)</ng-container></h4> <h4 mat-dialog-title>{{sub.name}}
@if (sub.paused) {
&nbsp;<ng-container i18n="Paused suffix">(Paused)</ng-container>
}
</h4>
<mat-dialog-content> <mat-dialog-content>
<div class="info-item"> <div class="info-item">
<strong><ng-container i18n="Subscription type property">Type:</ng-container>&nbsp;</strong> <strong><ng-container i18n="Subscription type property">Type:</ng-container>&nbsp;</strong>
@@ -13,12 +16,13 @@
<strong><ng-container i18n="Subscription ID property">ID:</ng-container>&nbsp;</strong> <strong><ng-container i18n="Subscription ID property">ID:</ng-container>&nbsp;</strong>
<span class="info-item-value">{{sub.id}}</span> <span class="info-item-value">{{sub.id}}</span>
</div> </div>
<div class="info-item" *ngIf="sub.archive"> @if (sub.archive) {
<div class="info-item">
<strong><ng-container i18n="Subscription ID property">Archive:</ng-container>&nbsp;</strong> <strong><ng-container i18n="Subscription ID property">Archive:</ng-container>&nbsp;</strong>
<span class="info-item-value">{{sub.archive}}</span> <span class="info-item-value">{{sub.archive}}</span>
</div> </div>
}
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close><ng-container i18n="Close subscription info button">Close</ng-container></button> <button mat-button mat-dialog-close><ng-container i18n="Close subscription info button">Close</ng-container></button>
<button mat-stroked-button (click)="downloadArchive()" color="accent"><ng-container i18n="Export Archive button">Export Archive</ng-container></button> <button mat-stroked-button (click)="downloadArchive()" color="accent"><ng-container i18n="Export Archive button">Export Archive</ng-container></button>

View File

@@ -3,6 +3,7 @@ import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dial
import { PostsService } from 'app/posts.services'; import { PostsService } from 'app/posts.services';
import { ConfirmDialogComponent } from '../confirm-dialog/confirm-dialog.component'; import { ConfirmDialogComponent } from '../confirm-dialog/confirm-dialog.component';
import { Subscription } from 'api-types'; import { Subscription } from 'api-types';
import { saveAs } from 'file-saver';
@Component({ @Component({
selector: 'app-subscription-info-dialog', selector: 'app-subscription-info-dialog',

View File

@@ -1,18 +1,26 @@
<h4 i18n="Update progress dialog title" mat-dialog-title>Updater</h4> <h4 i18n="Update progress dialog title" mat-dialog-title>Updater</h4>
<mat-dialog-content> <mat-dialog-content>
<div *ngIf="updateStatus"> @if (updateStatus) {
<div>
<div style="margin-bottom: 8px;"> <div style="margin-bottom: 8px;">
<h6 *ngIf="updateStatus['updating']">Update in progress</h6> @if (updateStatus['updating']) {
<h6 *ngIf="!updateStatus['updating'] && updateStatus['error']">Update failed</h6> <h6>Update in progress</h6>
<h6 *ngIf="!updateStatus['updating'] && !updateStatus['error']">Update succeeded!</h6> <mat-progress-bar mode="indeterminate"></mat-progress-bar>
} @else {
@if (updateStatus['error']) {
<h6>Update failed</h6>
} @else {
<h6>Update succeeded!</h6>
}
<mat-progress-bar mode="determinate" value="100"></mat-progress-bar>
}
</div> </div>
<mat-progress-bar *ngIf="updateStatus['updating']" mode="indeterminate"></mat-progress-bar> @if (updateStatus['details']) {
<mat-progress-bar *ngIf="!updateStatus['updating']" mode="determinate" value="100"></mat-progress-bar> <p style="margin-top: 4px; font-size: 13px;">{{updateStatus['details']}}</p>
<p style="margin-top: 4px; font-size: 13px;" *ngIf="updateStatus['details']">{{updateStatus['details']}}</p> }
</div> </div>
}
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close><ng-container i18n="Close update progress dialog">Close</ng-container></button> <button mat-button mat-dialog-close><ng-container i18n="Close update progress dialog">Close</ng-container></button>
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -1,5 +1,4 @@
<h4 mat-dialog-title><ng-container i18n="Update task schedule">Update task schedule</ng-container></h4> <h4 mat-dialog-title><ng-container i18n="Update task schedule">Update task schedule</ng-container></h4>
<mat-dialog-content> <mat-dialog-content>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
@@ -9,7 +8,8 @@
<div class="col-12 mt-2"> <div class="col-12 mt-2">
<mat-checkbox [(ngModel)]="recurring" [disabled]="!enabled"><ng-container i18n="Recurring">Recurring</ng-container></mat-checkbox> <mat-checkbox [(ngModel)]="recurring" [disabled]="!enabled"><ng-container i18n="Recurring">Recurring</ng-container></mat-checkbox>
</div> </div>
<div class="col-12 mt-2" *ngIf="recurring"> @if (recurring) {
<div class="col-12 mt-2">
<mat-form-field> <mat-form-field>
<mat-select placeholder="Interval" [(ngModel)]="interval" [disabled]="!enabled"> <mat-select placeholder="Interval" [(ngModel)]="interval" [disabled]="!enabled">
<mat-option value="weekly">Weekly</mat-option> <mat-option value="weekly">Weekly</mat-option>
@@ -17,15 +17,8 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<div *ngIf="!recurring" class="col-12 mt-2"> @if (interval === 'weekly') {
<mat-form-field> <div class="col-12 mt-2">
<mat-label i18n="Choose a date">Choose a date</mat-label>
<input [(ngModel)]="date" [min]="today" matInput [matDatepicker]="picker" [disabled]="!enabled">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
</div>
<div *ngIf="recurring && interval === 'weekly'" class="col-12 mt-2">
<mat-button-toggle-group [(ngModel)]="days_of_week" [multiple]="true" [disabled]="!enabled" aria-label="Week day"> <mat-button-toggle-group [(ngModel)]="days_of_week" [multiple]="true" [disabled]="!enabled" aria-label="Week day">
<!-- TODO: support translation --> <!-- TODO: support translation -->
<mat-button-toggle [value]="0">M</mat-button-toggle> <mat-button-toggle [value]="0">M</mat-button-toggle>
@@ -37,17 +30,29 @@
<mat-button-toggle [value]="6">S</mat-button-toggle> <mat-button-toggle [value]="6">S</mat-button-toggle>
</mat-button-toggle-group> </mat-button-toggle-group>
</div> </div>
}
} @else {
<div class="col-12 mt-2">
<mat-form-field>
<mat-label i18n="Choose a date">Choose a date</mat-label>
<input [(ngModel)]="date" [min]="today" matInput [matDatepicker]="picker" [disabled]="!enabled">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
</div>
}
<div class="col-12 mt-2"> <div class="col-12 mt-2">
<mat-form-field> <mat-form-field>
<mat-label>Time</mat-label> <mat-label>Time</mat-label>
<input type="time" matInput [(ngModel)]="time" [disabled]="!enabled"> <input type="time" matInput [(ngModel)]="time" [disabled]="!enabled">
<mat-hint *ngIf="Intl?.DateTimeFormat().resolvedOptions().timeZone">{{Intl.DateTimeFormat().resolvedOptions().timeZone}}</mat-hint> @if (Intl?.DateTimeFormat().resolvedOptions().timeZone) {
<mat-hint>{{Intl.DateTimeFormat().resolvedOptions().timeZone}}</mat-hint>
}
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</div> </div>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close><ng-container i18n="Update task schedule cancel button">Cancel</ng-container></button> <button mat-button mat-dialog-close><ng-container i18n="Update task schedule cancel button">Cancel</ng-container></button>
<button mat-button (click)="updateTaskSchedule()"><ng-container i18n="Update button">Update</ng-container></button> <button mat-button (click)="updateTaskSchedule()"><ng-container i18n="Update button">Update</ng-container></button>

View File

@@ -1,7 +1,7 @@
<h4 mat-dialog-title i18n="User profile dialog title">Your Profile</h4> <h4 mat-dialog-title i18n="User profile dialog title">Your Profile</h4>
<mat-dialog-content> <mat-dialog-content>
<div *ngIf="postsService.isLoggedIn && postsService.user"> @if (postsService.isLoggedIn && postsService.user) {
<div>
<div> <div>
<strong><ng-container i18n="Name">Name:</ng-container></strong>&nbsp;{{postsService.user.name}} <strong><ng-container i18n="Name">Name:</ng-container></strong>&nbsp;{{postsService.user.name}}
</div> </div>
@@ -15,14 +15,17 @@
</div> </div>
<mat-divider style="margin-bottom: 20px"></mat-divider> <mat-divider style="margin-bottom: 20px"></mat-divider>
</div> </div>
}
<mat-form-field color="accent"> <mat-form-field color="accent">
<mat-label><ng-container i18n="Language select label">Language</ng-container></mat-label> <mat-label><ng-container i18n="Language select label">Language</ng-container></mat-label>
<mat-select (selectionChange)="localeSelectChanged($event.value)" [(value)]="initialLocale"> <mat-select (selectionChange)="localeSelectChanged($event.value)" [(value)]="initialLocale">
<mat-option *ngFor="let locale of supported_locales" [value]="locale"> @for (locale of supported_locales; track locale) {
<ng-container *ngIf="all_locales[locale]"> <mat-option [value]="locale">
@if (all_locales[locale]) {
{{all_locales[locale]['nativeName']}} {{all_locales[locale]['nativeName']}}
</ng-container> }
</mat-option> </mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<br/> <br/>
@@ -53,12 +56,13 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<div style="width: 100%"> <div style="width: 100%">
<div style="position: relative"> <div style="position: relative">
<button mat-stroked-button mat-dialog-close color="primary"><ng-container i18n="Close">Close</ng-container></button> <button mat-stroked-button mat-dialog-close color="primary"><ng-container i18n="Close">Close</ng-container></button>
<button *ngIf="postsService.isLoggedIn" style="position: absolute; right: 0px;" (click)="logoutClicked()" mat-stroked-button color="warn"><ng-container i18n="Logout">Logout</ng-container></button> @if (postsService.isLoggedIn) {
<button style="position: absolute; right: 0px;" (click)="logoutClicked()" mat-stroked-button color="warn"><ng-container i18n="Logout">Logout</ng-container></button>
}
</div> </div>
</div> </div>
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -5,7 +5,6 @@
<button [disabled]="!initialized || retrieving_file || !write_access" (click)="toggleFavorite()" mat-icon-button ><mat-icon>{{file.favorite ? 'favorite_filled' : 'favorite_outline'}}</mat-icon></button> <button [disabled]="!initialized || retrieving_file || !write_access" (click)="toggleFavorite()" mat-icon-button ><mat-icon>{{file.favorite ? 'favorite_filled' : 'favorite_outline'}}</mat-icon></button>
</div> </div>
</h4> </h4>
<mat-dialog-content> <mat-dialog-content>
<mat-form-field class="info-field"> <mat-form-field class="info-field">
<mat-label i18n="Name">Name</mat-label> <mat-label i18n="Name">Name</mat-label>
@@ -36,17 +35,21 @@
<mat-label i18n="Thumbnail URL">Thumbnail URL</mat-label> <mat-label i18n="Thumbnail URL">Thumbnail URL</mat-label>
<input [(ngModel)]="new_file.thumbnailURL" matInput [disabled]="!editing || new_file.thumbnailPath"> <input [(ngModel)]="new_file.thumbnailURL" matInput [disabled]="!editing || new_file.thumbnailPath">
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="initialized && postsService.categories" class="info-field"> @if (initialized && postsService.categories) {
<mat-form-field class="info-field">
<mat-label i18n="Category">Category</mat-label> <mat-label i18n="Category">Category</mat-label>
<mat-select [value]="category" (selectionChange)="categoryChanged($event)" [compareWith]="categoryComparisonFunction" [disabled]="!editing"> <mat-select [value]="category" (selectionChange)="categoryChanged($event)" [compareWith]="categoryComparisonFunction" [disabled]="!editing">
<mat-option [value]="{}"> <mat-option [value]="{}">
N/A N/A
</mat-option> </mat-option>
<mat-option *ngFor="let available_category of postsService.categories | keyvalue" [value]="available_category.value"> @for (available_category of postsService.categories | keyvalue; track available_category) {
<mat-option [value]="available_category.value">
{{available_category.value['name']}} {{available_category.value['name']}}
</mat-option> </mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
}
<mat-form-field class="info-field"> <mat-form-field class="info-field">
<mat-label i18n="View count">View count</mat-label> <mat-label i18n="View count">View count</mat-label>
<input type="number" [(ngModel)]="new_file.view_count" matInput [disabled]="!editing"> <input type="number" [(ngModel)]="new_file.view_count" matInput [disabled]="!editing">
@@ -55,13 +58,13 @@
<mat-label i18n="Local view count">Local view count</mat-label> <mat-label i18n="Local view count">Local view count</mat-label>
<input type="number" [(ngModel)]="new_file.local_view_count" matInput [disabled]="!editing"> <input type="number" [(ngModel)]="new_file.local_view_count" matInput [disabled]="!editing">
</mat-form-field> </mat-form-field>
<mat-divider style="margin-bottom: 16px;"></mat-divider> <mat-divider style="margin-bottom: 16px;"></mat-divider>
@if (!new_file.isAudio) {
<div *ngIf="!new_file.isAudio" class="info-item"> <div class="info-item">
<div class="info-item-label"><strong><ng-container i18n="Video resolution property">Resolution:</ng-container>&nbsp;</strong></div> <div class="info-item-label"><strong><ng-container i18n="Video resolution property">Resolution:</ng-container>&nbsp;</strong></div>
<div class="info-item-value">{{new_file.height ? new_file.height + 'p' : 'N/A'}}</div> <div class="info-item-value">{{new_file.height ? new_file.height + 'p' : 'N/A'}}</div>
</div> </div>
}
<div class="info-item"> <div class="info-item">
<div class="info-item-label"><strong><ng-container i18n="Video audio bitrate property">Audio bitrate:</ng-container>&nbsp;</strong></div> <div class="info-item-label"><strong><ng-container i18n="Video audio bitrate property">Audio bitrate:</ng-container>&nbsp;</strong></div>
<div class="info-item-value">{{new_file.abr ? new_file.abr + ' Kbps' : 'N/A'}}</div> <div class="info-item-value">{{new_file.abr ? new_file.abr + ' Kbps' : 'N/A'}}</div>
@@ -74,9 +77,7 @@
<div class="info-item-label"><strong><ng-container i18n="Video path property">Path:</ng-container>&nbsp;</strong></div> <div class="info-item-label"><strong><ng-container i18n="Video path property">Path:</ng-container>&nbsp;</strong></div>
<div class="info-item-value">{{new_file.path ? new_file.path : 'N/A'}}</div> <div class="info-item-value">{{new_file.path ? new_file.path : 'N/A'}}</div>
</div> </div>
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close><ng-container i18n="Close video info button">Close</ng-container></button> <button mat-button mat-dialog-close><ng-container i18n="Close video info button">Close</ng-container></button>
<button mat-button [disabled]="!metadataChanged()" (click)="saveChanges()"><ng-container i18n="Save video info button">Save</ng-container></button> <button mat-button [disabled]="!metadataChanged()" (click)="saveChanges()"><ng-container i18n="Save video info button">Save</ng-container></button>

View File

@@ -11,7 +11,9 @@
<button mat-button mat-dialog-close>Cancel</button> <button mat-button mat-dialog-close>Cancel</button>
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. --> <!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
<button mat-button [disabled]="!inputText" type="submit" (click)="enterPressed()">{{submitText}}</button> <button mat-button [disabled]="!inputText" type="submit" (click)="enterPressed()">{{submitText}}</button>
<div class="mat-spinner" *ngIf="inputSubmitted"> @if (inputSubmitted) {
<div class="mat-spinner">
<mat-spinner [diameter]="25"></mat-spinner> <mat-spinner [diameter]="25"></mat-spinner>
</div> </div>
}
</mat-dialog-actions> </mat-dialog-actions>

View File

@@ -12,7 +12,8 @@
</mat-form-field> </mat-form-field>
<!--<button type="button" class="input-clear-button" mat-icon-button (click)="clearInput()"><mat-icon>clear</mat-icon></button>--> <!--<button type="button" class="input-clear-button" mat-icon-button (click)="clearInput()"><mat-icon>clear</mat-icon></button>-->
</div> </div>
<div *ngIf="allowQualitySelect" class="col-7 col-sm-3"> @if (allowQualitySelect) {
<div class="col-7 col-sm-3">
<mat-form-field color="accent" style="display: inline-block; width: inherit; min-width: 120px;"> <mat-form-field color="accent" style="display: inline-block; width: inherit; min-width: 120px;">
<mat-label> <mat-label>
<ng-container i18n="Quality select label"> <ng-container i18n="Quality select label">
@@ -23,30 +24,37 @@
<mat-option i18n="Best" [value]="''"> <mat-option i18n="Best" [value]="''">
Best Best
</mat-option> </mat-option>
<ng-container *ngIf="url && cachedAvailableFormats && cachedAvailableFormats[url]?.formats && !cachedAvailableFormats[url]?.formats_failed"> @if (url && cachedAvailableFormats && cachedAvailableFormats[url]?.formats && !cachedAvailableFormats[url]?.formats_failed) {
<ng-container *ngFor="let option of cachedAvailableFormats[url]['formats'][audioOnly ? 'audio' : 'video']"> @for (option of cachedAvailableFormats[url]['formats'][audioOnly ? 'audio' : 'video']; track option) {
<mat-option [matTooltip]="option.expected_filesize ? humanFileSize(option.expected_filesize) : null" *ngIf="option.key !== 'best_audio_format'" [value]="option"> @if (option.key !== 'best_audio_format') {
<mat-option [matTooltip]="option.expected_filesize ? humanFileSize(option.expected_filesize) : null" [value]="option">
{{option.key}} {{option.key}}
</mat-option> </mat-option>
</ng-container> }
</ng-container> }
<ng-container *ngIf="url && cachedAvailableFormats && cachedAvailableFormats[url]?.formats_failed"> }
<ng-container *ngFor="let option of qualityOptions[audioOnly ? 'audio' : 'video']"> @if (url && cachedAvailableFormats && cachedAvailableFormats[url]?.formats_failed) {
@for (option of qualityOptions[audioOnly ? 'audio' : 'video']; track option) {
<mat-option [value]="option.value"> <mat-option [value]="option.value">
{{option.label}} {{option.label}}
</mat-option> </mat-option>
</ng-container> }
</ng-container> }
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<div class="spinner-div" *ngIf="url !== '' && cachedAvailableFormats[url] && cachedAvailableFormats[url]['formats_loading']"> @if (url !== '' && cachedAvailableFormats[url] && cachedAvailableFormats[url]['formats_loading']) {
<div class="spinner-div">
<mat-spinner [diameter]="25"></mat-spinner> <mat-spinner [diameter]="25"></mat-spinner>
</div> </div>
}
</div>
}
</div> </div>
</div> </div>
</div> @if (results_showing) {
<div class="results-div" *ngIf="results_showing"> <div class="results-div">
<span *ngFor="let result of results; let i = index"> @for (result of results; track result; let i = $index) {
<span>
<mat-card appearance="outlined" class="result-card mat-elevation-z7" [ngClass]="[(i === 0 && results.length > 1) ? 'first-result-card' : '', ((i === results.length-1) && results.length > 1) ? 'last-result-card' : '', (results.length === 1) ? 'only-result-card' : '']"> <mat-card appearance="outlined" class="result-card mat-elevation-z7" [ngClass]="[(i === 0 && results.length > 1) ? 'first-result-card' : '', ((i === results.length-1) && results.length > 1) ? 'last-result-card' : '', (results.length === 1) ? 'only-result-card' : '']">
<div class="search-card-title"> <div class="search-card-title">
{{result.title}} {{result.title}}
@@ -64,7 +72,9 @@
</button> </button>
</mat-card> </mat-card>
</span> </span>
}
</div> </div>
}
</form> </form>
<br/> <br/>
<mat-checkbox [disabled]="autoplay && !!current_download" (change)="videoModeChanged($event)" [(ngModel)]="audioOnly" style="float: left; margin-top: -12px; margin-left: 4px;"> <mat-checkbox [disabled]="autoplay && !!current_download" (change)="videoModeChanged($event)" [(ngModel)]="audioOnly" style="float: left; margin-top: -12px; margin-left: 4px;">
@@ -72,12 +82,13 @@
Only Audio Only Audio
</ng-container> </ng-container>
</mat-checkbox> </mat-checkbox>
<mat-checkbox *ngIf="!forceAutoplay" [disabled]="getURLArray(url).length > 1" (change)="autoplayChanged($event)" [(ngModel)]="autoplay" style="float: right; margin-top: -12px"> @if (!forceAutoplay) {
<mat-checkbox [disabled]="getURLArray(url).length > 1" (change)="autoplayChanged($event)" [(ngModel)]="autoplay" style="float: right; margin-top: -12px">
<ng-container i18n="Autoplay checkbox"> <ng-container i18n="Autoplay checkbox">
Autoplay Autoplay
</ng-container> </ng-container>
</mat-checkbox> </mat-checkbox>
}
</div> </div>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
@@ -86,15 +97,18 @@
Download Download
</ng-container> </ng-container>
</button> </button>
<button (click)="cancelDownload()" style="margin-left: 8px; margin-bottom: 8px" *ngIf="!!current_download" mat-stroked-button color="warn"> @if (!!current_download) {
<button (click)="cancelDownload()" style="margin-left: 8px; margin-bottom: 8px" mat-stroked-button color="warn">
<ng-container i18n="Cancel download button"> <ng-container i18n="Cancel download button">
Cancel Cancel
</ng-container> </ng-container>
</button> </button>
}
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>
</div> </div>
<div *ngIf="allowAdvancedDownload" class="big demo-basic"> @if (allowAdvancedDownload) {
<div class="big demo-basic">
<form style="margin-left: 20px; margin-right: 20px;"> <form style="margin-left: 20px; margin-right: 20px;">
<mat-expansion-panel class="big no-border-radius-top"> <mat-expansion-panel class="big no-border-radius-top">
<mat-expansion-panel-header> <mat-expansion-panel-header>
@@ -104,11 +118,13 @@
</ng-container> </ng-container>
</mat-panel-title> </mat-panel-title>
</mat-expansion-panel-header> </mat-expansion-panel-header>
<p *ngIf="this.simulatedOutput"> @if (this.simulatedOutput) {
<p>
<ng-container i18n="Simulated command label"> <ng-container i18n="Simulated command label">
Simulated command: Simulated command:
</ng-container> </ng-container>
&nbsp;<i>{{this.simulatedOutput}}</i></p> &nbsp;<i>{{this.simulatedOutput}}</i></p>
}
<div class="container" style="padding-bottom: 20px;"> <div class="container" style="padding-bottom: 20px;">
<div class="row"> <div class="row">
<div class="col-12 col-sm-6"> <div class="col-12 col-sm-6">
@@ -148,69 +164,86 @@
</mat-hint> </mat-hint>
</mat-form-field> </mat-form-field>
</div> </div>
<div *ngIf="!youtubeAuthDisabledOverride" class="col-12 col-sm-6 mt-3"> @if (!youtubeAuthDisabledOverride) {
<div class="col-12 col-sm-6 mt-3">
<mat-checkbox color="accent" [disabled]="!!current_download" (change)="youtubeAuthEnabledChanged($event)" [(ngModel)]="youtubeAuthEnabled" style="z-index: 999" [ngModelOptions]="{standalone: true}"> <mat-checkbox color="accent" [disabled]="!!current_download" (change)="youtubeAuthEnabledChanged($event)" [(ngModel)]="youtubeAuthEnabled" style="z-index: 999" [ngModelOptions]="{standalone: true}">
<ng-container i18n="Use authentication checkbox"> <ng-container i18n="Use authentication checkbox">
Use authentication Use authentication
</ng-container> </ng-container>
</mat-checkbox> </mat-checkbox>
<mat-form-field *ngIf="youtubeAuthEnabled" color="accent" class="advanced-input"> @if (youtubeAuthEnabled) {
<mat-form-field color="accent" class="advanced-input">
<mat-label i18n="Username">Username</mat-label> <mat-label i18n="Username">Username</mat-label>
<input [(ngModel)]="youtubeUsername" [ngModelOptions]="{standalone: true}" matInput (ngModelChange)="argsChanged()"> <input [(ngModel)]="youtubeUsername" [ngModelOptions]="{standalone: true}" matInput (ngModelChange)="argsChanged()">
</mat-form-field> </mat-form-field>
}
</div> </div>
<div *ngIf="!youtubeAuthDisabledOverride" class="col-12 col-sm-6 mt-3"> <div class="col-12 col-sm-6 mt-3">
<mat-form-field *ngIf="youtubeAuthEnabled" style="margin-top: 40px;" color="accent" class="advanced-input"> @if (youtubeAuthEnabled) {
<mat-form-field style="margin-top: 40px;" color="accent" class="advanced-input">
<mat-label i18n="Password">Password</mat-label> <mat-label i18n="Password">Password</mat-label>
<input [(ngModel)]="youtubePassword" type="password" [ngModelOptions]="{standalone: true}" matInput (ngModelChange)="argsChanged()"> <input [(ngModel)]="youtubePassword" type="password" [ngModelOptions]="{standalone: true}" matInput (ngModelChange)="argsChanged()">
</mat-form-field> </mat-form-field>
}
</div> </div>
}
<div class="col-12 col-sm-6 mt-3"> <div class="col-12 col-sm-6 mt-3">
<mat-checkbox color="accent" [disabled]="!!current_download" [(ngModel)]="cropFile" style="z-index: 999" [ngModelOptions]="{standalone: true}"> <mat-checkbox color="accent" [disabled]="!!current_download" [(ngModel)]="cropFile" style="z-index: 999" [ngModelOptions]="{standalone: true}">
<ng-container i18n="Crop video checkbox"> <ng-container i18n="Crop video checkbox">
Crop file Crop file
</ng-container> </ng-container>
</mat-checkbox> </mat-checkbox>
<mat-form-field *ngIf="cropFile" color="accent" class="advanced-input"> @if (cropFile) {
<mat-form-field color="accent" class="advanced-input">
<mat-label i18n="Crop from (seconds)">Crop from (seconds)</mat-label> <mat-label i18n="Crop from (seconds)">Crop from (seconds)</mat-label>
<input [(ngModel)]="cropFileStart" type="number" [ngModelOptions]="{standalone: true}" matInput> <input [(ngModel)]="cropFileStart" type="number" [ngModelOptions]="{standalone: true}" matInput>
</mat-form-field> </mat-form-field>
}
</div> </div>
<div class="col-12 col-sm-6 mt-3"> <div class="col-12 col-sm-6 mt-3">
<mat-form-field *ngIf="cropFile" style="margin-top: 40px;" color="accent" class="advanced-input"> @if (cropFile) {
<mat-form-field style="margin-top: 40px;" color="accent" class="advanced-input">
<mat-label i18n="Crop to (seconds)">Crop to (seconds)</mat-label> <mat-label i18n="Crop to (seconds)">Crop to (seconds)</mat-label>
<input [(ngModel)]="cropFileEnd" type="number" [ngModelOptions]="{standalone: true}" matInput> <input [(ngModel)]="cropFileEnd" type="number" [ngModelOptions]="{standalone: true}" matInput>
</mat-form-field> </mat-form-field>
}
</div> </div>
</div> </div>
</div> </div>
</mat-expansion-panel> </mat-expansion-panel>
</form> </form>
</div> </div>
}
<br/> <br/>
<div class="centered big" id="bar_div" *ngIf="current_download && autoplay"> @if (current_download && autoplay) {
<div class="centered big" id="bar_div">
<div class="margined"> <div class="margined">
<div [ngClass]="(+percentDownloaded > 99)?'make-room-for-spinner':'equal-sizes'" style="display: inline-block; width: 100%; padding-left: 20px" *ngIf="current_download.percent_complete && current_download.percent_complete > 1;else indeterminateprogress"> @if (current_download.percent_complete && current_download.percent_complete > 1) {
<div [ngClass]="(+percentDownloaded > 99)?'make-room-for-spinner':'equal-sizes'" style="display: inline-block; width: 100%; padding-left: 20px">
<mat-progress-bar style="border-radius: 5px;" mode="determinate" value="{{percentDownloaded}}"></mat-progress-bar> <mat-progress-bar style="border-radius: 5px;" mode="determinate" value="{{percentDownloaded}}"></mat-progress-bar>
<br/> <br/>
</div> </div>
<div *ngIf="+percentDownloaded > 99" class="spinner"> } @else {
<mat-progress-bar style="border-radius: 5px;" mode="indeterminate"></mat-progress-bar>
}
@if (+percentDownloaded > 99) {
<div class="spinner">
<mat-spinner [diameter]="25"></mat-spinner> <mat-spinner [diameter]="25"></mat-spinner>
</div> </div>
<ng-template #indeterminateprogress> }
<mat-progress-bar style="border-radius: 5px;" mode="indeterminate"></mat-progress-bar>
</ng-template>
</div> </div>
<br/> <br/>
</div> </div>
}
<div style="display: flex; justify-content: center;" *ngIf="downloads && downloads.length > 0 && !autoplay"> @if (downloads && downloads.length > 0 && !autoplay) {
<div style="display: flex; justify-content: center;">
<app-downloads style="width: 80%; min-width: 350px; margin-bottom: 10px" [uids]="download_uids"></app-downloads> <app-downloads style="width: 80%; min-width: 350px; margin-bottom: 10px" [uids]="download_uids"></app-downloads>
</div> </div>
}
<ng-container *ngIf="cachedFileManagerEnabled || fileManagerEnabled"> @if (cachedFileManagerEnabled || fileManagerEnabled) {
<app-recent-videos #recentVideos></app-recent-videos> <app-recent-videos #recentVideos></app-recent-videos>
<br/> <br/>
<h4 style="text-align: center">Custom playlists</h4> <h4 style="text-align: center">Custom playlists</h4>
<app-custom-playlists></app-custom-playlists> <app-custom-playlists></app-custom-playlists>
</ng-container> }

View File

@@ -548,7 +548,7 @@ export class MainComponent implements OnInit {
} }
if (!(this.cachedAvailableFormats[url] && this.cachedAvailableFormats[url]['formats'])) { if (!(this.cachedAvailableFormats[url] && this.cachedAvailableFormats[url]['formats'])) {
this.cachedAvailableFormats[url]['formats_loading'] = true; this.cachedAvailableFormats[url]['formats_loading'] = true;
this.postsService.getFileFormats([url]).subscribe(res => { this.postsService.getFileFormats(url).subscribe(res => {
this.cachedAvailableFormats[url]['formats_loading'] = false; this.cachedAvailableFormats[url]['formats_loading'] = false;
const infos = res['result']; const infos = res['result'];
if (!infos || !infos.formats) { if (!infos || !infos.formats) {

View File

@@ -1,4 +1,5 @@
<div style="height: 100%" *ngIf="playlist.length > 0 && show_player"> @if (playlist.length > 0 && show_player) {
<div style="height: 100%">
<div style="height: 100%" [ngClass]="(currentItem.type === 'audio/mp3') ? null : 'container-video'"> <div style="height: 100%" [ngClass]="(currentItem.type === 'audio/mp3') ? null : 'container-video'">
<div style="max-width: 100%; margin-left: 0px; height: 100%"> <div style="max-width: 100%; margin-left: 0px; height: 100%">
<mat-drawer-container style="height: 100%" class="example-container" autosize> <mat-drawer-container style="height: 100%" class="example-container" autosize>
@@ -6,59 +7,80 @@
<vg-player style="height: fit-content; max-height: 75vh" (onPlayerReady)="onPlayerReady($event)" [style.background-color]="(currentItem.type === 'audio/mp3') ? postsService.theme.drawer_color : 'black'"> <vg-player style="height: fit-content; max-height: 75vh" (onPlayerReady)="onPlayerReady($event)" [style.background-color]="(currentItem.type === 'audio/mp3') ? postsService.theme.drawer_color : 'black'">
<video [ngClass]="(currentItem.type === 'audio/mp3') ? 'audio-styles' : 'video-styles'" #media class="video-player" [vgMedia]="$any(media)" [src]="currentItem.src" id="singleVideo" preload="auto" controls playsinline> <video [ngClass]="(currentItem.type === 'audio/mp3') ? 'audio-styles' : 'video-styles'" #media class="video-player" [vgMedia]="$any(media)" [src]="currentItem.src" id="singleVideo" preload="auto" controls playsinline>
</video> </video>
<app-skip-ad-button *ngIf="postsService['config']['API']['use_sponsorblock_API'] && api && playlist?.length > 0 && playlist[currentIndex]['type'] === 'video/mp4'" (setPlaybackTimestamp)="setPlaybackTimestamp($event)" [current_video]="playlist[currentIndex]" [playback_timestamp]="api.currentTime" class="skip-ad-button"></app-skip-ad-button> @if (postsService['config']['API']['use_sponsorblock_API'] && api && playlist?.length > 0 && playlist[currentIndex]['type'] === 'video/mp4') {
<app-skip-ad-button (setPlaybackTimestamp)="setPlaybackTimestamp($event)" [current_video]="playlist[currentIndex]" [playback_timestamp]="api.currentTime" class="skip-ad-button"></app-skip-ad-button>
}
</vg-player> </vg-player>
</div> </div>
<div style="height: fit-content; width: 100%; margin-top: 10px;"> <div style="height: fit-content; width: 100%; margin-top: 10px;">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-2 col-lg-1"> <div class="col-2 col-lg-1">
<ng-container *ngIf="db_file">{{db_file['local_view_count'] ? db_file['local_view_count']+1 : 1}}&nbsp;<ng-container i18n="View count label">views</ng-container></ng-container> @if (db_file) {
{{db_file['local_view_count'] ? db_file['local_view_count']+1 : 1}}&nbsp;<ng-container i18n="View count label">views</ng-container>
}
</div> </div>
<div style="white-space: pre-line;" class="col-8 col-lg-9"> <div style="white-space: pre-line;" class="col-8 col-lg-9">
<ng-container *ngIf="db_file && db_file['description']"> @if (db_file && db_file['description']) {
<p> <p>
<app-see-more [text]="db_file['description']"></app-see-more> <app-see-more [text]="db_file['description']"></app-see-more>
</p> </p>
</ng-container> } @else {
<ng-container *ngIf="!db_file || !db_file['description']">
<p i18n="No description" style="text-align: center;"> <p i18n="No description" style="text-align: center;">
No description available. No description available.
</p> </p>
</ng-container> }
</div> </div>
<div class="col-2"> <div class="col-2">
<span class="buttons" *ngIf="db_playlist"> @if (db_playlist) {
<span class="buttons">
<button (click)="downloadContent()" [disabled]="downloading" mat-icon-button><mat-icon>save</mat-icon></button> <button (click)="downloadContent()" [disabled]="downloading" mat-icon-button><mat-icon>save</mat-icon></button>
<mat-spinner *ngIf="downloading" class="spinner" [diameter]="35"></mat-spinner> @if (downloading) {
<button *ngIf="(!postsService.isLoggedIn || postsService.permissions.includes('sharing')) && !auto" (click)="openShareDialog()" mat-icon-button><mat-icon>share</mat-icon></button> <mat-spinner class="spinner" [diameter]="35"></mat-spinner>
}
@if ((!postsService.isLoggedIn || postsService.permissions.includes('sharing')) && !auto) {
<button (click)="openShareDialog()" mat-icon-button><mat-icon>share</mat-icon></button>
}
</span> </span>
<span class="buttons" *ngIf="db_file"> }
@if (db_file) {
<span class="buttons">
<button (click)="downloadFile()" [disabled]="downloading" mat-icon-button><mat-icon>cloud_download</mat-icon></button> <button (click)="downloadFile()" [disabled]="downloading" mat-icon-button><mat-icon>cloud_download</mat-icon></button>
<mat-spinner *ngIf="downloading" class="spinner" [diameter]="35"></mat-spinner> @if (downloading) {
<button *ngIf="!postsService.isLoggedIn || postsService.permissions.includes('sharing')" (click)="openShareDialog()" mat-icon-button><mat-icon>share</mat-icon></button> <mat-spinner class="spinner" [diameter]="35"></mat-spinner>
}
@if (!postsService.isLoggedIn || postsService.permissions.includes('sharing')) {
<button (click)="openShareDialog()" mat-icon-button><mat-icon>share</mat-icon></button>
}
</span> </span>
<ng-container *ngIf="db_file || playlist[currentIndex]"></ng-container> }
<button (click)="openFileInfoDialog()" *ngIf="db_file || db_playlist" mat-icon-button><mat-icon>info</mat-icon></button> @if (db_file || db_playlist) {
<button *ngIf="db_file && db_file.url.includes('twitch.tv')" (click)="drawer.toggle()" mat-icon-button><mat-icon>chat</mat-icon></button> <button (click)="openFileInfoDialog()" mat-icon-button><mat-icon>info</mat-icon></button>
}
@if (db_file && db_file.url.includes('twitch.tv')) {
<button (click)="drawer.toggle()" mat-icon-button><mat-icon>chat</mat-icon></button>
}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div style="height: fit-content; width: 100%; margin-top: 10px;"> <div style="height: fit-content; width: 100%; margin-top: 10px;">
<mat-button-toggle-group cdkDropList [cdkDropListSortingDisabled]="true" (cdkDropListDropped)="drop($event)" style="width: 80%; left: 9%" vertical name="videoSelect" aria-label="Video Select" #group="matButtonToggleGroup"> <mat-button-toggle-group cdkDropList [cdkDropListSortingDisabled]="true" (cdkDropListDropped)="drop($event)" style="width: 80%; left: 9%" vertical name="videoSelect" aria-label="Video Select" #group="matButtonToggleGroup">
<mat-button-toggle cdkDrag *ngFor="let playlist_item of playlist; let i = index" [checked]="currentItem.title === playlist_item.title" (click)="onClickPlaylistItem(playlist_item, i)" class="toggle-button" [value]="playlist_item.title">{{playlist_item.label}}</mat-button-toggle> @for (playlist_item of playlist; track playlist_item; let i = $index) {
<mat-button-toggle cdkDrag [checked]="currentItem.title === playlist_item.title" (click)="onClickPlaylistItem(playlist_item, i)" class="toggle-button" [value]="playlist_item.title">{{playlist_item.label}}</mat-button-toggle>
}
</mat-button-toggle-group> </mat-button-toggle-group>
</div> </div>
@if (db_file && api && postsService.config) {
<app-concurrent-stream *ngIf="db_file && api && postsService.config" (setPlaybackRate)="setPlaybackRate($event)" (togglePlayback)="togglePlayback($event)" (setPlaybackTimestamp)="setPlaybackTimestamp($event)" [playing]="api.state === 'playing'" [uid]="uid" [playback_timestamp]="api.time.current/1000" [server_mode]="!postsService.config.Advanced.multi_user_mode || postsService.isLoggedIn"></app-concurrent-stream> <app-concurrent-stream (setPlaybackRate)="setPlaybackRate($event)" (togglePlayback)="togglePlayback($event)" (setPlaybackTimestamp)="setPlaybackTimestamp($event)" [playing]="api.state === 'playing'" [uid]="uid" [playback_timestamp]="api.time.current/1000" [server_mode]="!postsService.config.Advanced.multi_user_mode || postsService.isLoggedIn"></app-concurrent-stream>
}
<mat-drawer #drawer class="example-sidenav" mode="side" position="end" [opened]="db_file && db_file['chat_exists']"> <mat-drawer #drawer class="example-sidenav" mode="side" position="end" [opened]="db_file && db_file['chat_exists']">
<ng-container *ngIf="api_ready && db_file && db_file.url.includes('twitch.tv')"> @if (api_ready && db_file && db_file.url.includes('twitch.tv')) {
<app-twitch-chat #twitchchat [db_file]="db_file" [current_timestamp]="api.currentTime" [sub]="subscription"></app-twitch-chat> <app-twitch-chat #twitchchat [db_file]="db_file" [current_timestamp]="api.currentTime" [sub]="subscription"></app-twitch-chat>
</ng-container> }
</mat-drawer> </mat-drawer>
</mat-drawer-container> </mat-drawer-container>
</div> </div>
</div> </div>
</div> </div>
}

View File

@@ -8,6 +8,7 @@ import { ShareMediaDialogComponent } from '../dialogs/share-media-dialog/share-m
import { DatabaseFile, FileType, Playlist } from '../../api-types'; import { DatabaseFile, FileType, Playlist } from '../../api-types';
import { TwitchChatComponent } from 'app/components/twitch-chat/twitch-chat.component'; import { TwitchChatComponent } from 'app/components/twitch-chat/twitch-chat.component';
import { VideoInfoDialogComponent } from 'app/dialogs/video-info-dialog/video-info-dialog.component'; import { VideoInfoDialogComponent } from 'app/dialogs/video-info-dialog/video-info-dialog.component';
import { saveAs } from 'file-saver';
export interface IMedia { export interface IMedia {
title: string; title: string;

View File

@@ -4,7 +4,7 @@ import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch'; import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw'; import 'rxjs/add/observable/throw';
import { THEMES_CONFIG } from '../themes'; import { THEMES_CONFIG } from '../themes';
import { Router, CanActivate, ActivatedRouteSnapshot } from '@angular/router'; import { Router, ActivatedRouteSnapshot } from '@angular/router';
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
@@ -119,9 +119,10 @@ import {
import { isoLangs } from './dialogs/user-profile-dialog/locales_list'; import { isoLangs } from './dialogs/user-profile-dialog/locales_list';
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser';
import { MatDrawerMode } from '@angular/material/sidenav'; import { MatDrawerMode } from '@angular/material/sidenav';
import { environment } from '../environments/environment';
@Injectable() @Injectable()
export class PostsService implements CanActivate { export class PostsService {
path = ''; path = '';
// local settings // local settings
@@ -176,7 +177,7 @@ export class PostsService implements CanActivate {
if (isDevMode()) { if (isDevMode()) {
this.debugMode = true; this.debugMode = true;
this.path = 'http://localhost:17442/api/'; this.path = !environment.codespaces ? 'http://localhost:17442/api/' : `${window.location.origin.replace('4200', '17442')}/api/`;
} }
this.http_params = `apiKey=${this.auth_token}` this.http_params = `apiKey=${this.auth_token}`
@@ -459,7 +460,7 @@ export class PostsService implements CanActivate {
return this.http.post<SuccessObject>(this.path + 'deleteArchiveItems', body, this.httpOptions); return this.http.post<SuccessObject>(this.path + 'deleteArchiveItems', body, this.httpOptions);
} }
getFileFormats(url) { getFileFormats(url: string) {
const body: GetFileFormatsRequest = {url: url}; const body: GetFileFormatsRequest = {url: url};
return this.http.post<GetFileFormatsResponse>(this.path + 'getFileFormats', body, this.httpOptions); return this.http.post<GetFileFormatsResponse>(this.path + 'getFileFormats', body, this.httpOptions);
} }

View File

@@ -3,7 +3,8 @@
<!-- Server --> <!-- Server -->
<mat-tab label="Main" i18n-label="Main settings label"> <mat-tab label="Main" i18n-label="Main settings label">
<ng-template matTabContent style="padding: 15px;"> <ng-template matTabContent style="padding: 15px;">
<div *ngIf="new_config" class="container-fluid"> @if (new_config) {
<div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<mat-form-field class="text-field" color="accent"> <mat-form-field class="text-field" color="accent">
@@ -22,7 +23,7 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<mat-checkbox color="accent" [(ngModel)]="new_config['Advanced']['multi_user_mode']"><ng-container i18n="Multi user mode setting">Multi-user mode</ng-container></mat-checkbox> <mat-checkbox color="accent" [(ngModel)]="new_config['Advanced']['multi_user_mode']"><ng-container i18n="Multi user mode setting">Multi-user mode</ng-container></mat-checkbox>
@@ -37,7 +38,7 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<mat-checkbox color="accent" [(ngModel)]="new_config['Subscriptions']['allow_subscriptions']"><ng-container i18n="Allow subscriptions setting">Allow subscriptions</ng-container></mat-checkbox> <mat-checkbox color="accent" [(ngModel)]="new_config['Subscriptions']['allow_subscriptions']"><ng-container i18n="Allow subscriptions setting">Allow subscriptions</ng-container></mat-checkbox>
@@ -62,7 +63,7 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<mat-form-field> <mat-form-field>
@@ -78,13 +79,15 @@
</div> </div>
</div> </div>
</div> </div>
}
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<!-- Downloader --> <!-- Downloader -->
<mat-tab label="Downloader" i18n-label="Downloader settings label"> <mat-tab label="Downloader" i18n-label="Downloader settings label">
<ng-template matTabContent> <ng-template matTabContent>
<!-- Downloader --> <!-- Downloader -->
<div *ngIf="new_config" class="container-fluid"> @if (new_config) {
<div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<mat-form-field class="text-field" color="accent"> <mat-form-field class="text-field" color="accent">
@@ -93,7 +96,6 @@
<mat-hint><ng-container i18n="Aduio path setting input hint">Path for audio only downloads. It is relative to YTDL-Material's root folder.</ng-container></mat-hint> <mat-hint><ng-container i18n="Aduio path setting input hint">Path for audio only downloads. It is relative to YTDL-Material's root folder.</ng-container></mat-hint>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<mat-form-field class="text-field" color="accent"> <mat-form-field class="text-field" color="accent">
<mat-label i18n="Video folder path">Video folder path</mat-label> <mat-label i18n="Video folder path">Video folder path</mat-label>
@@ -101,7 +103,6 @@
<mat-hint><ng-container i18n="Video path setting input hint">Path for video downloads. It is relative to YTDL-Material's root folder.</ng-container></mat-hint> <mat-hint><ng-container i18n="Video path setting input hint">Path for video downloads. It is relative to YTDL-Material's root folder.</ng-container></mat-hint>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-12 mt-3 mb-1"> <div class="col-12 mt-3 mb-1">
<mat-form-field class="text-field" color="accent"> <mat-form-field class="text-field" color="accent">
<mat-label i18n="Default file output">Default file output</mat-label> <mat-label i18n="Default file output">Default file output</mat-label>
@@ -112,7 +113,6 @@
</mat-hint> </mat-hint>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-12 mt-4 mb-5"> <div class="col-12 mt-4 mb-5">
<mat-form-field class="text-field" style="margin-right: 12px;" color="accent"> <mat-form-field class="text-field" style="margin-right: 12px;" color="accent">
<mat-label i18n="Global custom args">Global custom args</mat-label> <mat-label i18n="Global custom args">Global custom args</mat-label>
@@ -124,12 +124,14 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<h6 i18n="Categories">Categories</h6> <h6 i18n="Categories">Categories</h6>
<div *ngIf="postsService.categories && postsService.categories.length > 0" cdkDropList class="category-list" (cdkDropListDropped)="dropCategory($event)"> @if (postsService.categories && postsService.categories.length > 0) {
<div class="category-box" *ngFor="let category of postsService.categories" cdkDrag> <div cdkDropList class="category-list" (cdkDropListDropped)="dropCategory($event)">
@for (category of postsService.categories; track category) {
<div class="category-box" cdkDrag>
<div class="category-custom-placeholder" *cdkDragPlaceholder></div> <div class="category-custom-placeholder" *cdkDragPlaceholder></div>
{{category['name']}} {{category['name']}}
<span style="float: right"> <span style="float: right">
@@ -137,7 +139,9 @@
<button mat-icon-button (click)="deleteCategory(category)"><mat-icon>cancel</mat-icon></button> <button mat-icon-button (click)="deleteCategory(category)"><mat-icon>cancel</mat-icon></button>
</span> </span>
</div> </div>
}
</div> </div>
}
<button style="margin-top: 10px;" mat-mini-fab (click)="openAddCategoryDialog()"><mat-icon>add</mat-icon></button> <button style="margin-top: 10px;" mat-mini-fab (click)="openAddCategoryDialog()"><mat-icon>add</mat-icon></button>
</div> </div>
<div class="col-12 mt-2 mb-2"> <div class="col-12 mt-2 mb-2">
@@ -146,23 +150,21 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['use_youtubedl_archive']"><ng-container i18n="Use youtubedl archive setting">Use youtube-dl archive</ng-container></mat-checkbox> <mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['use_youtubedl_archive']"><ng-container i18n="Use youtubedl archive setting">Use youtube-dl archive</ng-container></mat-checkbox>
</div> </div>
<div class="col-12 mt-2"> <div class="col-12 mt-2">
<mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['include_thumbnail']"><ng-container i18n="Include thumbnail setting">Include thumbnail</ng-container></mat-checkbox> <mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['include_thumbnail']"><ng-container i18n="Include thumbnail setting">Include thumbnail</ng-container></mat-checkbox>
</div> </div>
<div class="col-12 mt-2 mb-2"> <div class="col-12 mt-2 mb-2">
<mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['include_metadata']"><ng-container i18n="Include metadata setting">Include metadata</ng-container></mat-checkbox> <mat-checkbox color="accent" [(ngModel)]="new_config['Downloader']['include_metadata']"><ng-container i18n="Include metadata setting">Include metadata</ng-container></mat-checkbox>
</div> </div>
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3 mb-4"> <div class="col-12 mt-3 mb-4">
<mat-form-field class="text-field" color="accent"> <mat-form-field class="text-field" color="accent">
@@ -181,19 +183,21 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<button (click)="killAllDownloads()" mat-stroked-button color="warn"><ng-container i18n="Kill all downloads button">Kill all downloads</ng-container></button> <button (click)="killAllDownloads()" mat-stroked-button color="warn"><ng-container i18n="Kill all downloads button">Kill all downloads</ng-container></button>
</div> </div>
</div> </div>
</div> </div>
}
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<!-- Extra --> <!-- Extra -->
<mat-tab label="Extra" i18n-label="Extra settings label"> <mat-tab label="Extra" i18n-label="Extra settings label">
<ng-template matTabContent> <ng-template matTabContent>
<div *ngIf="new_config" class="container-fluid"> @if (new_config) {
<div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<mat-form-field class="text-field" color="accent"> <mat-form-field class="text-field" color="accent">
@@ -220,7 +224,7 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<mat-checkbox color="accent" [(ngModel)]="new_config['API']['use_API_key']"><ng-container i18n="Enable Public API key setting">Enable Public API</ng-container></mat-checkbox> <mat-checkbox color="accent" [(ngModel)]="new_config['API']['use_API_key']"><ng-container i18n="Enable Public API key setting">Enable Public API</ng-container></mat-checkbox>
@@ -240,7 +244,7 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<mat-checkbox color="accent" [(ngModel)]="new_config['API']['use_youtube_API']"><ng-container i18n="Use YouTube API setting">Use YouTube API</ng-container></mat-checkbox> <mat-checkbox color="accent" [(ngModel)]="new_config['API']['use_youtube_API']"><ng-container i18n="Use YouTube API setting">Use YouTube API</ng-container></mat-checkbox>
@@ -264,7 +268,7 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<h6>RSS Feed</h6> <h6>RSS Feed</h6>
@@ -276,7 +280,7 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<h6>Chrome</h6> <h6>Chrome</h6>
@@ -299,50 +303,58 @@
</div> </div>
</div> </div>
</div> </div>
}
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<!-- Database --> <!-- Database -->
<mat-tab label="Database" i18n-label="Database settings label"> <mat-tab label="Database" i18n-label="Database settings label">
<ng-template matTabContent> <ng-template matTabContent>
<div *ngIf="new_config" class="container-fluid"> @if (new_config) {
<div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<div *ngIf="db_info"> @if (db_info) {
<div>
<p><ng-container i18n="Database location label">Database location:</ng-container>&nbsp;<strong>{{db_info['using_local_db'] ? 'Local' : 'MongoDB'}}</strong></p> <p><ng-container i18n="Database location label">Database location:</ng-container>&nbsp;<strong>{{db_info['using_local_db'] ? 'Local' : 'MongoDB'}}</strong></p>
<h6 i18n="Records per table label">Records per table</h6> <h6 i18n="Records per table label">Records per table</h6>
<mat-list style="padding-top: 0px"> <mat-list style="padding-top: 0px">
<mat-list-item style="height: 28px" *ngFor="let table_stats of db_info['stats_by_table'] | keyvalue"> @for (table_stats of db_info['stats_by_table'] | keyvalue; track table_stats) {
<mat-list-item style="height: 28px">
{{table_stats.key}}: {{table_stats.value.records_count}} {{table_stats.key}}: {{table_stats.value.records_count}}
</mat-list-item> </mat-list-item>
}
</mat-list> </mat-list>
<mat-form-field style="width: 100%; margin-top: 15px; margin-bottom: 10px" color="accent"> <mat-form-field style="width: 100%; margin-top: 15px; margin-bottom: 10px" color="accent">
<mat-label i18n="MongoDB Connection String">MongoDB Connection String</mat-label> <mat-label i18n="MongoDB Connection String">MongoDB Connection String</mat-label>
<input [(ngModel)]="new_config['Database']['mongodb_connection_string']" matInput required> <input [(ngModel)]="new_config['Database']['mongodb_connection_string']" matInput required>
<mat-hint><ng-container i18n="MongoDB Connection String setting hint AKA preamble">Example:</ng-container>&nbsp;mongodb://127.0.0.1:27017/?compressors=zlib<br>Docker: mongodb://&lt;container name&gt;:27017/?compressors=zlib</mat-hint> <mat-hint><ng-container i18n="MongoDB Connection String setting hint AKA preamble">Example:</ng-container>&nbsp;mongodb://127.0.0.1:27017/?compressors=zlib<br>Docker: mongodb://&lt;container name&gt;:27017/?compressors=zlib</mat-hint>
</mat-form-field> </mat-form-field>
<div class="test-connection-div"> <div class="test-connection-div">
<button (click)="testConnectionString(new_config['Database']['mongodb_connection_string'])" [disabled]="testing_connection_string" mat-flat-button color="accent"><ng-container i18n="Test connection string button">Test connection string</ng-container></button> <button (click)="testConnectionString(new_config['Database']['mongodb_connection_string'])" [disabled]="testing_connection_string" mat-flat-button color="accent"><ng-container i18n="Test connection string button">Test connection string</ng-container></button>
<mat-spinner class="test-connection-spinner" style="margin-left: 10px" *ngIf="testing_connection_string" [diameter]="25"></mat-spinner> @if (testing_connection_string) {
<mat-spinner class="test-connection-spinner" style="margin-left: 10px" [diameter]="25"></mat-spinner>
}
</div> </div>
<div class="transfer-db-div"> <div class="transfer-db-div">
<button [disabled]="db_transferring" color="accent" (click)="transferDB()" mat-raised-button><ng-container i18n="Transfer DB button">Transfer DB to </ng-container>{{db_info['using_local_db'] ? 'MongoDB' : 'Local'}}</button> <button [disabled]="db_transferring" color="accent" (click)="transferDB()" mat-raised-button><ng-container i18n="Transfer DB button">Transfer DB to </ng-container>{{db_info['using_local_db'] ? 'MongoDB' : 'Local'}}</button>
</div> </div>
</div> </div>
<div *ngIf="!db_info"> } @else {
<div>
<ng-container i18n="Database info not retrieved error message">Database information could not be retrieved. Check the server logs for more information.</ng-container> <ng-container i18n="Database info not retrieved error message">Database information could not be retrieved. Check the server logs for more information.</ng-container>
</div> </div>
}
</div> </div>
</div> </div>
</div> </div>
}
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<!-- Notifications --> <!-- Notifications -->
<mat-tab label="Notifications" i18n-label="Notifications settings label"> <mat-tab label="Notifications" i18n-label="Notifications settings label">
<ng-template matTabContent> <ng-template matTabContent>
<div *ngIf="new_config" class="container-fluid"> @if (new_config) {
<div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<div><a target="_blank" href="https://github.com/Tzahi12345/YoutubeDL-Material/wiki/Notifications"><ng-container i18n="Documentation">Documentation</ng-container></a></div> <div><a target="_blank" href="https://github.com/Tzahi12345/YoutubeDL-Material/wiki/Notifications"><ng-container i18n="Documentation">Documentation</ng-container></a></div>
@@ -435,12 +447,14 @@
</div> </div>
</div> </div>
</div> </div>
}
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<!-- Advanced --> <!-- Advanced -->
<mat-tab label="Advanced" i18n-label="Host settings label"> <mat-tab label="Advanced" i18n-label="Host settings label">
<ng-template matTabContent> <ng-template matTabContent>
<div *ngIf="new_config" class="container-fluid"> @if (new_config) {
<div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-3"> <div class="col-12 mt-3">
<mat-form-field> <mat-form-field>
@@ -500,7 +514,7 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-2 mb-2"> <div class="col-12 mt-2 mb-2">
<mat-checkbox color="accent" [(ngModel)]="new_config['Advanced']['use_cookies']"><ng-container i18n="Use cookies setting">Use Cookies</ng-container></mat-checkbox> <mat-checkbox color="accent" [(ngModel)]="new_config['Advanced']['use_cookies']"><ng-container i18n="Use cookies setting">Use Cookies</ng-container></mat-checkbox>
@@ -509,17 +523,18 @@
</div> </div>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid mt-3"> <div class="container-fluid mt-3">
<app-updater></app-updater> <app-updater></app-updater>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div *ngIf="new_config" class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-12 mt-4"> <div class="col-12 mt-4">
<button (click)="restartServer()" mat-stroked-button color="warn"><ng-container i18n="Restart server button">Restart server</ng-container></button> <button (click)="restartServer()" mat-stroked-button color="warn"><ng-container i18n="Restart server button">Restart server</ng-container></button>
</div> </div>
</div> </div>
</div> </div>
}
</ng-template> </ng-template>
</mat-tab> </mat-tab>
<mat-tab [disabled]="!postsService.config?.Advanced.multi_user_mode"> <mat-tab [disabled]="!postsService.config?.Advanced.multi_user_mode">
@@ -528,8 +543,9 @@
<ng-container i18n="Users settings label">Users</ng-container> <ng-container i18n="Users settings label">Users</ng-container>
</div> </div>
</ng-template> </ng-template>
<ng-container *ngIf="postsService.config?.Advanced.multi_user_mode"> @if (postsService.config?.Advanced.multi_user_mode) {
<div *ngIf="new_config" style="margin-top: 24px; margin-bottom: -25px;"> @if (new_config) {
<div style="margin-top: 24px; margin-bottom: -25px;">
<div> <div>
<mat-checkbox color="accent" [(ngModel)]="new_config['Users']['allow_registration']"><ng-container i18n="Allow registration setting">Allow user registration</ng-container></mat-checkbox> <mat-checkbox color="accent" [(ngModel)]="new_config['Users']['allow_registration']"><ng-container i18n="Allow registration setting">Allow user registration</ng-container></mat-checkbox>
</div> </div>
@@ -545,7 +561,8 @@
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<div *ngIf="new_config['Users']['auth_method'] === 'ldap'"> @if (new_config['Users']['auth_method'] === 'ldap') {
<div>
<div> <div>
<mat-form-field> <mat-form-field>
<mat-label i18n="LDAP URL">LDAP URL</mat-label> <mat-label i18n="LDAP URL">LDAP URL</mat-label>
@@ -577,20 +594,23 @@
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
}
<mat-divider></mat-divider> <mat-divider></mat-divider>
</div> </div>
<app-modify-users *ngIf="new_config"></app-modify-users> <app-modify-users></app-modify-users>
</ng-container> }
}
</mat-tab> </mat-tab>
<mat-tab *ngIf="postsService.config" label="Logs" i18n-label="Logs settings label"> @if (postsService.config) {
<mat-tab label="Logs" i18n-label="Logs settings label">
<ng-template matTabContent> <ng-template matTabContent>
<div style="margin-top: 15px; height: 84%;"> <div style="margin-top: 15px; height: 84%;">
<app-logs-viewer></app-logs-viewer> <app-logs-viewer></app-logs-viewer>
</div> </div>
</ng-template> </ng-template>
</mat-tab> </mat-tab>
}
</mat-tab-group> </mat-tab-group>
<div class="action-buttons"> <div class="action-buttons">
<button style="margin-left: 10px; height: 37.3px" color="accent" (click)="saveSettings()" [disabled]="settingsSame()" mat-raised-button><mat-icon>done</mat-icon>&nbsp;&nbsp; <button style="margin-left: 10px; height: 37.3px" color="accent" (click)="saveSettings()" [disabled]="settingsSame()" mat-raised-button><mat-icon>done</mat-icon>&nbsp;&nbsp;
<ng-container i18n="Settings save button">Save</ng-container> <ng-container i18n="Settings save button">Save</ng-container>
@@ -599,4 +619,3 @@
<span i18n="Settings cancel button">Cancel</span> <span i18n="Settings cancel button">Cancel</span>
</button> </button>
</div> </div>

View File

@@ -1,27 +1,38 @@
<div style="margin-top: 14px;"> <div style="margin-top: 14px;">
<button class="back-button" (click)="goBack()" mat-icon-button><mat-icon>arrow_back</mat-icon></button> <button class="back-button" (click)="goBack()" mat-icon-button><mat-icon>arrow_back</mat-icon></button>
<div style="margin-bottom: 15px;"> <div style="margin-bottom: 15px;">
<h2 style="text-align: center;" *ngIf="subscription"> @if (subscription) {
{{subscription.name}}&nbsp;<ng-container *ngIf="subscription.paused" i18n="Paused suffix">(Paused)</ng-container> <h2 style="text-align: center;">
{{subscription.name}}
@if (subscription.paused) {
&nbsp;<ng-container i18n="Paused suffix">(Paused)</ng-container>
}
<button class="edit-button" (click)="editSubscription()" [disabled]="downloading" matTooltip="Edit" i18n-matTooltip="Edit" mat-icon-button><mat-icon class="save-icon">edit</mat-icon></button> <button class="edit-button" (click)="editSubscription()" [disabled]="downloading" matTooltip="Edit" i18n-matTooltip="Edit" mat-icon-button><mat-icon class="save-icon">edit</mat-icon></button>
</h2> </h2>
<mat-progress-bar style="width: 80%; margin: 0 auto; margin-top: 15px;" *ngIf="subscription && subscription.downloading" mode="indeterminate"></mat-progress-bar> }
@if (subscription && subscription.downloading) {
<mat-progress-bar style="width: 80%; margin: 0 auto; margin-top: 15px;" mode="indeterminate"></mat-progress-bar>
}
</div> </div>
<mat-divider style="width: 80%; margin: 0 auto"></mat-divider> <mat-divider style="width: 80%; margin: 0 auto"></mat-divider>
<br/> <br/>
<!-- Extra margin added for floating buttons to have room --> <!-- Extra margin added for floating buttons to have room -->
<div style="margin-bottom: 100px;" *ngIf="subscription"> @if (subscription) {
<div style="margin-bottom: 100px;">
<app-recent-videos #recentVideos [sub_id]="subscription.id"></app-recent-videos> <app-recent-videos #recentVideos [sub_id]="subscription.id"></app-recent-videos>
</div> </div>
}
<div class="check-button"> <div class="check-button">
<ng-container *ngIf="subscription.downloading"> @if (subscription.downloading) {
<button color="primary" (click)="cancelCheckSubscription()" [disabled]="cancel_clicked" matTooltip="Cancel subscription check" i18n-matTooltip="Cancel subscription check" mat-fab><mat-icon class="save-icon">cancel</mat-icon></button> <button color="primary" (click)="cancelCheckSubscription()" [disabled]="cancel_clicked" matTooltip="Cancel subscription check" i18n-matTooltip="Cancel subscription check" mat-fab><mat-icon class="save-icon">cancel</mat-icon></button>
</ng-container> } @else {
<ng-container *ngIf="!subscription.downloading">
<button color="primary" (click)="checkSubscription()" [disabled]="check_clicked" matTooltip="Check subscription" i18n-matTooltip="Check subscription" mat-fab><mat-icon class="save-icon">youtube_searched_for</mat-icon></button> <button color="primary" (click)="checkSubscription()" [disabled]="check_clicked" matTooltip="Check subscription" i18n-matTooltip="Check subscription" mat-fab><mat-icon class="save-icon">youtube_searched_for</mat-icon></button>
</ng-container> }
</div> </div>
<button class="watch-button" color="primary" (click)="watchSubscription()" matTooltip="Play all" i18n-matTooltip="Play all" mat-fab><mat-icon class="save-icon">video_library</mat-icon></button> <button class="watch-button" color="primary" (click)="watchSubscription()" matTooltip="Play all" i18n-matTooltip="Play all" mat-fab><mat-icon class="save-icon">video_library</mat-icon></button>
<button class="save-button" color="primary" (click)="downloadContent()" [disabled]="downloading" matTooltip="Download zip" i18n-matTooltip="Download zip" mat-fab><mat-icon class="save-icon">save</mat-icon><mat-spinner *ngIf="downloading" class="spinner" [diameter]="50"></mat-spinner></button> <button class="save-button" color="primary" (click)="downloadContent()" [disabled]="downloading" matTooltip="Download zip" i18n-matTooltip="Download zip" mat-fab><mat-icon class="save-icon">save</mat-icon>
@if (downloading) {
<mat-spinner class="spinner" [diameter]="50"></mat-spinner>
}
</button>
</div> </div>

View File

@@ -4,6 +4,7 @@ import { ActivatedRoute, Router, ParamMap } from '@angular/router';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { EditSubscriptionDialogComponent } from 'app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component'; import { EditSubscriptionDialogComponent } from 'app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component';
import { Subscription } from 'api-types'; import { Subscription } from 'api-types';
import { saveAs } from 'file-saver';
@Component({ @Component({
selector: 'app-subscription', selector: 'app-subscription',

View File

@@ -1,18 +1,23 @@
<br/> <br/>
<h2 i18n="Subscriptions title" style="text-align: center; margin-bottom: 15px;">Your subscriptions</h2> <h2 i18n="Subscriptions title" style="text-align: center; margin-bottom: 15px;">Your subscriptions</h2>
<mat-divider style="width: 80%; margin: 0 auto"></mat-divider> <mat-divider style="width: 80%; margin: 0 auto"></mat-divider>
<br/> <br/>
<h4 i18n="Subscriptions channels title" style="text-align: center;">Channels</h4> <h4 i18n="Subscriptions channels title" style="text-align: center;">Channels</h4>
<mat-nav-list class="sub-nav-list"> <mat-nav-list class="sub-nav-list">
<mat-list-item *ngFor="let sub of channel_subscriptions" style="pointer-events: none"> @for (sub of channel_subscriptions; track sub) {
<mat-list-item style="pointer-events: none">
<a style="pointer-events: auto;" class="a-list-item" matListItemTitle (click)="goToSubscription(sub)"> <a style="pointer-events: auto;" class="a-list-item" matListItemTitle (click)="goToSubscription(sub)">
<strong *ngIf="sub.name">{{ sub.name }}&nbsp;<ng-container *ngIf="sub.paused" i18n="Paused suffix">(Paused)</ng-container></strong> @if (sub.name) {
<div *ngIf="!sub.name"> <strong>{{ sub.name }}
@if (sub.paused) {
&nbsp;<ng-container i18n="Paused suffix">(Paused)</ng-container>
}
</strong>
} @else {
<div>
<ng-container i18n="Subscription playlist not available text">Name not available. Channel retrieval in progress.</ng-container> <ng-container i18n="Subscription playlist not available text">Name not available. Channel retrieval in progress.</ng-container>
</div> </div>
}
</a> </a>
<div style="pointer-events: auto; color: unset" matListItemMeta> <div style="pointer-events: auto; color: unset" matListItemMeta>
<button matTooltip="Edit" i18n-matTooltip="Edit" mat-icon-button (click)="editSubscription(sub)"> <button matTooltip="Edit" i18n-matTooltip="Edit" mat-icon-button (click)="editSubscription(sub)">
@@ -23,20 +28,29 @@
</button> </button>
</div> </div>
</mat-list-item> </mat-list-item>
}
</mat-nav-list> </mat-nav-list>
@if (channel_subscriptions.length === 0 && subscriptions) {
<div style="width: 80%; margin: 0 auto; padding-left: 15px;" *ngIf="channel_subscriptions.length === 0 && subscriptions"> <div style="width: 80%; margin: 0 auto; padding-left: 15px;">
<p i18n="No channel subscriptions text">You have no channel subscriptions.</p> <p i18n="No channel subscriptions text">You have no channel subscriptions.</p>
</div> </div>
}
<h4 i18n="Subscriptions playlists title" style="text-align: center; margin-top: 10px;">Playlists</h4> <h4 i18n="Subscriptions playlists title" style="text-align: center; margin-top: 10px;">Playlists</h4>
<mat-nav-list class="sub-nav-list"> <mat-nav-list class="sub-nav-list">
<mat-list-item *ngFor="let sub of playlist_subscriptions" style="pointer-events: none"> @for (sub of playlist_subscriptions; track sub) {
<mat-list-item style="pointer-events: none">
<a style="pointer-events: auto;" class="a-list-item" matListItemTitle (click)="goToSubscription(sub)"> <a style="pointer-events: auto;" class="a-list-item" matListItemTitle (click)="goToSubscription(sub)">
<strong *ngIf="sub.name">{{ sub.name }}&nbsp;<ng-container *ngIf="sub.paused" i18n="Paused suffix">(Paused)</ng-container></strong> @if (sub.name) {
<div *ngIf="!sub.name"> <strong>{{ sub.name }}
@if (sub.paused) {
&nbsp;<ng-container i18n="Paused suffix">(Paused)</ng-container>
}
</strong>
} @else {
<div>
<ng-container i18n="Subscription playlist not available text">Name not available. Channel retrieval in progress.</ng-container> <ng-container i18n="Subscription playlist not available text">Name not available. Channel retrieval in progress.</ng-container>
</div> </div>
}
</a> </a>
<div style="pointer-events: auto; color: unset" matListItemMeta> <div style="pointer-events: auto; color: unset" matListItemMeta>
<button matTooltip="Edit" i18n-matTooltip="Edit" mat-icon-button (click)="editSubscription(sub)"> <button matTooltip="Edit" i18n-matTooltip="Edit" mat-icon-button (click)="editSubscription(sub)">
@@ -47,14 +61,16 @@
</button> </button>
</div> </div>
</mat-list-item> </mat-list-item>
}
</mat-nav-list> </mat-nav-list>
@if (playlist_subscriptions.length === 0 && subscriptions) {
<div style="width: 80%; margin: 0 auto; padding-left: 15px;" *ngIf="playlist_subscriptions.length === 0 && subscriptions"> <div style="width: 80%; margin: 0 auto; padding-left: 15px;">
<p i18n="No playlist subscriptions text">You have no playlist subscriptions.</p> <p i18n="No playlist subscriptions text">You have no playlist subscriptions.</p>
</div> </div>
}
<div style="margin: 0 auto; width: 80%" *ngIf="subscriptions_loading"> @if (subscriptions_loading) {
<div style="margin: 0 auto; width: 80%">
<mat-progress-bar mode="indeterminate"></mat-progress-bar> <mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div> </div>
}
<button class="add-subscription-button" (click)="openSubscribeDialog()" matTooltip="Add subscription" i18n-matTooltip="Add subscription" mat-fab><mat-icon>add</mat-icon></button> <button class="add-subscription-button" (click)="openSubscribeDialog()" matTooltip="Add subscription" i18n-matTooltip="Add subscription" mat-fab><mat-icon>add</mat-icon></button>

View File

@@ -2,17 +2,27 @@
<div style="display: inline-block"> <div style="display: inline-block">
<ng-container i18n="Select a version">Select a version:</ng-container> <ng-container i18n="Select a version">Select a version:</ng-container>
</div> </div>
<div *ngIf="availableVersions" style="display: inline-block; margin-left: 15px;"> @if (availableVersions) {
<div style="display: inline-block; margin-left: 15px;">
<mat-form-field> <mat-form-field>
<mat-select [(ngModel)]="selectedVersion"> <mat-select [(ngModel)]="selectedVersion">
<mat-option *ngFor="let version of availableVersionsFiltered" [value]="version['tag_name']"> @for (version of availableVersionsFiltered; track version) {
<mat-option [value]="version['tag_name']">
{{version['tag_name'] + (version === latestStableRelease ? ' - Latest Stable' : '') + (version['tag_name'] === CURRENT_VERSION ? ' - Current Version' : '')}} {{version['tag_name'] + (version === latestStableRelease ? ' - Latest Stable' : '') + (version['tag_name'] === CURRENT_VERSION ? ' - Current Version' : '')}}
</mat-option> </mat-option>
}
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
<div *ngIf="selectedVersion && selectedVersion !== CURRENT_VERSION" style="display: inline-block; margin-left: 15px;"> }
@if (selectedVersion && selectedVersion !== CURRENT_VERSION) {
<div style="display: inline-block; margin-left: 15px;">
<button (click)="updateServer()" color="accent" mat-raised-button><mat-icon>update</mat-icon>&nbsp;&nbsp; <button (click)="updateServer()" color="accent" mat-raised-button><mat-icon>update</mat-icon>&nbsp;&nbsp;
<ng-container *ngIf="selectedVersion > CURRENT_VERSION">Upgrade to</ng-container><ng-container *ngIf="selectedVersion < CURRENT_VERSION">Downgrade to</ng-container>&nbsp;{{selectedVersion}}</button> @if (selectedVersion > CURRENT_VERSION) {
<ng-container i18n="Upgrade to">Upgrade to</ng-container>
} @else {
<ng-container i18n="Downgrade to">Downgrade to</ng-container>
}&nbsp;{{selectedVersion}}</button>
</div> </div>
}
</div> </div>

View File

@@ -4142,6 +4142,163 @@
<context context-type="linenumber">363</context> <context context-type="linenumber">363</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6549265851868599441" datatype="html">
<source>Video</source>
<target state="translated">Vídeo</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">40</context>
</context-group>
</trans-unit>
<trans-unit id="28da11220a3377ddce3c7948825d33101f142782" datatype="html">
<source>Extractor</source>
<target state="translated">Extractor</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">57</context>
</context-group>
<note priority="1" from="description">Extractor</note>
</trans-unit>
<trans-unit id="c150a30bbbdb175b4d08820196a9acb66b167653" datatype="html">
<source>Archives empty</source>
<target state="translated">Arxius buits</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">72</context>
</context-group>
<note priority="1" from="description">Archives empty</note>
</trans-unit>
<trans-unit id="51a161ce175abcd44f6c6cbd0e996681bf553ac3" datatype="html">
<source>Delete selected</source>
<target state="translated">Elimina seleccionat</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">77</context>
</context-group>
<note priority="1" from="description">Delete selected</note>
</trans-unit>
<trans-unit id="a2f14a73f7a6e94479f67423cc51102da8d6f524" datatype="html">
<source>None</source>
<target state="translated">Cap</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">84</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">126</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">27</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">36</context>
</context-group>
<note priority="1" from="description">None</note>
</trans-unit>
<trans-unit id="c41475a25c9f9d9639db9efa56637603a77528b4" datatype="html">
<source>Download archive</source>
<target state="translated">Descarregar arxiu</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">80</context>
</context-group>
<note priority="1" from="description">Download archive</note>
</trans-unit>
<trans-unit id="8425787787095143143" datatype="html">
<source>Would you like to delete <x id="selected archives amount" equiv-text="this.selection.selected.length"/> archive(s)?</source>
<target state="translated">T'agradaria eliminar <x id="selected archives amount" equiv-text="this.selection.selected.length"/> arxiu(s)?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">153</context>
</context-group>
</trans-unit>
<trans-unit id="2525880134753073592" datatype="html">
<source>Successfully deleted archive items!</source>
<target state="translated">Elements de l'arxiu eliminats amb èxit!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">172</context>
</context-group>
</trans-unit>
<trans-unit id="c748ac656af9f13998206ef2c52018dd418b0483" datatype="html">
<source>Archives</source>
<target state="translated">Arxius</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">26</context>
</context-group>
<note priority="1" from="description">Archives menu label</note>
</trans-unit>
<trans-unit id="4b3972c3e9485218508a95f7a4ce7758e3f09ced" datatype="html">
<source>Upload</source>
<target state="translated">Pujar</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">137</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.html</context>
<context context-type="linenumber">30</context>
</context-group>
<note priority="1" from="description">Upload</note>
</trans-unit>
<trans-unit id="347407180135731058" datatype="html">
<source>Audio</source>
<target state="translated">Àudio</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">44</context>
</context-group>
</trans-unit>
<trans-unit id="7022070615528435141" datatype="html">
<source>Delete</source>
<target state="translated">Suprimir</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">154</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">160</context>
</context-group>
</trans-unit>
<trans-unit id="8953483585652369683" datatype="html">
<source>Archive successfully imported!</source>
<target state="translated">Arxiu importat amb èxit!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">130</context>
</context-group>
</trans-unit>
<trans-unit id="45cc8ca94b5a50842a9a8ef804a5ab089a38ae5c" datatype="html">
<source>ID</source>
<target state="translated">ID</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">47</context>
</context-group>
<note priority="1" from="description">ID</note>
</trans-unit>
<trans-unit id="3159807825117518005" datatype="html">
<source>Delete archives</source>
<target state="translated">Elimina arxius</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">152</context>
</context-group>
</trans-unit>
<trans-unit id="5ca707824ab93066c7d9b44e1b8bf216725c2c22" datatype="html">
<source>Filter</source>
<target state="translated">Filtres</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
<note priority="1" from="description">Filter</note>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -1107,7 +1107,7 @@
</trans-unit> </trans-unit>
<trans-unit id="dc3d990391c944d1fbfc7cfb402f7b5e112fb3a8" datatype="html"> <trans-unit id="dc3d990391c944d1fbfc7cfb402f7b5e112fb3a8" datatype="html">
<source>Allow advanced download</source> <source>Allow advanced download</source>
<target xml:lang="de-DE">Erweiterte Download-Optionen aktivieren</target> <target xml:lang="de-DE" state="translated">Erweiterte Downloads erlauben</target>
<note from="description" priority="1">Allow advanced downloading setting</note> <note from="description" priority="1">Allow advanced downloading setting</note>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/settings/settings.component.html</context> <context context-type="sourcefile">app/settings/settings.component.html</context>
@@ -1525,7 +1525,7 @@
</trans-unit> </trans-unit>
<trans-unit id="ea30873bd3f0d5e4fb2378eec3f0a1db77634a28" datatype="html"> <trans-unit id="ea30873bd3f0d5e4fb2378eec3f0a1db77634a28" datatype="html">
<source>Download all uploads</source> <source>Download all uploads</source>
<target xml:lang="de-DE">Alle hochgeladene Videos herunterladen</target> <target xml:lang="de-DE" state="translated">Alle hochgeladenen Videos herunterladen</target>
<note from="description" priority="1">Download all uploads subscription setting</note> <note from="description" priority="1">Download all uploads subscription setting</note>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/dialogs/subscribe-dialog/subscribe-dialog.component.html</context> <context context-type="sourcefile">app/dialogs/subscribe-dialog/subscribe-dialog.component.html</context>
@@ -1642,7 +1642,7 @@
</trans-unit> </trans-unit>
<trans-unit id="587b57ced54965d8874c3fd0e9dfedb987e5df04" datatype="html"> <trans-unit id="587b57ced54965d8874c3fd0e9dfedb987e5df04" datatype="html">
<source>You have no playlist subscriptions.</source> <source>You have no playlist subscriptions.</source>
<target xml:lang="de-DE">Sie haben keine Wiedergabeliste abonniert.</target> <target xml:lang="de-DE" state="translated">Du hast keine Wiedergabeliste abonniert.</target>
<note from="description" priority="1">No playlist subscriptions text</note> <note from="description" priority="1">No playlist subscriptions text</note>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/subscriptions/subscriptions.component.html</context> <context context-type="sourcefile">app/subscriptions/subscriptions.component.html</context>
@@ -2059,7 +2059,7 @@
</trans-unit> </trans-unit>
<trans-unit id="fb35145bfb84521e21b6385363d59221f436a573" datatype="html"> <trans-unit id="fb35145bfb84521e21b6385363d59221f436a573" datatype="html">
<source>Kill all downloads</source> <source>Kill all downloads</source>
<target>Alle Herunterladen-Ereignisse abbrechen</target> <target state="translated">Alle Downloads abbrechen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/settings/settings.component.html</context> <context context-type="sourcefile">app/settings/settings.component.html</context>
<context context-type="linenumber">139</context> <context context-type="linenumber">139</context>
@@ -2221,7 +2221,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3697f8583ea42868aa269489ad366103d94aece7" datatype="html"> <trans-unit id="3697f8583ea42868aa269489ad366103d94aece7" datatype="html">
<source>Editing</source> <source>Editing</source>
<target>Bearbeiten</target> <target state="translated">In Bearbeitung</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html</context> <context context-type="sourcefile">app/dialogs/edit-subscription-dialog/edit-subscription-dialog.component.html</context>
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
@@ -2592,7 +2592,7 @@
</trans-unit> </trans-unit>
<trans-unit id="d54142de169844b014ae913a4056c31495f4a305" datatype="html"> <trans-unit id="d54142de169844b014ae913a4056c31495f4a305" datatype="html">
<source>Test connection string</source> <source>Test connection string</source>
<target>Verbindungstest-String</target> <target state="translated">Verbindungsstring testen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context> <context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">304</context> <context context-type="linenumber">304</context>
@@ -2717,7 +2717,7 @@
</trans-unit> </trans-unit>
<trans-unit id="49e09cce4426975ba06c1667063d2c1df9c94362" datatype="html"> <trans-unit id="49e09cce4426975ba06c1667063d2c1df9c94362" datatype="html">
<source>Autoplay</source> <source>Autoplay</source>
<target>Automatisches abspielen</target> <target state="translated">Automatisches Abspielen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/main/main.component.html</context> <context context-type="sourcefile">src/app/main/main.component.html</context>
<context context-type="linenumber">70,71</context> <context context-type="linenumber">70,71</context>
@@ -4309,7 +4309,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6219551536751479443" datatype="html"> <trans-unit id="6219551536751479443" datatype="html">
<source>Finished downloading</source> <source>Finished downloading</source>
<target state="translated">Herunterladen abgeschlossen</target> <target state="translated">Download abgeschlossen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context> <context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">17</context> <context context-type="linenumber">17</context>
@@ -4317,7 +4317,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5947241266456580665" datatype="html"> <trans-unit id="5947241266456580665" datatype="html">
<source>Download failed</source> <source>Download failed</source>
<target state="translated">Herunterladen fehlgeschlagen</target> <target state="translated">Download fehlgeschlagen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context> <context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">18</context> <context context-type="linenumber">18</context>
@@ -4358,7 +4358,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6876310993601590130" datatype="html"> <trans-unit id="6876310993601590130" datatype="html">
<source>Download completed</source> <source>Download completed</source>
<target state="translated">Herunterladen abgeschlossen</target> <target state="translated">Download abgeschlossen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context> <context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">23</context> <context context-type="linenumber">23</context>
@@ -4523,7 +4523,7 @@
</trans-unit> </trans-unit>
<trans-unit id="c5dc5fbcce45e9b1530e2a5c2baa8ebe722aef4c" datatype="html"> <trans-unit id="c5dc5fbcce45e9b1530e2a5c2baa8ebe722aef4c" datatype="html">
<source>Download complete</source> <source>Download complete</source>
<target state="translated">Herunterladen abgeschlossen</target> <target state="translated">Download abgeschlossen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context> <context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">391</context> <context context-type="linenumber">391</context>
@@ -4703,7 +4703,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8643601595923420698" datatype="html"> <trans-unit id="8643601595923420698" datatype="html">
<source>Retry download</source> <source>Retry download</source>
<target state="translated">Herunterladen erneut versuchen</target> <target state="translated">Download erneut versuchen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context> <context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">31</context> <context context-type="linenumber">31</context>
@@ -4727,7 +4727,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5000203534763292992" datatype="html"> <trans-unit id="5000203534763292992" datatype="html">
<source>Download restarted!</source> <source>Download restarted!</source>
<target state="translated">Herunterladen neu gestartet!</target> <target state="translated">Download neu gestartet!</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context> <context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">72</context> <context context-type="linenumber">72</context>
@@ -4783,7 +4783,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3ffd9490f3a4c0b24021d25e1dc71fcfe5d39cd6" datatype="html"> <trans-unit id="3ffd9490f3a4c0b24021d25e1dc71fcfe5d39cd6" datatype="html">
<source>Download error</source> <source>Download error</source>
<target state="translated">Herunterladefehler</target> <target state="translated">Downloadfehler</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context> <context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">392</context> <context context-type="linenumber">392</context>
@@ -4941,6 +4941,206 @@
</context-group> </context-group>
<note priority="1" from="description">No description</note> <note priority="1" from="description">No description</note>
</trans-unit> </trans-unit>
<trans-unit id="4665451070906079743" datatype="html">
<source>Favorited</source>
<target state="translated">Favorisiert</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">65</context>
</context-group>
</trans-unit>
<trans-unit id="56b1a3c93fb95fed1805005c561a5e431d57ffae" datatype="html">
<source>Blacklist all files</source>
<target state="translated">Alle Dateien blacklisten</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">11</context>
</context-group>
<note priority="1" from="description">Blacklist deleted files</note>
</trans-unit>
<trans-unit id="9aa1b4779a515170b297d2c0507e6ff9d2e3e0e0" datatype="html">
<source>Blacklist deleted subscription files</source>
<target state="translated">Gelöschte Abo-Dateien blacklisten</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">14</context>
</context-group>
<note priority="1" from="description">Blacklist deleted subscription files</note>
</trans-unit>
<trans-unit id="c2faa86201eab08b5b39b5437f96ab9432e125e7" datatype="html">
<source>Item limit</source>
<target state="translated">Elementlimit</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">46</context>
</context-group>
<note priority="1" from="description">Item limit</note>
</trans-unit>
<trans-unit id="784837056777689544" datatype="html">
<source>Would you like to unsubscribe from <x id="subscription name" equiv-text="this.sub['name']"/>?</source>
<target state="translated">Möchtest Du dich von <x id="subscription name" equiv-text="this.sub['name']"/> abmelden?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.ts</context>
<context context-type="linenumber">31</context>
</context-group>
</trans-unit>
<trans-unit id="d618f383a0ea2458eeb945a85190d4a002ea394b" datatype="html">
<source>Arg</source>
<target state="translated">Argument</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html</context>
<context context-type="linenumber">41</context>
</context-group>
<note priority="1" from="description">Arg</note>
</trans-unit>
<trans-unit id="0cfc9cfe7cd8ea14bc053693b28872da739af02c" datatype="html">
<source>See docs here.</source>
<target state="translated">Doku ansehen.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">375</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">382</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">392</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">402</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">409</context>
</context-group>
<note priority="1" from="description">Discord API setting hint</note>
</trans-unit>
<trans-unit id="9e766e11a9de375907aaf566897ecc6dac393ebc" datatype="html">
<source>Webhook URL</source>
<target state="translated">Webhook URL</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">366</context>
</context-group>
<note priority="1" from="description">webhook URL</note>
</trans-unit>
<trans-unit id="b770c48628d98cb4633d6a17e3f0ba0265376af5" datatype="html">
<source>Gotify app token</source>
<target state="translated">Gotify app token</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">407</context>
</context-group>
<note priority="1" from="description">Gotify app token</note>
</trans-unit>
<trans-unit id="3e420c675b8f3f3702576d52e8bb6e8e1d3feda0" datatype="html">
<source>How do I get the chat ID?</source>
<target state="translated">Wie bekomme ich die Chat ID?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">426</context>
</context-group>
<note priority="1" from="description">Telegram chat ID help</note>
</trans-unit>
<trans-unit id="06f503e492d6dbcf59e7b9c412ca86913d718689" datatype="html">
<source>ntfy topic URL</source>
<target state="translated">ntfy Themen URL</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">390</context>
</context-group>
<note priority="1" from="description">ntfy topic URL</note>
</trans-unit>
<trans-unit id="5827fde8fcafdd55ae80921ad3ad4aa01012e203" datatype="html">
<source>Use gotify API</source>
<target state="translated">gotify API verwenden</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">396</context>
</context-group>
<note priority="1" from="description">Use gotify API setting</note>
</trans-unit>
<trans-unit id="55f559d6f666b945479f534b0c182f70cd0a8a69" datatype="html">
<source>Gotify server URL</source>
<target state="translated">Gotify Server URL</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">400</context>
</context-group>
<note priority="1" from="description">Gotify server URL</note>
</trans-unit>
<trans-unit id="144e1a21ebe8fa238f88d2ac27515ed711cfc9a0" datatype="html">
<source>Telegram chat ID</source>
<target state="translated">Telegram chat ID</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">424</context>
</context-group>
<note priority="1" from="description">Telegram chat ID</note>
</trans-unit>
<trans-unit id="2481374649045841364" datatype="html">
<source>Would you like to delete <x id="category name" equiv-text="category['name']"/>?</source>
<target state="translated">Möchtest du <x id="category name" equiv-text="category['name']"/> löschen?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">159</context>
</context-group>
</trans-unit>
<trans-unit id="3371159074051387771" datatype="html">
<source>Failed to delete <x id="category name" equiv-text="category['name']"/>!</source>
<target state="translated">Löschen von <x id="category name" equiv-text="category['name']"/> fehlgeschlagen!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">172</context>
</context-group>
</trans-unit>
<trans-unit id="8336047719608684263" datatype="html">
<source>Unsubscribe from <x id="subscription name" equiv-text="this.sub['name']"/></source>
<target state="translated">Von <x id="subscription name" equiv-text="this.sub['name']"/> abmelden</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.ts</context>
<context context-type="linenumber">30</context>
</context-group>
</trans-unit>
<trans-unit id="37469c9f3e31d95087fa22b6c9c3bc64adf1692d" datatype="html">
<source>Enable RSS Feed</source>
<target state="translated">RSS Feed aktivieren</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">271</context>
</context-group>
<note priority="1" from="description">Enable RSS Feed setting</note>
</trans-unit>
<trans-unit id="3264d82792954815be755b3da01e2625458711dc" datatype="html">
<source>Discord Webhook URL</source>
<target state="translated">Discord Webhook URL</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">373</context>
</context-group>
<note priority="1" from="description">Discord Webhook URL</note>
</trans-unit>
<trans-unit id="7cedb649779673568447b994463b2882c4e0436a" datatype="html">
<source>Slack Webhook URL</source>
<target state="translated">Slack Webhook URL</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">380</context>
</context-group>
<note priority="1" from="description">Slack Webhook URL</note>
</trans-unit>
<trans-unit id="8c1bf02206fbc371ff69ab1b7e35a17ba29d169d" datatype="html">
<source>Use ntfy API</source>
<target state="translated">ntfy API verwenden</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">386</context>
</context-group>
<note priority="1" from="description">Use ntfy API setting</note>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -4802,7 +4802,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2481374649045841364" datatype="html"> <trans-unit id="2481374649045841364" datatype="html">
<source>Would you like to delete <x id="category name" equiv-text="category['name']"/>?</source> <source>Would you like to delete <x id="category name" equiv-text="category['name']"/>?</source>
<target state="needs-translation">Apakah Anda ingin menghapus <x id="category name" equiv-text="category['name']"/>?</target> <target state="translated">Apakah Anda ingin menghapus <x id="category name" equiv-text="category['name']"/>?</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context> <context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">159</context> <context context-type="linenumber">159</context>
@@ -5026,6 +5026,70 @@
</context-group> </context-group>
<note priority="1" from="description">Telegram chat ID</note> <note priority="1" from="description">Telegram chat ID</note>
</trans-unit> </trans-unit>
<trans-unit id="af30e51aa8b67e1133a341ec28359be05150e65c" datatype="html">
<source>No description available.</source>
<target state="translated">Tidak ada deskripsi.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/player/player.component.html</context>
<context context-type="linenumber">25,27</context>
</context-group>
<note priority="1" from="description">No description</note>
</trans-unit>
<trans-unit id="e0a11fbea353b1ce1131161774e4a3e10bcb99b1" datatype="html">
<source>Large</source>
<target state="translated">Besar</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">44,46</context>
</context-group>
<note priority="1" from="description">Large</note>
</trans-unit>
<trans-unit id="b6399391e706e2d7b7b7880eb5630e4e6f49728c" datatype="html">
<source>Side</source>
<target state="translated">Samping</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">35,37</context>
</context-group>
<note priority="1" from="description">Side</note>
</trans-unit>
<trans-unit id="1236604279860679031" datatype="html">
<source>Restart</source>
<target state="translated">Mulai ulang</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">62</context>
</context-group>
</trans-unit>
<trans-unit id="9042260521669277115" datatype="html">
<source>Pause</source>
<target state="translated">Jeda</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">68</context>
</context-group>
</trans-unit>
<trans-unit id="7182974689040833178" datatype="html">
<source>Resume</source>
<target state="translated">Lanjutkan</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">74</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">80</context>
</context-group>
</trans-unit>
<trans-unit id="338b44701a53ce3ef2f36abfb56f89c3edfa9eab" datatype="html">
<source>Over</source>
<target state="translated">Lebih</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">32,34</context>
</context-group>
<note priority="1" from="description">Over</note>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -4119,6 +4119,963 @@
<context context-type="linenumber">58</context> <context context-type="linenumber">58</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="ea2b65121b93921fe54692025da9b9e3ce779ad5" datatype="html">
<source>Task settings - <x id="INTERPOLATION" equiv-text="{{task.title}}"/></source>
<target state="translated">Impostazioni attività - <x id="INTERPOLATION" equiv-text="{{task.title}}"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
<note priority="1" from="description">Task settings</note>
</trans-unit>
<trans-unit id="3264d82792954815be755b3da01e2625458711dc" datatype="html">
<source>Discord Webhook URL</source>
<target state="translated">URL webhook Discord</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">373</context>
</context-group>
<note priority="1" from="description">Discord Webhook URL</note>
</trans-unit>
<trans-unit id="5827fde8fcafdd55ae80921ad3ad4aa01012e203" datatype="html">
<source>Use gotify API</source>
<target state="translated">Utilizza API gotify</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">396</context>
</context-group>
<note priority="1" from="description">Use gotify API setting</note>
</trans-unit>
<trans-unit id="5000203534763292992" datatype="html">
<source>Download restarted!</source>
<target state="translated">Download riavviato!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">72</context>
</context-group>
</trans-unit>
<trans-unit id="8643601595923420698" datatype="html">
<source>Retry download</source>
<target state="translated">Riprova download</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">31</context>
</context-group>
</trans-unit>
<trans-unit id="6785427850041119037" datatype="html">
<source>Delete category</source>
<target state="translated">Elimina categoria</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">158</context>
</context-group>
</trans-unit>
<trans-unit id="3ffd9490f3a4c0b24021d25e1dc71fcfe5d39cd6" datatype="html">
<source>Download error</source>
<target state="translated">Errore nel download</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">359</context>
</context-group>
<note priority="1" from="description">Download error</note>
</trans-unit>
<trans-unit id="9176960997786930103" datatype="html">
<source>Error for: <x id="PH" equiv-text="task['title']"/></source>
<target state="translated">Errore per: <x id="PH" equiv-text="task['title']"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/tasks/tasks.component.ts</context>
<context context-type="linenumber">174</context>
</context-group>
</trans-unit>
<trans-unit id="019d4bd6a5690f0cfa0ecf346a4e6bf7f0d8debb" datatype="html">
<source>Remove</source>
<target state="translated">Rimuovi</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.html</context>
<context context-type="linenumber">23</context>
</context-group>
<note priority="1" from="description">Remove</note>
</trans-unit>
<trans-unit id="b770c48628d98cb4633d6a17e3f0ba0265376af5" datatype="html">
<source>Gotify app token</source>
<target state="translated">Token app Gotify</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">407</context>
</context-group>
<note priority="1" from="description">Gotify app token</note>
</trans-unit>
<trans-unit id="56b1a3c93fb95fed1805005c561a5e431d57ffae" datatype="html">
<source>Blacklist all files</source>
<target state="translated">Metti nella lista nera tutti i file</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">11</context>
</context-group>
<note priority="1" from="description">Blacklist deleted files</note>
</trans-unit>
<trans-unit id="6549265851868599441" datatype="html">
<source>Video</source>
<target state="translated">Video</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">40</context>
</context-group>
</trans-unit>
<trans-unit id="1d4fa01d25990f60abf21c3a451fa8ba262b7912" datatype="html">
<source>Unfavorite</source>
<target state="translated">Non preferito</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/unified-file-card/unified-file-card.component.html</context>
<context context-type="linenumber">27</context>
</context-group>
<note priority="1" from="description">Unfavorite button</note>
</trans-unit>
<trans-unit id="1f2809e6a99d511fdb6eaf041d785fe54d0680cc" datatype="html">
<source>File card size</source>
<target state="translated">Dimensione carta file</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">42</context>
</context-group>
<note priority="1" from="description">File card size</note>
</trans-unit>
<trans-unit id="28da11220a3377ddce3c7948825d33101f142782" datatype="html">
<source>Extractor</source>
<target state="translated">Estrattore</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">57</context>
</context-group>
<note priority="1" from="description">Extractor</note>
</trans-unit>
<trans-unit id="338b44701a53ce3ef2f36abfb56f89c3edfa9eab" datatype="html">
<source>Over</source>
<target state="translated">Sopra</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">32,34</context>
</context-group>
<note priority="1" from="description">Over</note>
</trans-unit>
<trans-unit id="378c072ce05889c9771718d05106e7685fcd3507" datatype="html">
<source>Medium</source>
<target state="translated">Media</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">47,49</context>
</context-group>
<note priority="1" from="description">Medium</note>
</trans-unit>
<trans-unit id="9a865c2922f5c01899d06c472dba2e5bd63bcff9" datatype="html">
<source>Small</source>
<target state="translated">Piccola</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">50,52</context>
</context-group>
<note priority="1" from="description">Small</note>
</trans-unit>
<trans-unit id="5a105e7bd7e7db6ea211fe950fc9f317379acceb" datatype="html">
<source>No notifications available</source>
<target state="translated">Nessuna notifica disponibile</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
<note priority="1" from="description">No notifications available</note>
</trans-unit>
<trans-unit id="0cfc9cfe7cd8ea14bc053693b28872da739af02c" datatype="html">
<source>See docs here.</source>
<target state="translated">Vedi la documentazione qui.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">375</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">382</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">392</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">402</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">409</context>
</context-group>
<note priority="1" from="description">Discord API setting hint</note>
</trans-unit>
<trans-unit id="8c1bf02206fbc371ff69ab1b7e35a17ba29d169d" datatype="html">
<source>Use ntfy API</source>
<target state="translated">Utilizza API ntfy</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">386</context>
</context-group>
<note priority="1" from="description">Use ntfy API setting</note>
</trans-unit>
<trans-unit id="9aa62bf1a535a97a4d752bbc5cf1c31af0f0c1f7" datatype="html">
<source>Supports regex</source>
<target state="translated">Supporta espressioni regolari</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">10</context>
</context-group>
<note priority="1" from="description">Supports regex</note>
</trans-unit>
<trans-unit id="4665451070906079743" datatype="html">
<source>Favorited</source>
<target state="translated">Preferito</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">65</context>
</context-group>
</trans-unit>
<trans-unit id="7cedb649779673568447b994463b2882c4e0436a" datatype="html">
<source>Slack Webhook URL</source>
<target state="translated">URL webhook Slack</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">380</context>
</context-group>
<note priority="1" from="description">Slack Webhook URL</note>
</trans-unit>
<trans-unit id="b6399391e706e2d7b7b7880eb5630e4e6f49728c" datatype="html">
<source>Side</source>
<target state="translated">Laterale</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">35,37</context>
</context-group>
<note priority="1" from="description">Side</note>
</trans-unit>
<trans-unit id="144e1a21ebe8fa238f88d2ac27515ed711cfc9a0" datatype="html">
<source>Telegram chat ID</source>
<target state="translated">ID chat Telegram</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">424</context>
</context-group>
<note priority="1" from="description">Telegram chat ID</note>
</trans-unit>
<trans-unit id="c150a30bbbdb175b4d08820196a9acb66b167653" datatype="html">
<source>Archives empty</source>
<target state="translated">Archivi vuoti</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">72</context>
</context-group>
<note priority="1" from="description">Archives empty</note>
</trans-unit>
<trans-unit id="5947241266456580665" datatype="html">
<source>Download failed</source>
<target state="translated">Download non riuscito</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">18</context>
</context-group>
</trans-unit>
<trans-unit id="b78a98bc54259a29cf6250dbaeab5fe11fae91cf" datatype="html">
<source>Favorited</source>
<target state="translated">Preferito</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">51</context>
</context-group>
<note priority="1" from="description">Favorited</note>
</trans-unit>
<trans-unit id="51a161ce175abcd44f6c6cbd0e996681bf553ac3" datatype="html">
<source>Delete selected</source>
<target state="translated">Cancella selezionati</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">77</context>
</context-group>
<note priority="1" from="description">Delete selected</note>
</trans-unit>
<trans-unit id="35cf4cdcedc8ef3f94b6100e0d86836e31dbb908" datatype="html">
<source>Force autoplay</source>
<target state="translated">Forza l'auto-riproduzione</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">218</context>
</context-group>
<note priority="1" from="description">Force autoplay setting</note>
</trans-unit>
<trans-unit id="3640026747176198246" datatype="html">
<source>Watch content</source>
<target state="translated">Guarda contenuto</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">50</context>
</context-group>
</trans-unit>
<trans-unit id="a2f14a73f7a6e94479f67423cc51102da8d6f524" datatype="html">
<source>None</source>
<target state="translated">Nessuno</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">84</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">126</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">27</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">36</context>
</context-group>
<note priority="1" from="description">None</note>
</trans-unit>
<trans-unit id="06f503e492d6dbcf59e7b9c412ca86913d718689" datatype="html">
<source>ntfy topic URL</source>
<target state="translated">URL argomento ntfy</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">390</context>
</context-group>
<note priority="1" from="description">ntfy topic URL</note>
</trans-unit>
<trans-unit id="e08a77594f3d89311cdf6da5090044270909c194" datatype="html">
<source>User</source>
<target state="translated">Utente</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">25</context>
</context-group>
<note priority="1" from="description">User</note>
</trans-unit>
<trans-unit id="c41475a25c9f9d9639db9efa56637603a77528b4" datatype="html">
<source>Download archive</source>
<target state="translated">Scarica archivio</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">80</context>
</context-group>
<note priority="1" from="description">Download archive</note>
</trans-unit>
<trans-unit id="8224301330941792118" datatype="html">
<source>Failed to delete archive items!</source>
<target state="translated">Impossibile eliminare l'archivio/i!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">174</context>
</context-group>
</trans-unit>
<trans-unit id="33a7c6d5ff3515fa237f1fd4e43df8b65373954d" datatype="html">
<source>Enable all notifications</source>
<target state="translated">Abilita tutte le notifiche</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">352</context>
</context-group>
<note priority="1" from="description">Enable all notifications setting</note>
</trans-unit>
<trans-unit id="8425787787095143143" datatype="html">
<source>Would you like to delete <x id="selected archives amount" equiv-text="this.selection.selected.length"/> archive(s)?</source>
<target state="translated">Desideri eliminare <x id="selected archives amount" equiv-text="this.selection.selected.length"/> archivio/i?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">153</context>
</context-group>
</trans-unit>
<trans-unit id="11a0771f88158a540a54e0e4ec5d25733d65fc0e" datatype="html">
<source>Favorite</source>
<target state="translated">Preferito</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/unified-file-card/unified-file-card.component.html</context>
<context context-type="linenumber">26</context>
</context-group>
<note priority="1" from="description">Favorite button</note>
</trans-unit>
<trans-unit id="2525880134753073592" datatype="html">
<source>Successfully deleted archive items!</source>
<target state="translated">Archivio/i eliminato/i con successo!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">172</context>
</context-group>
</trans-unit>
<trans-unit id="c65dd978b3c7566551c0ebefb234c2d41942b847" datatype="html">
<source>Delete files older than</source>
<target state="translated">Elimina file più vecchi di</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">6</context>
</context-group>
<note priority="1" from="description">Delete files older than</note>
</trans-unit>
<trans-unit id="1698114086921246480" datatype="html">
<source>Unsubscribe</source>
<target state="translated">Annulla iscrizione</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.ts</context>
<context context-type="linenumber">32</context>
</context-group>
</trans-unit>
<trans-unit id="d57c023a4cf63b2f12c10328c15b636ff18929aa" datatype="html">
<source>Best</source>
<target state="translated">Migliore</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/main/main.component.html</context>
<context context-type="linenumber">24,25</context>
</context-group>
<note priority="1" from="description">Best</note>
</trans-unit>
<trans-unit id="c5dc5fbcce45e9b1530e2a5c2baa8ebe722aef4c" datatype="html">
<source>Download complete</source>
<target state="translated">Download completato</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">358</context>
</context-group>
<note priority="1" from="description">Download complete</note>
</trans-unit>
<trans-unit id="5709555629190115111" datatype="html">
<source>View task</source>
<target state="translated">Visualizza attività</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">33</context>
</context-group>
</trans-unit>
<trans-unit id="7410432243549869948" datatype="html">
<source>Duration</source>
<target state="translated">Durata</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">29</context>
</context-group>
</trans-unit>
<trans-unit id="7332320960988475089" datatype="html">
<source>Successfully deleted <x id="category name" equiv-text="category['name']"/>!</source>
<target state="translated">Categoria <x id="category name" equiv-text="category['name']"/> eliminata con successo!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">168</context>
</context-group>
</trans-unit>
<trans-unit id="8336047719608684263" datatype="html">
<source>Unsubscribe from <x id="subscription name" equiv-text="this.sub['name']"/></source>
<target state="translated">Annulla l'iscrizione da <x id="subscription name" equiv-text="this.sub['name']"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.ts</context>
<context context-type="linenumber">30</context>
</context-group>
</trans-unit>
<trans-unit id="3371159074051387771" datatype="html">
<source>Failed to delete <x id="category name" equiv-text="category['name']"/>!</source>
<target state="translated">Impossibile eliminare la categoria <x id="category name" equiv-text="category['name']"/>!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">172</context>
</context-group>
</trans-unit>
<trans-unit id="c748ac656af9f13998206ef2c52018dd418b0483" datatype="html">
<source>Archives</source>
<target state="translated">Archivi</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">26</context>
</context-group>
<note priority="1" from="description">Archives menu label</note>
</trans-unit>
<trans-unit id="8443034725057696949" datatype="html">
<source>Task finished</source>
<target state="translated">Attività completata</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">19</context>
</context-group>
</trans-unit>
<trans-unit id="2361a4f76caaa4574803fbcdca8b0a47c91cc7ed" datatype="html">
<source>Task finished</source>
<target state="translated">Attività completata</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">360</context>
</context-group>
<note priority="1" from="description">Task finished</note>
</trans-unit>
<trans-unit id="1a9b415816364f554ee411020e65219092655271" datatype="html">
<source>Title filter</source>
<target state="translated">Filtro titolo</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">8</context>
</context-group>
<note priority="1" from="description">Title filter</note>
</trans-unit>
<trans-unit id="e0a11fbea353b1ce1131161774e4a3e10bcb99b1" datatype="html">
<source>Large</source>
<target state="translated">Grande</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">44,46</context>
</context-group>
<note priority="1" from="description">Large</note>
</trans-unit>
<trans-unit id="674a999dd48d7da565ffdd105602261b8a4761ea" datatype="html">
<source>Download zip</source>
<target state="translated">Scarica zip</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/subscription/subscription/subscription.component.html</context>
<context context-type="linenumber">18</context>
</context-group>
<note priority="1" from="description">Download zip</note>
</trans-unit>
<trans-unit id="55f559d6f666b945479f534b0c182f70cd0a8a69" datatype="html">
<source>Gotify server URL</source>
<target state="translated">URL server Gotify</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">400</context>
</context-group>
<note priority="1" from="description">Gotify server URL</note>
</trans-unit>
<trans-unit id="9c562d26e041390ecc3f49dabc51cc50ebba7469" datatype="html">
<source>Allowed notification types</source>
<target state="translated">Tipi di notifiche consentiti</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">356</context>
</context-group>
<note priority="1" from="description">Allowed notification types</note>
</trans-unit>
<trans-unit id="7b4585a9072f3c1292972c14a3d0e14978fbfc9c" datatype="html">
<source>Delete old files:</source>
<target state="translated">Elimina vecchi file:</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/tasks/tasks.component.html</context>
<context context-type="linenumber">66</context>
</context-group>
<note priority="1" from="description">Delete old files</note>
</trans-unit>
<trans-unit id="6219551536751479443" datatype="html">
<source>Finished downloading</source>
<target state="translated">Download completato</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
<trans-unit id="6268070779441507380" datatype="html">
<source>Download Date</source>
<target state="translated">Data download</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit id="2481374649045841364" datatype="html">
<source>Would you like to delete <x id="category name" equiv-text="category['name']"/>?</source>
<target state="translated">Desideri eliminare <x id="category name" equiv-text="category['name']"/>?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">159</context>
</context-group>
</trans-unit>
<trans-unit id="eeb0ba2e4743901d8f5eebd9a3529aa1f236c608" datatype="html">
<source>Create bot here.</source>
<target state="translated">Crea il bot qui.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">419</context>
</context-group>
<note priority="1" from="description">Telegram bot create link</note>
</trans-unit>
<trans-unit id="6437411876967154040" datatype="html">
<source>Audio only</source>
<target state="translated">Solo audio</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">60</context>
</context-group>
</trans-unit>
<trans-unit id="6080b77234e92ad41bb52653b239c4c4f851317d" datatype="html">
<source>Error</source>
<target state="translated">Errore</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">39</context>
</context-group>
<note priority="1" from="description">Error</note>
</trans-unit>
<trans-unit id="8456659390937171831" datatype="html">
<source>Show error</source>
<target state="translated">Mostra errore</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>
<trans-unit id="4b3972c3e9485218508a95f7a4ce7758e3f09ced" datatype="html">
<source>Upload</source>
<target state="translated">Carica</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">137</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.html</context>
<context context-type="linenumber">30</context>
</context-group>
<note priority="1" from="description">Upload</note>
</trans-unit>
<trans-unit id="37469c9f3e31d95087fa22b6c9c3bc64adf1692d" datatype="html">
<source>Enable RSS Feed</source>
<target state="translated">Abilita feed RSS</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">271</context>
</context-group>
<note priority="1" from="description">Enable RSS Feed setting</note>
</trans-unit>
<trans-unit id="1236604279860679031" datatype="html">
<source>Restart</source>
<target state="translated">Riavvia</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">62</context>
</context-group>
</trans-unit>
<trans-unit id="8bcabdf6b16cad0313a86c7e940c5e3ad7f9f8ab" datatype="html">
<source>Notifications</source>
<target state="translated">Notifiche</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">343</context>
</context-group>
<note priority="1" from="description">Notifications settings label</note>
</trans-unit>
<trans-unit id="784837056777689544" datatype="html">
<source>Would you like to unsubscribe from <x id="subscription name" equiv-text="this.sub['name']"/>?</source>
<target state="translated">Desideri annullare l'iscrizione da <x id="subscription name" equiv-text="this.sub['name']"/>?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.ts</context>
<context context-type="linenumber">31</context>
</context-group>
</trans-unit>
<trans-unit id="1091872159779006651" datatype="html">
<source>You must input a time!</source>
<target state="translated">Devi inserire un tempo!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/update-task-schedule-dialog/update-task-schedule-dialog.component.ts</context>
<context context-type="linenumber">70</context>
</context-group>
</trans-unit>
<trans-unit id="0ed98b4c6ec1db6708a963e8a2699478ac97f55c" datatype="html">
<source>Add subscription</source>
<target state="translated">Aggiungi abbonamento</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/subscriptions/subscriptions.component.html</context>
<context context-type="linenumber">60</context>
</context-group>
<note priority="1" from="description">Add subscription</note>
</trans-unit>
<trans-unit id="61d6b5fa4311b1c617b66dad72496f9dd43b07b4" datatype="html">
<source>Be careful enabling this with multi-user mode! User data may be exposed.</source>
<target state="translated">Attenzione all'abilitazione in modalità multiutente! I dati dell'utente potrebbero essere esposti.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">272</context>
</context-group>
<note priority="1" from="description">RSS Feed prefix</note>
</trans-unit>
<trans-unit id="3533826530554274875" datatype="html">
<source>Upload Date</source>
<target state="translated">Data upload</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
<trans-unit id="347407180135731058" datatype="html">
<source>Audio</source>
<target state="translated">Audio</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">44</context>
</context-group>
</trans-unit>
<trans-unit id="cdf5297d8d080a78e8b10debc5c38b7845a3cbe7" datatype="html">
<source>Do not ask for confirmation</source>
<target state="translated">Non chiedere conferma</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">19</context>
</context-group>
<note priority="1" from="description">Do not ask for confirmation</note>
</trans-unit>
<trans-unit id="c2faa86201eab08b5b39b5437f96ab9432e125e7" datatype="html">
<source>Item limit</source>
<target state="translated">Limite elementi</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">46</context>
</context-group>
<note priority="1" from="description">Item limit</note>
</trans-unit>
<trans-unit id="7022070615528435141" datatype="html">
<source>Delete</source>
<target state="translated">Elimina</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">154</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">160</context>
</context-group>
</trans-unit>
<trans-unit id="9e766e11a9de375907aaf566897ecc6dac393ebc" datatype="html">
<source>Webhook URL</source>
<target state="translated">URL webhook</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">366</context>
</context-group>
<note priority="1" from="description">webhook URL</note>
</trans-unit>
<trans-unit id="2e076ff9866213d0815961c494aa48b177046b9d" datatype="html">
<source>Telegram bot token</source>
<target state="translated">Token bot Telegram</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">417</context>
</context-group>
<note priority="1" from="description">Telegram bot token</note>
</trans-unit>
<trans-unit id="d618f383a0ea2458eeb945a85190d4a002ea394b" datatype="html">
<source>Arg</source>
<target state="translated">Argomento</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html</context>
<context context-type="linenumber">41</context>
</context-group>
<note priority="1" from="description">Arg</note>
</trans-unit>
<trans-unit id="1879058637439215882" datatype="html">
<source>Download error</source>
<target state="translated">Errore nel download</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">27</context>
</context-group>
</trans-unit>
<trans-unit id="8953483585652369683" datatype="html">
<source>Archive successfully imported!</source>
<target state="translated">Archivio importato con successo!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">130</context>
</context-group>
</trans-unit>
<trans-unit id="7911845622864460134" datatype="html">
<source>Video only</source>
<target state="translated">Solo video</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">55</context>
</context-group>
</trans-unit>
<trans-unit id="38992954440d6afb54aeb58af12ca0123ee5e26e" datatype="html">
<source>Use Telegram API</source>
<target state="translated">Utilizza API Telegram</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">413</context>
</context-group>
<note priority="1" from="description">Use Telegram API setting</note>
</trans-unit>
<trans-unit id="6876310993601590130" datatype="html">
<source>Download completed</source>
<target state="translated">Download completato</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">23</context>
</context-group>
</trans-unit>
<trans-unit id="4578192247039196794" datatype="html">
<source>Task</source>
<target state="translated">Attività</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">31</context>
</context-group>
</trans-unit>
<trans-unit id="5ac5a0e5ffe8e5623b40696f4c2403c17349271f" datatype="html">
<source>Sidepanel mode</source>
<target state="translated">Modalità pannello laterale</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">30</context>
</context-group>
<note priority="1" from="description">Sidepanel mode</note>
</trans-unit>
<trans-unit id="8571838164752006148" datatype="html">
<source>View error</source>
<target state="translated">Visualizza errore</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">32</context>
</context-group>
</trans-unit>
<trans-unit id="9aa1b4779a515170b297d2c0507e6ff9d2e3e0e0" datatype="html">
<source>Blacklist deleted subscription files</source>
<target state="translated">Metti nella lista nera i file di abbonamenti eliminati</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">14</context>
</context-group>
<note priority="1" from="description">Blacklist deleted subscription files</note>
</trans-unit>
<trans-unit id="9042260521669277115" datatype="html">
<source>Pause</source>
<target state="translated">Metti in pausa</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">68</context>
</context-group>
</trans-unit>
<trans-unit id="45cc8ca94b5a50842a9a8ef804a5ab089a38ae5c" datatype="html">
<source>ID</source>
<target state="translated">ID</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">47</context>
</context-group>
<note priority="1" from="description">ID</note>
</trans-unit>
<trans-unit id="8c6e24eab969d9f63a8a0e9d617aee3b99e28ae6" datatype="html">
<source>Play all</source>
<target state="translated">Riproduci tutto</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/subscription/subscription/subscription.component.html</context>
<context context-type="linenumber">17</context>
</context-group>
<note priority="1" from="description">Play all</note>
</trans-unit>
<trans-unit id="3e420c675b8f3f3702576d52e8bb6e8e1d3feda0" datatype="html">
<source>How do I get the chat ID?</source>
<target state="translated">Come posso ottenere l'ID chat?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">426</context>
</context-group>
<note priority="1" from="description">Telegram chat ID help</note>
</trans-unit>
<trans-unit id="7182974689040833178" datatype="html">
<source>Resume</source>
<target state="translated">Riprendi</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">74</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">80</context>
</context-group>
</trans-unit>
<trans-unit id="8564202903947049539" datatype="html">
<source>Play</source>
<target state="translated">Riproduci</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">30</context>
</context-group>
</trans-unit>
<trans-unit id="a4ed8eba1e057e67d5c2d87b52230f182b3dae4e" datatype="html">
<source>Restart required.</source>
<target state="translated">Riavvio richiesto.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">446</context>
</context-group>
<note priority="1" from="description">Restart required hint</note>
</trans-unit>
<trans-unit id="3159807825117518005" datatype="html">
<source>Delete archives</source>
<target state="translated">Elimina archivi</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">152</context>
</context-group>
</trans-unit>
<trans-unit id="c35ef0f03a863d33b04aae6807f140397a50f491" datatype="html">
<source>Generate RSS URL</source>
<target state="translated">Genera URL RSS</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">273</context>
</context-group>
<note priority="1" from="description">Generate RSS URL</note>
</trans-unit>
<trans-unit id="8953033926734869941" datatype="html">
<source>Name</source>
<target state="translated">Nome</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">21</context>
</context-group>
</trans-unit>
<trans-unit id="5ca707824ab93066c7d9b44e1b8bf216725c2c22" datatype="html">
<source>Filter</source>
<target state="translated">Filtra</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
<note priority="1" from="description">Filter</note>
</trans-unit>
<trans-unit id="c40370dc182b5e4333828b70f7478bde58bb5cfe" datatype="html">
<source>Enable notifications</source>
<target state="translated">Abilita notifiche</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">349</context>
</context-group>
<note priority="1" from="description">Enable notifications setting</note>
</trans-unit>
<trans-unit id="2492098975665776610" datatype="html">
<source>File Size</source>
<target state="translated">Dimensione file</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">25</context>
</context-group>
</trans-unit>
<trans-unit id="af30e51aa8b67e1133a341ec28359be05150e65c" datatype="html">
<source>No description available.</source>
<target state="translated">Nessuna descrizione disponibile.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/player/player.component.html</context>
<context context-type="linenumber">25,27</context>
</context-group>
<note priority="1" from="description">No description</note>
</trans-unit>
<trans-unit id="fd467148a18f0921c10d116d4e0174fe29452be4" datatype="html">
<source>See documentation here.</source>
<target state="translated">Vedi la documentazione qui.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">274</context>
</context-group>
<note priority="1" from="description">RSS feed documentation</note>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

File diff suppressed because it is too large Load Diff

View File

@@ -4142,6 +4142,963 @@
</context-group> </context-group>
<note priority="1" from="description">Video resolution property</note> <note priority="1" from="description">Video resolution property</note>
</trans-unit> </trans-unit>
<trans-unit id="a2f14a73f7a6e94479f67423cc51102da8d6f524" datatype="html">
<source>None</source>
<target state="translated">Geen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">84</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">126</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">27</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">36</context>
</context-group>
<note priority="1" from="description">None</note>
</trans-unit>
<trans-unit id="5ca707824ab93066c7d9b44e1b8bf216725c2c22" datatype="html">
<source>Filter</source>
<target state="translated">Filter</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
<note priority="1" from="description">Filter</note>
</trans-unit>
<trans-unit id="6080b77234e92ad41bb52653b239c4c4f851317d" datatype="html">
<source>Error</source>
<target state="translated">Fout</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">39</context>
</context-group>
<note priority="1" from="description">Error</note>
</trans-unit>
<trans-unit id="3640026747176198246" datatype="html">
<source>Watch content</source>
<target state="translated">Inhoud bekijken</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">50</context>
</context-group>
</trans-unit>
<trans-unit id="c150a30bbbdb175b4d08820196a9acb66b167653" datatype="html">
<source>Archives empty</source>
<target state="translated">Archieven leeg</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">72</context>
</context-group>
<note priority="1" from="description">Archives empty</note>
</trans-unit>
<trans-unit id="8571838164752006148" datatype="html">
<source>View error</source>
<target state="translated">Toon foutmelding</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">32</context>
</context-group>
</trans-unit>
<trans-unit id="5709555629190115111" datatype="html">
<source>View task</source>
<target state="translated">Toon taak</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">33</context>
</context-group>
</trans-unit>
<trans-unit id="b78a98bc54259a29cf6250dbaeab5fe11fae91cf" datatype="html">
<source>Favorited</source>
<target state="translated">Favorieten</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">51</context>
</context-group>
<note priority="1" from="description">Favorited</note>
</trans-unit>
<trans-unit id="5ac5a0e5ffe8e5623b40696f4c2403c17349271f" datatype="html">
<source>Sidepanel mode</source>
<target state="translated">Zijpaneel-modus</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">30</context>
</context-group>
<note priority="1" from="description">Sidepanel mode</note>
</trans-unit>
<trans-unit id="8c1bf02206fbc371ff69ab1b7e35a17ba29d169d" datatype="html">
<source>Use ntfy API</source>
<target state="translated">Gebruik ntfy API</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">386</context>
</context-group>
<note priority="1" from="description">Use ntfy API setting</note>
</trans-unit>
<trans-unit id="0ed98b4c6ec1db6708a963e8a2699478ac97f55c" datatype="html">
<source>Add subscription</source>
<target state="translated">Abonnement toevoegen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/subscriptions/subscriptions.component.html</context>
<context context-type="linenumber">60</context>
</context-group>
<note priority="1" from="description">Add subscription</note>
</trans-unit>
<trans-unit id="9a865c2922f5c01899d06c472dba2e5bd63bcff9" datatype="html">
<source>Small</source>
<target state="translated">Klein</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">50,52</context>
</context-group>
<note priority="1" from="description">Small</note>
</trans-unit>
<trans-unit id="c748ac656af9f13998206ef2c52018dd418b0483" datatype="html">
<source>Archives</source>
<target state="translated">Archieven</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">26</context>
</context-group>
<note priority="1" from="description">Archives menu label</note>
</trans-unit>
<trans-unit id="45cc8ca94b5a50842a9a8ef804a5ab089a38ae5c" datatype="html">
<source>ID</source>
<target state="translated">ID</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">47</context>
</context-group>
<note priority="1" from="description">ID</note>
</trans-unit>
<trans-unit id="28da11220a3377ddce3c7948825d33101f142782" datatype="html">
<source>Extractor</source>
<target state="translated">Extractor</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">57</context>
</context-group>
<note priority="1" from="description">Extractor</note>
</trans-unit>
<trans-unit id="51a161ce175abcd44f6c6cbd0e996681bf553ac3" datatype="html">
<source>Delete selected</source>
<target state="translated">Verwijder geselecteerde</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">77</context>
</context-group>
<note priority="1" from="description">Delete selected</note>
</trans-unit>
<trans-unit id="c41475a25c9f9d9639db9efa56637603a77528b4" datatype="html">
<source>Download archive</source>
<target state="translated">Archief downloaden</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">80</context>
</context-group>
<note priority="1" from="description">Download archive</note>
</trans-unit>
<trans-unit id="4b3972c3e9485218508a95f7a4ce7758e3f09ced" datatype="html">
<source>Upload</source>
<target state="translated">Uploaden</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">137</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.html</context>
<context context-type="linenumber">30</context>
</context-group>
<note priority="1" from="description">Upload</note>
</trans-unit>
<trans-unit id="6549265851868599441" datatype="html">
<source>Video</source>
<target state="translated">Video</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">40</context>
</context-group>
</trans-unit>
<trans-unit id="347407180135731058" datatype="html">
<source>Audio</source>
<target state="translated">Audio</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">44</context>
</context-group>
</trans-unit>
<trans-unit id="8953483585652369683" datatype="html">
<source>Archive successfully imported!</source>
<target state="translated">Archief succesvol geïmporteerd!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">130</context>
</context-group>
</trans-unit>
<trans-unit id="3159807825117518005" datatype="html">
<source>Delete archives</source>
<target state="translated">Archieven verwijderen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">152</context>
</context-group>
</trans-unit>
<trans-unit id="8425787787095143143" datatype="html">
<source>Would you like to delete <x id="selected archives amount" equiv-text="this.selection.selected.length"/> archive(s)?</source>
<target state="translated">Wilt u <x id="selected archives amount" equiv-text="this.selection.selected.length"/> archieven verwijderen?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">153</context>
</context-group>
</trans-unit>
<trans-unit id="7022070615528435141" datatype="html">
<source>Delete</source>
<target state="translated">Verwijderen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">154</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">160</context>
</context-group>
</trans-unit>
<trans-unit id="2525880134753073592" datatype="html">
<source>Successfully deleted archive items!</source>
<target state="translated">Archiefstukken succesvol verwijderd!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">172</context>
</context-group>
</trans-unit>
<trans-unit id="8224301330941792118" datatype="html">
<source>Failed to delete archive items!</source>
<target state="translated">Archiefstukken verwijderen mislukt!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">174</context>
</context-group>
</trans-unit>
<trans-unit id="8443034725057696949" datatype="html">
<source>Task finished</source>
<target state="translated">Taak voltooid</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">19</context>
</context-group>
</trans-unit>
<trans-unit id="6219551536751479443" datatype="html">
<source>Finished downloading</source>
<target state="translated">Downloaden voltooid</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
<trans-unit id="5947241266456580665" datatype="html">
<source>Download failed</source>
<target state="translated">Download mislukt</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">18</context>
</context-group>
</trans-unit>
<trans-unit id="8564202903947049539" datatype="html">
<source>Play</source>
<target state="translated">Afspelen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">30</context>
</context-group>
</trans-unit>
<trans-unit id="8643601595923420698" datatype="html">
<source>Retry download</source>
<target state="translated">Download opnieuw proberen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">31</context>
</context-group>
</trans-unit>
<trans-unit id="1879058637439215882" datatype="html">
<source>Download error</source>
<target state="translated">Download fout</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">27</context>
</context-group>
</trans-unit>
<trans-unit id="4578192247039196794" datatype="html">
<source>Task</source>
<target state="translated">Taak</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">31</context>
</context-group>
</trans-unit>
<trans-unit id="5a105e7bd7e7db6ea211fe950fc9f317379acceb" datatype="html">
<source>No notifications available</source>
<target state="translated">Geen notificaties beschikbaar</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
<note priority="1" from="description">No notifications available</note>
</trans-unit>
<trans-unit id="6876310993601590130" datatype="html">
<source>Download completed</source>
<target state="translated">Download voltooid</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">23</context>
</context-group>
</trans-unit>
<trans-unit id="6437411876967154040" datatype="html">
<source>Audio only</source>
<target state="translated">Alleen audio</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">60</context>
</context-group>
</trans-unit>
<trans-unit id="7911845622864460134" datatype="html">
<source>Video only</source>
<target state="translated">Alleen video</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">55</context>
</context-group>
</trans-unit>
<trans-unit id="4665451070906079743" datatype="html">
<source>Favorited</source>
<target state="translated">Favoriet</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">65</context>
</context-group>
</trans-unit>
<trans-unit id="6268070779441507380" datatype="html">
<source>Download Date</source>
<target state="translated">Downloaddatum</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit id="8953033926734869941" datatype="html">
<source>Name</source>
<target state="translated">Naam</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">21</context>
</context-group>
</trans-unit>
<trans-unit id="2492098975665776610" datatype="html">
<source>File Size</source>
<target state="translated">Bestandsgrootte</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">25</context>
</context-group>
</trans-unit>
<trans-unit id="7410432243549869948" datatype="html">
<source>Duration</source>
<target state="translated">Duur</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">29</context>
</context-group>
</trans-unit>
<trans-unit id="c65dd978b3c7566551c0ebefb234c2d41942b847" datatype="html">
<source>Delete files older than</source>
<target state="translated">Verwijder bestanden ouder dan</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">6</context>
</context-group>
<note priority="1" from="description">Delete files older than</note>
</trans-unit>
<trans-unit id="3533826530554274875" datatype="html">
<source>Upload Date</source>
<target state="translated">Uploaddatum</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
<trans-unit id="9aa1b4779a515170b297d2c0507e6ff9d2e3e0e0" datatype="html">
<source>Blacklist deleted subscription files</source>
<target state="translated">Verwijderde abonnementsbestanden op de zwarte lijst zetten</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">14</context>
</context-group>
<note priority="1" from="description">Blacklist deleted subscription files</note>
</trans-unit>
<trans-unit id="cdf5297d8d080a78e8b10debc5c38b7845a3cbe7" datatype="html">
<source>Do not ask for confirmation</source>
<target state="translated">Niet om bevestiging vragen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">19</context>
</context-group>
<note priority="1" from="description">Do not ask for confirmation</note>
</trans-unit>
<trans-unit id="56b1a3c93fb95fed1805005c561a5e431d57ffae" datatype="html">
<source>Blacklist all files</source>
<target state="translated">Alle bestanden op de zwarte lijst zetten</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">11</context>
</context-group>
<note priority="1" from="description">Blacklist deleted files</note>
</trans-unit>
<trans-unit id="9176960997786930103" datatype="html">
<source>Error for: <x id="PH" equiv-text="task['title']"/></source>
<target state="translated">Fout voor: <x id="PH" equiv-text="task['title']"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/tasks/tasks.component.ts</context>
<context context-type="linenumber">174</context>
</context-group>
</trans-unit>
<trans-unit id="11a0771f88158a540a54e0e4ec5d25733d65fc0e" datatype="html">
<source>Favorite</source>
<target state="translated">Favoriet</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/unified-file-card/unified-file-card.component.html</context>
<context context-type="linenumber">26</context>
</context-group>
<note priority="1" from="description">Favorite button</note>
</trans-unit>
<trans-unit id="1d4fa01d25990f60abf21c3a451fa8ba262b7912" datatype="html">
<source>Unfavorite</source>
<target state="translated">Verwijder uit favorieten</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/unified-file-card/unified-file-card.component.html</context>
<context context-type="linenumber">27</context>
</context-group>
<note priority="1" from="description">Unfavorite button</note>
</trans-unit>
<trans-unit id="1f2809e6a99d511fdb6eaf041d785fe54d0680cc" datatype="html">
<source>File card size</source>
<target state="translated">Bestandskaart grootte</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">42</context>
</context-group>
<note priority="1" from="description">File card size</note>
</trans-unit>
<trans-unit id="d618f383a0ea2458eeb945a85190d4a002ea394b" datatype="html">
<source>Arg</source>
<target state="translated">Optie</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html</context>
<context context-type="linenumber">41</context>
</context-group>
<note priority="1" from="description">Arg</note>
</trans-unit>
<trans-unit id="e08a77594f3d89311cdf6da5090044270909c194" datatype="html">
<source>User</source>
<target state="translated">Gebruiker</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">25</context>
</context-group>
<note priority="1" from="description">User</note>
</trans-unit>
<trans-unit id="c35ef0f03a863d33b04aae6807f140397a50f491" datatype="html">
<source>Generate RSS URL</source>
<target state="translated">RSS-URL genereren</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">273</context>
</context-group>
<note priority="1" from="description">Generate RSS URL</note>
</trans-unit>
<trans-unit id="35cf4cdcedc8ef3f94b6100e0d86836e31dbb908" datatype="html">
<source>Force autoplay</source>
<target state="translated">Automatisch afspelen forceren</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">218</context>
</context-group>
<note priority="1" from="description">Force autoplay setting</note>
</trans-unit>
<trans-unit id="61d6b5fa4311b1c617b66dad72496f9dd43b07b4" datatype="html">
<source>Be careful enabling this with multi-user mode! User data may be exposed.</source>
<target state="translated">Wees voorzichtig als je dit aanzet samen met de modus voor meerdere gebruikers! Gebruikersdata kunnen blootgesteld worden.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">272</context>
</context-group>
<note priority="1" from="description">RSS Feed prefix</note>
</trans-unit>
<trans-unit id="fd467148a18f0921c10d116d4e0174fe29452be4" datatype="html">
<source>See documentation here.</source>
<target state="translated">Zie documentatie hier.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">274</context>
</context-group>
<note priority="1" from="description">RSS feed documentation</note>
</trans-unit>
<trans-unit id="9c562d26e041390ecc3f49dabc51cc50ebba7469" datatype="html">
<source>Allowed notification types</source>
<target state="translated">Toegestane typen notificaties</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">356</context>
</context-group>
<note priority="1" from="description">Allowed notification types</note>
</trans-unit>
<trans-unit id="33a7c6d5ff3515fa237f1fd4e43df8b65373954d" datatype="html">
<source>Enable all notifications</source>
<target state="translated">Alle notificaties inschakelen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">352</context>
</context-group>
<note priority="1" from="description">Enable all notifications setting</note>
</trans-unit>
<trans-unit id="9e766e11a9de375907aaf566897ecc6dac393ebc" datatype="html">
<source>Webhook URL</source>
<target state="translated">Webhook URL</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">366</context>
</context-group>
<note priority="1" from="description">webhook URL</note>
</trans-unit>
<trans-unit id="55f559d6f666b945479f534b0c182f70cd0a8a69" datatype="html">
<source>Gotify server URL</source>
<target state="translated">Gotify server URL</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">400</context>
</context-group>
<note priority="1" from="description">Gotify server URL</note>
</trans-unit>
<trans-unit id="eeb0ba2e4743901d8f5eebd9a3529aa1f236c608" datatype="html">
<source>Create bot here.</source>
<target state="translated">Bot hier maken.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">419</context>
</context-group>
<note priority="1" from="description">Telegram bot create link</note>
</trans-unit>
<trans-unit id="8c6e24eab969d9f63a8a0e9d617aee3b99e28ae6" datatype="html">
<source>Play all</source>
<target state="translated">Alles afspelen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/subscription/subscription/subscription.component.html</context>
<context context-type="linenumber">17</context>
</context-group>
<note priority="1" from="description">Play all</note>
</trans-unit>
<trans-unit id="7cedb649779673568447b994463b2882c4e0436a" datatype="html">
<source>Slack Webhook URL</source>
<target state="translated">Slack Webhook URL</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">380</context>
</context-group>
<note priority="1" from="description">Slack Webhook URL</note>
</trans-unit>
<trans-unit id="a4ed8eba1e057e67d5c2d87b52230f182b3dae4e" datatype="html">
<source>Restart required.</source>
<target state="translated">Opnieuw starten vereist.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">446</context>
</context-group>
<note priority="1" from="description">Restart required hint</note>
</trans-unit>
<trans-unit id="3e420c675b8f3f3702576d52e8bb6e8e1d3feda0" datatype="html">
<source>How do I get the chat ID?</source>
<target state="translated">Hoe kan ik het chat ID krijgen?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">426</context>
</context-group>
<note priority="1" from="description">Telegram chat ID help</note>
</trans-unit>
<trans-unit id="8456659390937171831" datatype="html">
<source>Show error</source>
<target state="translated">Toon fout</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>
<trans-unit id="1236604279860679031" datatype="html">
<source>Restart</source>
<target state="translated">Herstarten</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">62</context>
</context-group>
</trans-unit>
<trans-unit id="9042260521669277115" datatype="html">
<source>Pause</source>
<target state="translated">Pauzeren</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">68</context>
</context-group>
</trans-unit>
<trans-unit id="7182974689040833178" datatype="html">
<source>Resume</source>
<target state="translated">Hervatten</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">74</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">80</context>
</context-group>
</trans-unit>
<trans-unit id="019d4bd6a5690f0cfa0ecf346a4e6bf7f0d8debb" datatype="html">
<source>Remove</source>
<target state="translated">Verwijderen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.html</context>
<context context-type="linenumber">23</context>
</context-group>
<note priority="1" from="description">Remove</note>
</trans-unit>
<trans-unit id="5000203534763292992" datatype="html">
<source>Download restarted!</source>
<target state="translated">Download herstart!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">72</context>
</context-group>
</trans-unit>
<trans-unit id="ea2b65121b93921fe54692025da9b9e3ce779ad5" datatype="html">
<source>Task settings - <x id="INTERPOLATION" equiv-text="{{task.title}}"/></source>
<target state="translated">Taakinstellingen - <x id="INTERPOLATION" equiv-text="{{task.title}}"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
<note priority="1" from="description">Task settings</note>
</trans-unit>
<trans-unit id="7b4585a9072f3c1292972c14a3d0e14978fbfc9c" datatype="html">
<source>Delete old files:</source>
<target state="translated">Verwijder oude bestanden:</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/tasks/tasks.component.html</context>
<context context-type="linenumber">66</context>
</context-group>
<note priority="1" from="description">Delete old files</note>
</trans-unit>
<trans-unit id="1a9b415816364f554ee411020e65219092655271" datatype="html">
<source>Title filter</source>
<target state="translated">Titelfilter</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">8</context>
</context-group>
<note priority="1" from="description">Title filter</note>
</trans-unit>
<trans-unit id="9aa62bf1a535a97a4d752bbc5cf1c31af0f0c1f7" datatype="html">
<source>Supports regex</source>
<target state="translated">Ondersteunt regex</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">10</context>
</context-group>
<note priority="1" from="description">Supports regex</note>
</trans-unit>
<trans-unit id="c2faa86201eab08b5b39b5437f96ab9432e125e7" datatype="html">
<source>Item limit</source>
<target state="translated">Item limiet</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">46</context>
</context-group>
<note priority="1" from="description">Item limit</note>
</trans-unit>
<trans-unit id="8336047719608684263" datatype="html">
<source>Unsubscribe from <x id="subscription name" equiv-text="this.sub['name']"/></source>
<target state="translated">Afmelden van <x id="subscription name" equiv-text="this.sub['name']"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.ts</context>
<context context-type="linenumber">30</context>
</context-group>
</trans-unit>
<trans-unit id="784837056777689544" datatype="html">
<source>Would you like to unsubscribe from <x id="subscription name" equiv-text="this.sub['name']"/>?</source>
<target state="translated">Wil je afmelden van <x id="subscription name" equiv-text="this.sub['name']"/>?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.ts</context>
<context context-type="linenumber">31</context>
</context-group>
</trans-unit>
<trans-unit id="1698114086921246480" datatype="html">
<source>Unsubscribe</source>
<target state="translated">Afmelden</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.ts</context>
<context context-type="linenumber">32</context>
</context-group>
</trans-unit>
<trans-unit id="1091872159779006651" datatype="html">
<source>You must input a time!</source>
<target state="translated">Je moet een tijd invoeren!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/update-task-schedule-dialog/update-task-schedule-dialog.component.ts</context>
<context context-type="linenumber">70</context>
</context-group>
</trans-unit>
<trans-unit id="338b44701a53ce3ef2f36abfb56f89c3edfa9eab" datatype="html">
<source>Over</source>
<target state="translated">Over</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">32,34</context>
</context-group>
<note priority="1" from="description">Over</note>
</trans-unit>
<trans-unit id="b6399391e706e2d7b7b7880eb5630e4e6f49728c" datatype="html">
<source>Side</source>
<target state="translated">Zijkant</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">35,37</context>
</context-group>
<note priority="1" from="description">Side</note>
</trans-unit>
<trans-unit id="e0a11fbea353b1ce1131161774e4a3e10bcb99b1" datatype="html">
<source>Large</source>
<target state="translated">Groot</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">44,46</context>
</context-group>
<note priority="1" from="description">Large</note>
</trans-unit>
<trans-unit id="d57c023a4cf63b2f12c10328c15b636ff18929aa" datatype="html">
<source>Best</source>
<target state="translated">Beste</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/main/main.component.html</context>
<context context-type="linenumber">24,25</context>
</context-group>
<note priority="1" from="description">Best</note>
</trans-unit>
<trans-unit id="af30e51aa8b67e1133a341ec28359be05150e65c" datatype="html">
<source>No description available.</source>
<target state="translated">Geen beschrijving beschikbaar.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/player/player.component.html</context>
<context context-type="linenumber">25,27</context>
</context-group>
<note priority="1" from="description">No description</note>
</trans-unit>
<trans-unit id="c40370dc182b5e4333828b70f7478bde58bb5cfe" datatype="html">
<source>Enable notifications</source>
<target state="translated">Notificaties inschakelen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">349</context>
</context-group>
<note priority="1" from="description">Enable notifications setting</note>
</trans-unit>
<trans-unit id="8bcabdf6b16cad0313a86c7e940c5e3ad7f9f8ab" datatype="html">
<source>Notifications</source>
<target state="translated">Notificaties</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">343</context>
</context-group>
<note priority="1" from="description">Notifications settings label</note>
</trans-unit>
<trans-unit id="c5dc5fbcce45e9b1530e2a5c2baa8ebe722aef4c" datatype="html">
<source>Download complete</source>
<target state="translated">Download voltooid</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">358</context>
</context-group>
<note priority="1" from="description">Download complete</note>
</trans-unit>
<trans-unit id="3ffd9490f3a4c0b24021d25e1dc71fcfe5d39cd6" datatype="html">
<source>Download error</source>
<target state="translated">Downloadfout</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">359</context>
</context-group>
<note priority="1" from="description">Download error</note>
</trans-unit>
<trans-unit id="2361a4f76caaa4574803fbcdca8b0a47c91cc7ed" datatype="html">
<source>Task finished</source>
<target state="translated">Taak voltooid</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">360</context>
</context-group>
<note priority="1" from="description">Task finished</note>
</trans-unit>
<trans-unit id="3264d82792954815be755b3da01e2625458711dc" datatype="html">
<source>Discord Webhook URL</source>
<target state="translated">Discord Webhook URL</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">373</context>
</context-group>
<note priority="1" from="description">Discord Webhook URL</note>
</trans-unit>
<trans-unit id="0cfc9cfe7cd8ea14bc053693b28872da739af02c" datatype="html">
<source>See docs here.</source>
<target state="translated">Zie documentatie hier.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">375</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">382</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">392</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">402</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">409</context>
</context-group>
<note priority="1" from="description">Discord API setting hint</note>
</trans-unit>
<trans-unit id="06f503e492d6dbcf59e7b9c412ca86913d718689" datatype="html">
<source>ntfy topic URL</source>
<target state="translated">ntfy onderwerp URL</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">390</context>
</context-group>
<note priority="1" from="description">ntfy topic URL</note>
</trans-unit>
<trans-unit id="5827fde8fcafdd55ae80921ad3ad4aa01012e203" datatype="html">
<source>Use gotify API</source>
<target state="translated">Gebruik gotify API</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">396</context>
</context-group>
<note priority="1" from="description">Use gotify API setting</note>
</trans-unit>
<trans-unit id="b770c48628d98cb4633d6a17e3f0ba0265376af5" datatype="html">
<source>Gotify app token</source>
<target state="translated">Gotify app token</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">407</context>
</context-group>
<note priority="1" from="description">Gotify app token</note>
</trans-unit>
<trans-unit id="38992954440d6afb54aeb58af12ca0123ee5e26e" datatype="html">
<source>Use Telegram API</source>
<target state="translated">Telegram API gebruiken</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">413</context>
</context-group>
<note priority="1" from="description">Use Telegram API setting</note>
</trans-unit>
<trans-unit id="2e076ff9866213d0815961c494aa48b177046b9d" datatype="html">
<source>Telegram bot token</source>
<target state="translated">Telegram bot token</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">417</context>
</context-group>
<note priority="1" from="description">Telegram bot token</note>
</trans-unit>
<trans-unit id="144e1a21ebe8fa238f88d2ac27515ed711cfc9a0" datatype="html">
<source>Telegram chat ID</source>
<target state="translated">Telegram chat ID</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">424</context>
</context-group>
<note priority="1" from="description">Telegram chat ID</note>
</trans-unit>
<trans-unit id="6785427850041119037" datatype="html">
<source>Delete category</source>
<target state="translated">Categorie verwijderen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">158</context>
</context-group>
</trans-unit>
<trans-unit id="2481374649045841364" datatype="html">
<source>Would you like to delete <x id="category name" equiv-text="category['name']"/>?</source>
<target state="translated">Wil je <x id="category name" equiv-text="category['name']"/> verwijderen?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">159</context>
</context-group>
</trans-unit>
<trans-unit id="7332320960988475089" datatype="html">
<source>Successfully deleted <x id="category name" equiv-text="category['name']"/>!</source>
<target state="translated"><x id="category name" equiv-text="category['name']"/> is succesvol verwijderd!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">168</context>
</context-group>
</trans-unit>
<trans-unit id="3371159074051387771" datatype="html">
<source>Failed to delete <x id="category name" equiv-text="category['name']"/>!</source>
<target state="translated"><x id="category name" equiv-text="category['name']"/> verwijderen mislukt!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">172</context>
</context-group>
</trans-unit>
<trans-unit id="674a999dd48d7da565ffdd105602261b8a4761ea" datatype="html">
<source>Download zip</source>
<target state="translated">Zip downloaden</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/subscription/subscription/subscription.component.html</context>
<context context-type="linenumber">18</context>
</context-group>
<note priority="1" from="description">Download zip</note>
</trans-unit>
<trans-unit id="378c072ce05889c9771718d05106e7685fcd3507" datatype="html">
<source>Medium</source>
<target state="translated">Gemiddeld</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">47,49</context>
</context-group>
<note priority="1" from="description">Medium</note>
</trans-unit>
<trans-unit id="37469c9f3e31d95087fa22b6c9c3bc64adf1692d" datatype="html">
<source>Enable RSS Feed</source>
<target state="translated">RSS-feed aanzetten</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">271</context>
</context-group>
<note priority="1" from="description">Enable RSS Feed setting</note>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -3784,6 +3784,727 @@
</context-group> </context-group>
<note priority="1" from="description">Download error</note> <note priority="1" from="description">Download error</note>
</trans-unit> </trans-unit>
<trans-unit id="51a161ce175abcd44f6c6cbd0e996681bf553ac3" datatype="html">
<source>Delete selected</source>
<target state="translated">Usuń wybrane</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">77</context>
</context-group>
<note priority="1" from="description">Delete selected</note>
</trans-unit>
<trans-unit id="7022070615528435141" datatype="html">
<source>Delete</source>
<target state="translated">Usuń</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">154</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">160</context>
</context-group>
</trans-unit>
<trans-unit id="5709555629190115111" datatype="html">
<source>View task</source>
<target state="translated">Wyświetl zadanie</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">33</context>
</context-group>
</trans-unit>
<trans-unit id="4578192247039196794" datatype="html">
<source>Task</source>
<target state="translated">Zadanie</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">31</context>
</context-group>
</trans-unit>
<trans-unit id="9a865c2922f5c01899d06c472dba2e5bd63bcff9" datatype="html">
<source>Small</source>
<target state="translated">Mały</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">50,52</context>
</context-group>
<note priority="1" from="description">Small</note>
</trans-unit>
<trans-unit id="9c562d26e041390ecc3f49dabc51cc50ebba7469" datatype="html">
<source>Allowed notification types</source>
<target state="translated">Dozwolone typy powiadomień</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">356</context>
</context-group>
<note priority="1" from="description">Allowed notification types</note>
</trans-unit>
<trans-unit id="33a7c6d5ff3515fa237f1fd4e43df8b65373954d" datatype="html">
<source>Enable all notifications</source>
<target state="translated">Włącz wszystkie powiadomienia</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">352</context>
</context-group>
<note priority="1" from="description">Enable all notifications setting</note>
</trans-unit>
<trans-unit id="6268070779441507380" datatype="html">
<source>Download Date</source>
<target state="translated">Data pobrania</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit id="9aa1b4779a515170b297d2c0507e6ff9d2e3e0e0" datatype="html">
<source>Blacklist deleted subscription files</source>
<target state="translated">Czarna lista usuniętych plików subskrypcji</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">14</context>
</context-group>
<note priority="1" from="description">Blacklist deleted subscription files</note>
</trans-unit>
<trans-unit id="c748ac656af9f13998206ef2c52018dd418b0483" datatype="html">
<source>Archives</source>
<target state="translated">Archiwum</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">26</context>
</context-group>
<note priority="1" from="description">Archives menu label</note>
</trans-unit>
<trans-unit id="5ca707824ab93066c7d9b44e1b8bf216725c2c22" datatype="html">
<source>Filter</source>
<target state="translated">Filtr</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
<note priority="1" from="description">Filter</note>
</trans-unit>
<trans-unit id="45cc8ca94b5a50842a9a8ef804a5ab089a38ae5c" datatype="html">
<source>ID</source>
<target state="translated">ID</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">47</context>
</context-group>
<note priority="1" from="description">ID</note>
</trans-unit>
<trans-unit id="28da11220a3377ddce3c7948825d33101f142782" datatype="html">
<source>Extractor</source>
<target state="translated">Ekstraktor</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">57</context>
</context-group>
<note priority="1" from="description">Extractor</note>
</trans-unit>
<trans-unit id="c150a30bbbdb175b4d08820196a9acb66b167653" datatype="html">
<source>Archives empty</source>
<target state="translated">Archiwum puste</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">72</context>
</context-group>
<note priority="1" from="description">Archives empty</note>
</trans-unit>
<trans-unit id="c41475a25c9f9d9639db9efa56637603a77528b4" datatype="html">
<source>Download archive</source>
<target state="translated">Pobierz archiwum</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">80</context>
</context-group>
<note priority="1" from="description">Download archive</note>
</trans-unit>
<trans-unit id="a2f14a73f7a6e94479f67423cc51102da8d6f524" datatype="html">
<source>None</source>
<target state="translated">Brak</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">84</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">126</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">27</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">36</context>
</context-group>
<note priority="1" from="description">None</note>
</trans-unit>
<trans-unit id="2d1ea268a6a9f483dbc2cbfe19bf4256a57a6af4" datatype="html">
<source>Video</source>
<target state="translated">Wideo</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">92</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">133</context>
</context-group>
<note priority="1" from="description">Video</note>
</trans-unit>
<trans-unit id="f0baeb8b69d120073b6d60d34785889b0c3232c8" datatype="html">
<source>Audio</source>
<target state="translated">Audio</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">93</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">134</context>
</context-group>
<note priority="1" from="description">Audio</note>
</trans-unit>
<trans-unit id="4b3972c3e9485218508a95f7a4ce7758e3f09ced" datatype="html">
<source>Upload</source>
<target state="translated">Wyślij</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">137</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.html</context>
<context context-type="linenumber">30</context>
</context-group>
<note priority="1" from="description">Upload</note>
</trans-unit>
<trans-unit id="6549265851868599441" datatype="html">
<source>Video</source>
<target state="translated">Wideo</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">40</context>
</context-group>
</trans-unit>
<trans-unit id="347407180135731058" datatype="html">
<source>Audio</source>
<target state="translated">Audio</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">44</context>
</context-group>
</trans-unit>
<trans-unit id="8425787787095143143" datatype="html">
<source>Would you like to delete <x id="selected archives amount" equiv-text="this.selection.selected.length"/> archive(s)?</source>
<target state="translated">Chcesz usunąć archiwum(a) <x id="selected archives amount" equiv-text="this.selection.selected.length"/>?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">153</context>
</context-group>
</trans-unit>
<trans-unit id="2525880134753073592" datatype="html">
<source>Successfully deleted archive items!</source>
<target state="translated">Pomyślnie usunięto elementy archiwum!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">172</context>
</context-group>
</trans-unit>
<trans-unit id="8953483585652369683" datatype="html">
<source>Archive successfully imported!</source>
<target state="translated">Archiwum zostało pomyślnie zaimportowane!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">130</context>
</context-group>
</trans-unit>
<trans-unit id="6876310993601590130" datatype="html">
<source>Download completed</source>
<target state="translated">Pobieranie zakończone</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">23</context>
</context-group>
</trans-unit>
<trans-unit id="7911845622864460134" datatype="html">
<source>Video only</source>
<target state="translated">Tylko wideo</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">55</context>
</context-group>
</trans-unit>
<trans-unit id="9176960997786930103" datatype="html">
<source>Error for: <x id="PH" equiv-text="task['title']"/></source>
<target state="translated">Błąd dla: <x id="PH" equiv-text="task['title']"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/tasks/tasks.component.ts</context>
<context context-type="linenumber">174</context>
</context-group>
</trans-unit>
<trans-unit id="1d4fa01d25990f60abf21c3a451fa8ba262b7912" datatype="html">
<source>Unfavorite</source>
<target state="translated">Nieulubione</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/unified-file-card/unified-file-card.component.html</context>
<context context-type="linenumber">27</context>
</context-group>
<note priority="1" from="description">Unfavorite button</note>
</trans-unit>
<trans-unit id="1f2809e6a99d511fdb6eaf041d785fe54d0680cc" datatype="html">
<source>File card size</source>
<target state="translated">Rozmiar karty pliku</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">42</context>
</context-group>
<note priority="1" from="description">File card size</note>
</trans-unit>
<trans-unit id="d618f383a0ea2458eeb945a85190d4a002ea394b" datatype="html">
<source>Arg</source>
<target state="translated">Argument</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/arg-modifier-dialog/arg-modifier-dialog.component.html</context>
<context context-type="linenumber">41</context>
</context-group>
<note priority="1" from="description">Arg</note>
</trans-unit>
<trans-unit id="1a9b415816364f554ee411020e65219092655271" datatype="html">
<source>Title filter</source>
<target state="translated">Filtr tytułu</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">8</context>
</context-group>
<note priority="1" from="description">Title filter</note>
</trans-unit>
<trans-unit id="9aa62bf1a535a97a4d752bbc5cf1c31af0f0c1f7" datatype="html">
<source>Supports regex</source>
<target state="translated">Obsługa regex</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">10</context>
</context-group>
<note priority="1" from="description">Supports regex</note>
</trans-unit>
<trans-unit id="1698114086921246480" datatype="html">
<source>Unsubscribe</source>
<target state="translated">Anuluj subskrypcję</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.ts</context>
<context context-type="linenumber">32</context>
</context-group>
</trans-unit>
<trans-unit id="35cf4cdcedc8ef3f94b6100e0d86836e31dbb908" datatype="html">
<source>Force autoplay</source>
<target state="translated">Wymuś autoodtwarzanie</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">218</context>
</context-group>
<note priority="1" from="description">Force autoplay setting</note>
</trans-unit>
<trans-unit id="37469c9f3e31d95087fa22b6c9c3bc64adf1692d" datatype="html">
<source>Enable RSS Feed</source>
<target state="translated">Włącz kanał RSS</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">271</context>
</context-group>
<note priority="1" from="description">Enable RSS Feed setting</note>
</trans-unit>
<trans-unit id="0ed98b4c6ec1db6708a963e8a2699478ac97f55c" datatype="html">
<source>Add subscription</source>
<target state="translated">Dodaj subskrypcję</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/subscriptions/subscriptions.component.html</context>
<context context-type="linenumber">60</context>
</context-group>
<note priority="1" from="description">Add subscription</note>
</trans-unit>
<trans-unit id="8c6e24eab969d9f63a8a0e9d617aee3b99e28ae6" datatype="html">
<source>Play all</source>
<target state="translated">Odtwórz wszystko</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/subscription/subscription/subscription.component.html</context>
<context context-type="linenumber">17</context>
</context-group>
<note priority="1" from="description">Play all</note>
</trans-unit>
<trans-unit id="6080b77234e92ad41bb52653b239c4c4f851317d" datatype="html">
<source>Error</source>
<target state="translated">Błąd</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">39</context>
</context-group>
<note priority="1" from="description">Error</note>
</trans-unit>
<trans-unit id="3640026747176198246" datatype="html">
<source>Watch content</source>
<target state="translated">Oglądaj zawartość</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">50</context>
</context-group>
</trans-unit>
<trans-unit id="8456659390937171831" datatype="html">
<source>Show error</source>
<target state="translated">Pokaż błąd</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>
<trans-unit id="1236604279860679031" datatype="html">
<source>Restart</source>
<target state="translated">Uruchom ponownie</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">62</context>
</context-group>
</trans-unit>
<trans-unit id="9042260521669277115" datatype="html">
<source>Pause</source>
<target state="translated">Wstrzymaj</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">68</context>
</context-group>
</trans-unit>
<trans-unit id="7182974689040833178" datatype="html">
<source>Resume</source>
<target state="translated">Wznów</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">74</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">80</context>
</context-group>
</trans-unit>
<trans-unit id="019d4bd6a5690f0cfa0ecf346a4e6bf7f0d8debb" datatype="html">
<source>Remove</source>
<target state="translated">Usuń</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.html</context>
<context context-type="linenumber">23</context>
</context-group>
<note priority="1" from="description">Remove</note>
</trans-unit>
<trans-unit id="8443034725057696949" datatype="html">
<source>Task finished</source>
<target state="translated">Zadanie zakończone</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">19</context>
</context-group>
</trans-unit>
<trans-unit id="8564202903947049539" datatype="html">
<source>Play</source>
<target state="translated">Odtwarzaj</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">30</context>
</context-group>
</trans-unit>
<trans-unit id="8643601595923420698" datatype="html">
<source>Retry download</source>
<target state="translated">Ponów próbę pobrania</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">31</context>
</context-group>
</trans-unit>
<trans-unit id="5a105e7bd7e7db6ea211fe950fc9f317379acceb" datatype="html">
<source>No notifications available</source>
<target state="translated">B</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
<note priority="1" from="description">No notifications available</note>
</trans-unit>
<trans-unit id="1879058637439215882" datatype="html">
<source>Download error</source>
<target state="translated">Błąd pobierania</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">27</context>
</context-group>
</trans-unit>
<trans-unit id="6437411876967154040" datatype="html">
<source>Audio only</source>
<target state="translated">Tylko audio</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">60</context>
</context-group>
</trans-unit>
<trans-unit id="3533826530554274875" datatype="html">
<source>Upload Date</source>
<target state="translated">Data przesłania</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
<trans-unit id="8953033926734869941" datatype="html">
<source>Name</source>
<target state="translated">Nazwa</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">21</context>
</context-group>
</trans-unit>
<trans-unit id="2492098975665776610" datatype="html">
<source>File Size</source>
<target state="translated">Rozmiar pliku</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">25</context>
</context-group>
</trans-unit>
<trans-unit id="ea2b65121b93921fe54692025da9b9e3ce779ad5" datatype="html">
<source>Task settings - <x id="INTERPOLATION" equiv-text="{{task.title}}"/></source>
<target state="translated">Ustawienia zadania - <x id="INTERPOLATION" equiv-text="{{task.title}}"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
<note priority="1" from="description">Task settings</note>
</trans-unit>
<trans-unit id="cdf5297d8d080a78e8b10debc5c38b7845a3cbe7" datatype="html">
<source>Do not ask for confirmation</source>
<target state="translated">Nie pytaj o potwierdzenie</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">19</context>
</context-group>
<note priority="1" from="description">Do not ask for confirmation</note>
</trans-unit>
<trans-unit id="8571838164752006148" datatype="html">
<source>View error</source>
<target state="translated">Wyświetl błąd</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">32</context>
</context-group>
</trans-unit>
<trans-unit id="4665451070906079743" datatype="html">
<source>Favorited</source>
<target state="translated">Ulubione</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.ts</context>
<context context-type="linenumber">65</context>
</context-group>
</trans-unit>
<trans-unit id="7410432243549869948" datatype="html">
<source>Duration</source>
<target state="translated">Czas trwania</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/sort-property/sort-property.component.ts</context>
<context context-type="linenumber">29</context>
</context-group>
</trans-unit>
<trans-unit id="c65dd978b3c7566551c0ebefb234c2d41942b847" datatype="html">
<source>Delete files older than</source>
<target state="translated">Usuń pliki starsze niż</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/task-settings/task-settings.component.html</context>
<context context-type="linenumber">6</context>
</context-group>
<note priority="1" from="description">Delete files older than</note>
</trans-unit>
<trans-unit id="7b4585a9072f3c1292972c14a3d0e14978fbfc9c" datatype="html">
<source>Delete old files:</source>
<target state="translated">Usuń stare pliki:</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/tasks/tasks.component.html</context>
<context context-type="linenumber">66</context>
</context-group>
<note priority="1" from="description">Delete old files</note>
</trans-unit>
<trans-unit id="b78a98bc54259a29cf6250dbaeab5fe11fae91cf" datatype="html">
<source>Favorited</source>
<target state="translated">Ulubione</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">51</context>
</context-group>
<note priority="1" from="description">Favorited</note>
</trans-unit>
<trans-unit id="e08a77594f3d89311cdf6da5090044270909c194" datatype="html">
<source>User</source>
<target state="translated">Użytkownik</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">25</context>
</context-group>
<note priority="1" from="description">User</note>
</trans-unit>
<trans-unit id="1091872159779006651" datatype="html">
<source>You must input a time!</source>
<target state="translated">Musisz wprowadzić czas!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/update-task-schedule-dialog/update-task-schedule-dialog.component.ts</context>
<context context-type="linenumber">70</context>
</context-group>
</trans-unit>
<trans-unit id="338b44701a53ce3ef2f36abfb56f89c3edfa9eab" datatype="html">
<source>Over</source>
<target state="translated">Nad</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">32,34</context>
</context-group>
<note priority="1" from="description">Over</note>
</trans-unit>
<trans-unit id="e0a11fbea353b1ce1131161774e4a3e10bcb99b1" datatype="html">
<source>Large</source>
<target state="translated">Duży</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/user-profile-dialog/user-profile-dialog.component.html</context>
<context context-type="linenumber">44,46</context>
</context-group>
<note priority="1" from="description">Large</note>
</trans-unit>
<trans-unit id="d57c023a4cf63b2f12c10328c15b636ff18929aa" datatype="html">
<source>Best</source>
<target state="translated">Najlepsze</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/main/main.component.html</context>
<context context-type="linenumber">24,25</context>
</context-group>
<note priority="1" from="description">Best</note>
</trans-unit>
<trans-unit id="af30e51aa8b67e1133a341ec28359be05150e65c" datatype="html">
<source>No description available.</source>
<target state="translated">Opis nie dostępny.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/player/player.component.html</context>
<context context-type="linenumber">25,27</context>
</context-group>
<note priority="1" from="description">No description</note>
</trans-unit>
<trans-unit id="61d6b5fa4311b1c617b66dad72496f9dd43b07b4" datatype="html">
<source>Be careful enabling this with multi-user mode! User data may be exposed.</source>
<target state="translated">Uważaj, włączając to w trybie wielu użytkowników! Dane użytkownika mogą zostać ujawnione.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">272</context>
</context-group>
<note priority="1" from="description">RSS Feed prefix</note>
</trans-unit>
<trans-unit id="fd467148a18f0921c10d116d4e0174fe29452be4" datatype="html">
<source>See documentation here.</source>
<target state="translated">Zobacz dokumentację tutaj.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">274</context>
</context-group>
<note priority="1" from="description">RSS feed documentation</note>
</trans-unit>
<trans-unit id="06f503e492d6dbcf59e7b9c412ca86913d718689" datatype="html">
<source>ntfy topic URL</source>
<target state="translated">ntfy topic URL</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">390</context>
</context-group>
<note priority="1" from="description">ntfy topic URL</note>
</trans-unit>
<trans-unit id="5827fde8fcafdd55ae80921ad3ad4aa01012e203" datatype="html">
<source>Use gotify API</source>
<target state="translated">Użyj gotify API</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">396</context>
</context-group>
<note priority="1" from="description">Use gotify API setting</note>
</trans-unit>
<trans-unit id="55f559d6f666b945479f534b0c182f70cd0a8a69" datatype="html">
<source>Gotify server URL</source>
<target state="translated">Adres serwera Gotify</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">400</context>
</context-group>
<note priority="1" from="description">Gotify server URL</note>
</trans-unit>
<trans-unit id="b770c48628d98cb4633d6a17e3f0ba0265376af5" datatype="html">
<source>Gotify app token</source>
<target state="translated">Token aplikacji Gotify</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">407</context>
</context-group>
<note priority="1" from="description">Gotify app token</note>
</trans-unit>
<trans-unit id="38992954440d6afb54aeb58af12ca0123ee5e26e" datatype="html">
<source>Use Telegram API</source>
<target state="translated">Użyj Telegram API</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">413</context>
</context-group>
<note priority="1" from="description">Use Telegram API setting</note>
</trans-unit>
<trans-unit id="2e076ff9866213d0815961c494aa48b177046b9d" datatype="html">
<source>Telegram bot token</source>
<target state="translated">Token bota Telegramu</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">417</context>
</context-group>
<note priority="1" from="description">Telegram bot token</note>
</trans-unit>
<trans-unit id="144e1a21ebe8fa238f88d2ac27515ed711cfc9a0" datatype="html">
<source>Telegram chat ID</source>
<target state="translated">ID czatu Telegram</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">424</context>
</context-group>
<note priority="1" from="description">Telegram chat ID</note>
</trans-unit>
<trans-unit id="3e420c675b8f3f3702576d52e8bb6e8e1d3feda0" datatype="html">
<source>How do I get the chat ID?</source>
<target state="translated">Jak uzyskać ID czatu Telegram?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">426</context>
</context-group>
<note priority="1" from="description">Telegram chat ID help</note>
</trans-unit>
<trans-unit id="8bcabdf6b16cad0313a86c7e940c5e3ad7f9f8ab" datatype="html">
<source>Notifications</source>
<target state="translated">Powiadomienia</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">343</context>
</context-group>
<note priority="1" from="description">Notifications settings label</note>
</trans-unit>
<trans-unit id="7cedb649779673568447b994463b2882c4e0436a" datatype="html">
<source>Slack Webhook URL</source>
<target state="translated">Slack Webhook URL</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">380</context>
</context-group>
<note priority="1" from="description">Slack Webhook URL</note>
</trans-unit>
<trans-unit id="2481374649045841364" datatype="html">
<source>Would you like to delete <x id="category name" equiv-text="category['name']"/>?</source>
<target state="translated">Czy chcesz usunąć <x id="category name" equiv-text="category['name']"/>?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">159</context>
</context-group>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -4,6 +4,7 @@
<body> <body>
<trans-unit id="994363f08f9fbfa3b3994ff7b35c6904fdff18d8" datatype="html"> <trans-unit id="994363f08f9fbfa3b3994ff7b35c6904fdff18d8" datatype="html">
<source>Profile</source> <source>Profile</source>
<target state="translated">Perfil</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context> <context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">19</context> <context context-type="linenumber">19</context>
@@ -12,6 +13,7 @@
</trans-unit> </trans-unit>
<trans-unit id="adb4562d2dbd3584370e44496969d58c511ecb63" datatype="html"> <trans-unit id="adb4562d2dbd3584370e44496969d58c511ecb63" datatype="html">
<source>Dark</source> <source>Dark</source>
<target state="translated">Escuro</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context> <context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">23</context> <context context-type="linenumber">23</context>
@@ -24,6 +26,7 @@
</trans-unit> </trans-unit>
<trans-unit id="004b222ff9ef9dd4771b777950ca1d0e4cd4348a" datatype="html"> <trans-unit id="004b222ff9ef9dd4771b777950ca1d0e4cd4348a" datatype="html">
<source>About</source> <source>About</source>
<target state="translated">Sobre</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context> <context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">32</context> <context context-type="linenumber">32</context>
@@ -32,6 +35,7 @@
</trans-unit> </trans-unit>
<trans-unit id="92eee6be6de0b11c924e3ab27db30257159c0a7c" datatype="html"> <trans-unit id="92eee6be6de0b11c924e3ab27db30257159c0a7c" datatype="html">
<source>Home</source> <source>Home</source>
<target state="translated">Início</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context> <context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">43</context> <context context-type="linenumber">43</context>
@@ -40,6 +44,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6765b4c916060f6bc42d9bb69e80377dbcb5e4e9" datatype="html"> <trans-unit id="6765b4c916060f6bc42d9bb69e80377dbcb5e4e9" datatype="html">
<source>Login</source> <source>Login</source>
<target state="translated">Login</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context> <context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">44</context> <context context-type="linenumber">44</context>
@@ -56,6 +61,7 @@
</trans-unit> </trans-unit>
<trans-unit id="357064ca9d9ac859eb618e28e8126fa32be049e2" datatype="html"> <trans-unit id="357064ca9d9ac859eb618e28e8126fa32be049e2" datatype="html">
<source>Subscriptions</source> <source>Subscriptions</source>
<target state="translated">Inscrições</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context> <context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">45</context> <context context-type="linenumber">45</context>
@@ -64,6 +70,7 @@
</trans-unit> </trans-unit>
<trans-unit id="822fab38216f64e8166d368b59fe756ca39d301b" datatype="html"> <trans-unit id="822fab38216f64e8166d368b59fe756ca39d301b" datatype="html">
<source>Downloads</source> <source>Downloads</source>
<target state="translated">Downloads</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context> <context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">46</context> <context context-type="linenumber">46</context>
@@ -72,6 +79,7 @@
</trans-unit> </trans-unit>
<trans-unit id="586a5fd72602b5b14ec0c55f84814de47bb21e3a" datatype="html"> <trans-unit id="586a5fd72602b5b14ec0c55f84814de47bb21e3a" datatype="html">
<source>Tasks</source> <source>Tasks</source>
<target state="translated">Tarefas</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context> <context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">47</context> <context context-type="linenumber">47</context>
@@ -80,6 +88,7 @@
</trans-unit> </trans-unit>
<trans-unit id="121cc5391cd2a5115bc2b3160379ee5b36cd7716" datatype="html"> <trans-unit id="121cc5391cd2a5115bc2b3160379ee5b36cd7716" datatype="html">
<source>Settings</source> <source>Settings</source>
<target state="translated">Configurações</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context> <context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">50</context> <context context-type="linenumber">50</context>
@@ -92,6 +101,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2f933b826a570836cab04f683970a2d22068458c" datatype="html"> <trans-unit id="2f933b826a570836cab04f683970a2d22068458c" datatype="html">
<source>Date</source> <source>Date</source>
<target state="translated">Data</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">7</context> <context context-type="linenumber">7</context>
@@ -100,6 +110,7 @@
</trans-unit> </trans-unit>
<trans-unit id="fdf7cbdc140d0aab0f0b6c06065a0fd448ed6a2e" datatype="html"> <trans-unit id="fdf7cbdc140d0aab0f0b6c06065a0fd448ed6a2e" datatype="html">
<source>Title</source> <source>Title</source>
<target state="translated">Título</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">13</context> <context context-type="linenumber">13</context>
@@ -112,6 +123,7 @@
</trans-unit> </trans-unit>
<trans-unit id="47bbc861efa59ba4135e6aa8f63213420e3f3b91" datatype="html"> <trans-unit id="47bbc861efa59ba4135e6aa8f63213420e3f3b91" datatype="html">
<source>Subscription</source> <source>Subscription</source>
<target state="translated">Inscrição</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">23</context> <context context-type="linenumber">23</context>
@@ -128,6 +140,7 @@
</trans-unit> </trans-unit>
<trans-unit id="15793f4cbc261bedbc60f7105533dde536a3f42b" datatype="html"> <trans-unit id="15793f4cbc261bedbc60f7105533dde536a3f42b" datatype="html">
<source>Progress</source> <source>Progress</source>
<target state="translated">Progresso</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">42</context> <context context-type="linenumber">42</context>
@@ -136,6 +149,7 @@
</trans-unit> </trans-unit>
<trans-unit id="030b4423b92167200e39519599f9b863b4f7c62c" datatype="html"> <trans-unit id="030b4423b92167200e39519599f9b863b4f7c62c" datatype="html">
<source>Actions</source> <source>Actions</source>
<target state="translated">Ações</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">55</context> <context context-type="linenumber">55</context>
@@ -164,6 +178,7 @@
</trans-unit> </trans-unit>
<trans-unit id="d7b35c384aecd25a516200d6921836374613dfe7" datatype="html"> <trans-unit id="d7b35c384aecd25a516200d6921836374613dfe7" datatype="html">
<source>Cancel</source> <source>Cancel</source>
<target state="translated">Cancelar</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">61</context> <context context-type="linenumber">61</context>
@@ -240,6 +255,7 @@
</trans-unit> </trans-unit>
<trans-unit id="b36b7458192b833592e13029fa8a0b3555e0d9bd" datatype="html"> <trans-unit id="b36b7458192b833592e13029fa8a0b3555e0d9bd" datatype="html">
<source>Pause all downloads</source> <source>Pause all downloads</source>
<target state="translated">Pausar todos os downloads</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">83</context> <context context-type="linenumber">83</context>
@@ -248,6 +264,7 @@
</trans-unit> </trans-unit>
<trans-unit id="9b2084f9aea764292cf0978cb083907d8be51bf7" datatype="html"> <trans-unit id="9b2084f9aea764292cf0978cb083907d8be51bf7" datatype="html">
<source>Resume all downloads</source> <source>Resume all downloads</source>
<target state="translated">Retomar todos os downloads</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">84</context> <context context-type="linenumber">84</context>
@@ -264,6 +281,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7117fc42f860e86d983bfccfcf2654e5750f3406" datatype="html"> <trans-unit id="7117fc42f860e86d983bfccfcf2654e5750f3406" datatype="html">
<source>No downloads available!</source> <source>No downloads available!</source>
<target state="translated">Nenhum download disponível!</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">90</context> <context context-type="linenumber">90</context>
@@ -272,6 +290,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2827589726081052618" datatype="html"> <trans-unit id="2827589726081052618" datatype="html">
<source>Creating download</source> <source>Creating download</source>
<target state="translated">Criando download</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">58</context> <context context-type="linenumber">58</context>
@@ -279,6 +298,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4027175717527633324" datatype="html"> <trans-unit id="4027175717527633324" datatype="html">
<source>Getting info</source> <source>Getting info</source>
<target state="translated">Obtendo informações</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">59</context> <context context-type="linenumber">59</context>
@@ -286,6 +306,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7724483709075923163" datatype="html"> <trans-unit id="7724483709075923163" datatype="html">
<source>Downloading file</source> <source>Downloading file</source>
<target state="translated">Baixando arquivo</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">60</context> <context context-type="linenumber">60</context>
@@ -293,6 +314,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8384225360105280028" datatype="html"> <trans-unit id="8384225360105280028" datatype="html">
<source>Complete</source> <source>Complete</source>
<target state="translated">Terminado</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">61</context> <context context-type="linenumber">61</context>
@@ -314,6 +336,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8700121026680200191" datatype="html"> <trans-unit id="8700121026680200191" datatype="html">
<source>Clear</source> <source>Clear</source>
<target state="translated">Limpar</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">131</context> <context context-type="linenumber">131</context>
@@ -321,6 +344,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2560364143605631750" datatype="html"> <trans-unit id="2560364143605631750" datatype="html">
<source>Error for <x id="url" equiv-text="download['url']"/></source> <source>Error for <x id="url" equiv-text="download['url']"/></source>
<target state="needs-translation">Erro para <x id="url" equiv-text="download['url']"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">238</context> <context context-type="linenumber">238</context>
@@ -328,6 +352,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8738732372986673558" datatype="html"> <trans-unit id="8738732372986673558" datatype="html">
<source>Copy to clipboard</source> <source>Copy to clipboard</source>
<target state="translated">Copiar para a área de transferência</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">240</context> <context context-type="linenumber">240</context>
@@ -335,6 +360,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7819314041543176992" datatype="html"> <trans-unit id="7819314041543176992" datatype="html">
<source>Close</source> <source>Close</source>
<target state="translated">Fechar</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">241</context> <context context-type="linenumber">241</context>
@@ -342,6 +368,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3795459839164395144" datatype="html"> <trans-unit id="3795459839164395144" datatype="html">
<source>Copied to clipboard!</source> <source>Copied to clipboard!</source>
<target state="translated">Copiado para a área de transferência!</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context> <context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">249</context> <context context-type="linenumber">249</context>
@@ -349,6 +376,7 @@
</trans-unit> </trans-unit>
<trans-unit id="cfc2f436ec2beffb042e7511a73c89c372e86a6c" datatype="html"> <trans-unit id="cfc2f436ec2beffb042e7511a73c89c372e86a6c" datatype="html">
<source>Register</source> <source>Register</source>
<target state="translated">Registrar</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/login/login.component.html</context> <context context-type="sourcefile">src/app/components/login/login.component.html</context>
<context context-type="linenumber">38</context> <context context-type="linenumber">38</context>
@@ -361,6 +389,7 @@
</trans-unit> </trans-unit>
<trans-unit id="5009630cdf32ab4f1c78737b9617b8773512c05a" datatype="html"> <trans-unit id="5009630cdf32ab4f1c78737b9617b8773512c05a" datatype="html">
<source>Lines:</source> <source>Lines:</source>
<target state="needs-translation">Linhas:</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.html</context> <context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.html</context>
<context context-type="linenumber">22</context> <context context-type="linenumber">22</context>
@@ -369,6 +398,7 @@
</trans-unit> </trans-unit>
<trans-unit id="8a0bda4c47f10b2423ff183acefbf70d4ab52ea2" datatype="html"> <trans-unit id="8a0bda4c47f10b2423ff183acefbf70d4ab52ea2" datatype="html">
<source>Clear logs</source> <source>Clear logs</source>
<target state="translated">Limpar logs</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.html</context> <context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.html</context>
<context context-type="linenumber">34</context> <context context-type="linenumber">34</context>
@@ -377,6 +407,7 @@
</trans-unit> </trans-unit>
<trans-unit id="57c6c05d8ebf4ef1180c2705033c044f655bb2c4" datatype="html"> <trans-unit id="57c6c05d8ebf4ef1180c2705033c044f655bb2c4" datatype="html">
<source>Manage role</source> <source>Manage role</source>
<target state="needs-translation">Editar perfil de usuário</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.html</context> <context context-type="sourcefile">src/app/components/manage-role/manage-role.component.html</context>
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
@@ -385,6 +416,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4f20f2d5a6882190892e58b85f6ccbedfa737952" datatype="html"> <trans-unit id="4f20f2d5a6882190892e58b85f6ccbedfa737952" datatype="html">
<source>Yes</source> <source>Yes</source>
<target state="translated">Sim</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.html</context> <context context-type="sourcefile">src/app/components/manage-role/manage-role.component.html</context>
<context context-type="linenumber">9</context> <context context-type="linenumber">9</context>
@@ -397,6 +429,7 @@
</trans-unit> </trans-unit>
<trans-unit id="3d3ae7deebc5949b0c1c78b9847886a94321d9fd" datatype="html"> <trans-unit id="3d3ae7deebc5949b0c1c78b9847886a94321d9fd" datatype="html">
<source>No</source> <source>No</source>
<target state="translated">Não</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.html</context> <context context-type="sourcefile">src/app/components/manage-role/manage-role.component.html</context>
<context context-type="linenumber">10</context> <context context-type="linenumber">10</context>
@@ -409,6 +442,7 @@
</trans-unit> </trans-unit>
<trans-unit id="f4e529ae5ffd73001d1ff4bbdeeb0a72e342e5c8" datatype="html"> <trans-unit id="f4e529ae5ffd73001d1ff4bbdeeb0a72e342e5c8" datatype="html">
<source>Close</source> <source>Close</source>
<target state="translated">Fechar</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.html</context> <context context-type="sourcefile">src/app/components/manage-role/manage-role.component.html</context>
<context context-type="linenumber">18</context> <context context-type="linenumber">18</context>
@@ -453,6 +487,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2bd201aea09e43fbfd3cd15ec0499b6755302329" datatype="html"> <trans-unit id="2bd201aea09e43fbfd3cd15ec0499b6755302329" datatype="html">
<source>Manage user</source> <source>Manage user</source>
<target state="needs-translation">Editar usuário</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-user/manage-user.component.html</context> <context context-type="sourcefile">src/app/components/manage-user/manage-user.component.html</context>
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
@@ -465,6 +500,7 @@
</trans-unit> </trans-unit>
<trans-unit id="29c97c8e76763bb15b6d515648fa5bd1eb0f7510" datatype="html"> <trans-unit id="29c97c8e76763bb15b6d515648fa5bd1eb0f7510" datatype="html">
<source>User UID:</source> <source>User UID:</source>
<target state="translated">UID do Usuário:</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-user/manage-user.component.html</context> <context context-type="sourcefile">src/app/components/manage-user/manage-user.component.html</context>
<context context-type="linenumber">4</context> <context context-type="linenumber">4</context>
@@ -473,6 +509,7 @@
</trans-unit> </trans-unit>
<trans-unit id="e70e209561583f360b1e9cefd2cbb1fe434b6229" datatype="html"> <trans-unit id="e70e209561583f360b1e9cefd2cbb1fe434b6229" datatype="html">
<source>New password</source> <source>New password</source>
<target state="translated">Nova senha</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-user/manage-user.component.html</context> <context context-type="sourcefile">src/app/components/manage-user/manage-user.component.html</context>
<context context-type="linenumber">8</context> <context context-type="linenumber">8</context>
@@ -481,6 +518,7 @@
</trans-unit> </trans-unit>
<trans-unit id="6498fa1b8f563988f769654a75411bb8060134b9" datatype="html"> <trans-unit id="6498fa1b8f563988f769654a75411bb8060134b9" datatype="html">
<source>Set new password</source> <source>Set new password</source>
<target state="translated">Criar nova senha</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-user/manage-user.component.html</context> <context context-type="sourcefile">src/app/components/manage-user/manage-user.component.html</context>
<context context-type="linenumber">10</context> <context context-type="linenumber">10</context>
@@ -497,6 +535,7 @@
</trans-unit> </trans-unit>
<trans-unit id="7e892ba15f2c6c17e83510e273b3e10fc32ea016" datatype="html"> <trans-unit id="7e892ba15f2c6c17e83510e273b3e10fc32ea016" datatype="html">
<source>Search</source> <source>Search</source>
<target state="translated">Buscar</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context> <context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context>
<context context-type="linenumber">7</context> <context context-type="linenumber">7</context>
@@ -513,6 +552,7 @@
</trans-unit> </trans-unit>
<trans-unit id="746f64ddd9001ac456327cd9a3d5152203a4b93c" datatype="html"> <trans-unit id="746f64ddd9001ac456327cd9a3d5152203a4b93c" datatype="html">
<source>User name</source> <source>User name</source>
<target state="translated">Nome de usuário</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context> <context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context>
<context context-type="linenumber">17</context> <context context-type="linenumber">17</context>
@@ -521,6 +561,7 @@
</trans-unit> </trans-unit>
<trans-unit id="52c1447c1ec9570a2a3025c7e566557b8d19ed92" datatype="html"> <trans-unit id="52c1447c1ec9570a2a3025c7e566557b8d19ed92" datatype="html">
<source>Role</source> <source>Role</source>
<target state="needs-translation">Perfil</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context> <context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context>
<context context-type="linenumber">35</context> <context context-type="linenumber">35</context>
@@ -529,6 +570,7 @@
</trans-unit> </trans-unit>
<trans-unit id="59a8c38db3091a63ac1cb9590188dc3a972acfb3" datatype="html"> <trans-unit id="59a8c38db3091a63ac1cb9590188dc3a972acfb3" datatype="html">
<source>Actions</source> <source>Actions</source>
<target state="translated">Ações</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context> <context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context>
<context context-type="linenumber">55</context> <context context-type="linenumber">55</context>
@@ -537,6 +579,7 @@
</trans-unit> </trans-unit>
<trans-unit id="52c9a103b812f258bcddc3d90a6e3f46871d25fe" datatype="html"> <trans-unit id="52c9a103b812f258bcddc3d90a6e3f46871d25fe" datatype="html">
<source>Save</source> <source>Save</source>
<target state="translated">Salvar</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context> <context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context>
<context context-type="linenumber">58</context> <context context-type="linenumber">58</context>
@@ -561,6 +604,7 @@
</trans-unit> </trans-unit>
<trans-unit id="632e8b20c98e8eec4059a605a4b011bb476137af" datatype="html"> <trans-unit id="632e8b20c98e8eec4059a605a4b011bb476137af" datatype="html">
<source>Edit user</source> <source>Edit user</source>
<target state="translated">Editar dados de usuário</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context> <context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context>
<context context-type="linenumber">66</context> <context context-type="linenumber">66</context>
@@ -569,6 +613,7 @@
</trans-unit> </trans-unit>
<trans-unit id="95b95a9c79e4fd9ed41f6855e37b3b06af25bcab" datatype="html"> <trans-unit id="95b95a9c79e4fd9ed41f6855e37b3b06af25bcab" datatype="html">
<source>Delete user</source> <source>Delete user</source>
<target state="translated">Deletar usuário</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context> <context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context>
<context context-type="linenumber">73</context> <context context-type="linenumber">73</context>
@@ -577,6 +622,7 @@
</trans-unit> </trans-unit>
<trans-unit id="4d92a0395dd66778a931460118626c5794a3fc7a" datatype="html"> <trans-unit id="4d92a0395dd66778a931460118626c5794a3fc7a" datatype="html">
<source>Add Users</source> <source>Add Users</source>
<target state="translated">Adicionar usuário</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context> <context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context>
<context context-type="linenumber">90</context> <context context-type="linenumber">90</context>
@@ -585,6 +631,7 @@
</trans-unit> </trans-unit>
<trans-unit id="b0d7dd8a1b0349622d6e0c6e643e24a9ea0efa1d" datatype="html"> <trans-unit id="b0d7dd8a1b0349622d6e0c6e643e24a9ea0efa1d" datatype="html">
<source>Edit Role</source> <source>Edit Role</source>
<target state="needs-translation">Editar Perfil</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context> <context context-type="sourcefile">src/app/components/modify-users/modify-users.component.html</context>
<context context-type="linenumber">95</context> <context context-type="linenumber">95</context>
@@ -609,6 +656,7 @@
</trans-unit> </trans-unit>
<trans-unit id="b4e61d531b8db72449f043f122119da964f4fc54" datatype="html"> <trans-unit id="b4e61d531b8db72449f043f122119da964f4fc54" datatype="html">
<source>File type</source> <source>File type</source>
<target state="translated">Tipo de arquivo</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context> <context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context>
<context context-type="linenumber">52</context> <context context-type="linenumber">52</context>
@@ -617,6 +665,7 @@
</trans-unit> </trans-unit>
<trans-unit id="a47b663952ecf47fd8bc942a1c08ff0d3893bba5" datatype="html"> <trans-unit id="a47b663952ecf47fd8bc942a1c08ff0d3893bba5" datatype="html">
<source>Both</source> <source>Both</source>
<target state="translated">Ambos</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context> <context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context>
<context context-type="linenumber">54</context> <context context-type="linenumber">54</context>
@@ -1012,6 +1061,7 @@
</trans-unit> </trans-unit>
<trans-unit id="f0baeb8b69d120073b6d60d34785889b0c3232c8" datatype="html"> <trans-unit id="f0baeb8b69d120073b6d60d34785889b0c3232c8" datatype="html">
<source>Audio</source> <source>Audio</source>
<target state="translated">Áudio</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/create-playlist/create-playlist.component.html</context> <context context-type="sourcefile">src/app/create-playlist/create-playlist.component.html</context>
<context context-type="linenumber">12</context> <context context-type="linenumber">12</context>
@@ -1020,6 +1070,7 @@
</trans-unit> </trans-unit>
<trans-unit id="2d1ea268a6a9f483dbc2cbfe19bf4256a57a6af4" datatype="html"> <trans-unit id="2d1ea268a6a9f483dbc2cbfe19bf4256a57a6af4" datatype="html">
<source>Video</source> <source>Video</source>
<target state="translated">Vídeo</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/create-playlist/create-playlist.component.html</context> <context context-type="sourcefile">src/app/create-playlist/create-playlist.component.html</context>
<context context-type="linenumber">13</context> <context context-type="linenumber">13</context>
@@ -1176,6 +1227,7 @@
</trans-unit> </trans-unit>
<trans-unit id="024886ca34a6f309e3e51c2ed849320592c3faaa" datatype="html"> <trans-unit id="024886ca34a6f309e3e51c2ed849320592c3faaa" datatype="html">
<source>User name</source> <source>User name</source>
<target state="translated">Nome de usuário</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/add-user-dialog/add-user-dialog.component.html</context> <context context-type="sourcefile">src/app/dialogs/add-user-dialog/add-user-dialog.component.html</context>
<context context-type="linenumber">6</context> <context context-type="linenumber">6</context>
@@ -1184,6 +1236,7 @@
</trans-unit> </trans-unit>
<trans-unit id="c32ef07f8803a223a83ed17024b38e8d82292407" datatype="html"> <trans-unit id="c32ef07f8803a223a83ed17024b38e8d82292407" datatype="html">
<source>Password</source> <source>Password</source>
<target state="translated">Senha</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/add-user-dialog/add-user-dialog.component.html</context> <context context-type="sourcefile">src/app/dialogs/add-user-dialog/add-user-dialog.component.html</context>
<context context-type="linenumber">11</context> <context context-type="linenumber">11</context>
@@ -1272,6 +1325,7 @@
</trans-unit> </trans-unit>
<trans-unit id="98a8a42e5efffe17ab786636ed0139b4c7032d0e" datatype="html"> <trans-unit id="98a8a42e5efffe17ab786636ed0139b4c7032d0e" datatype="html">
<source>Drag and Drop</source> <source>Drag and Drop</source>
<target state="translated">Arraste e Solte</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.html</context> <context context-type="sourcefile">src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.html</context>
<context context-type="linenumber">11</context> <context context-type="linenumber">11</context>
@@ -1515,7 +1569,7 @@
<note priority="1" from="description">Randomize order when playing checkbox label</note> <note priority="1" from="description">Randomize order when playing checkbox label</note>
</trans-unit> </trans-unit>
<trans-unit id="33026f57ea65cd9c8a5d917a08083f71a718933a" datatype="html"> <trans-unit id="33026f57ea65cd9c8a5d917a08083f71a718933a" datatype="html">
<source>Normal order </source> <source>Normal order</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/modify-playlist/modify-playlist.component.html</context> <context context-type="sourcefile">src/app/dialogs/modify-playlist/modify-playlist.component.html</context>
<context context-type="linenumber">18</context> <context context-type="linenumber">18</context>
@@ -1523,7 +1577,7 @@
<note priority="1" from="description">Normal order</note> <note priority="1" from="description">Normal order</note>
</trans-unit> </trans-unit>
<trans-unit id="29376982b1205d9d6ea3d289e8e2f8e1ac2839b1" datatype="html"> <trans-unit id="29376982b1205d9d6ea3d289e8e2f8e1ac2839b1" datatype="html">
<source>Reverse order </source> <source>Reverse order</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/modify-playlist/modify-playlist.component.html</context> <context context-type="sourcefile">src/app/dialogs/modify-playlist/modify-playlist.component.html</context>
<context context-type="linenumber">19</context> <context context-type="linenumber">19</context>
@@ -2872,6 +2926,580 @@
</context-group> </context-group>
<note priority="1" from="description">Select a version</note> <note priority="1" from="description">Select a version</note>
</trans-unit> </trans-unit>
<trans-unit id="c150a30bbbdb175b4d08820196a9acb66b167653" datatype="html">
<source>Archives empty</source>
<target state="translated">Arquivos vazios</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">72</context>
</context-group>
<note priority="1" from="description">Archives empty</note>
</trans-unit>
<trans-unit id="7022070615528435141" datatype="html">
<source>Delete</source>
<target state="translated">Deletar</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">154</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.ts</context>
<context context-type="linenumber">160</context>
</context-group>
</trans-unit>
<trans-unit id="347407180135731058" datatype="html">
<source>Audio</source>
<target state="translated">Áudio</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">44</context>
</context-group>
</trans-unit>
<trans-unit id="7182974689040833178" datatype="html">
<source>Resume</source>
<target state="translated">Retomar</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">74</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">80</context>
</context-group>
</trans-unit>
<trans-unit id="39921032161993566" datatype="html">
<source>Successfully created playlist!</source>
<target state="translated">Playlist criada com sucesso!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/custom-playlists/custom-playlists.component.ts</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>
<trans-unit id="2159130950882492111" datatype="html">
<source>Cancel</source>
<target state="translated">Cancelar</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">86</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/confirm-dialog/confirm-dialog.component.ts</context>
<context context-type="linenumber">15</context>
</context-group>
</trans-unit>
<trans-unit id="1236604279860679031" datatype="html">
<source>Restart</source>
<target state="translated">Reiniciar</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">62</context>
</context-group>
</trans-unit>
<trans-unit id="9042260521669277115" datatype="html">
<source>Pause</source>
<target state="translated">Pausar</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">68</context>
</context-group>
</trans-unit>
<trans-unit id="4516710756538206828" datatype="html">
<source>Failed to clear logs!</source>
<target state="translated">Não foi possível remover os logs!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.ts</context>
<context context-type="linenumber">77</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.ts</context>
<context context-type="linenumber">80</context>
</context-group>
</trans-unit>
<trans-unit id="5029464708330583545" datatype="html">
<source>Use advanced download mode</source>
<target state="translated">Usar o modo de download avançado</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">21</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-user/manage-user.component.ts</context>
<context context-type="linenumber">23</context>
</context-group>
</trans-unit>
<trans-unit id="4250042184551786923" datatype="html">
<source>File manager</source>
<target state="translated">Gerenciador de arquivos</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">17</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-user/manage-user.component.ts</context>
<context context-type="linenumber">19</context>
</context-group>
</trans-unit>
<trans-unit id="4278268519633335280" datatype="html">
<source>Use downloads manager</source>
<target state="translated">Usar o gerenciador de downloads</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">22</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-user/manage-user.component.ts</context>
<context context-type="linenumber">24</context>
</context-group>
</trans-unit>
<trans-unit id="3426988753455920032" datatype="html">
<source>Use tasks manager</source>
<target state="translated">Usar o gerenciador de tarefas</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">23</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-user/manage-user.component.ts</context>
<context context-type="linenumber">25</context>
</context-group>
</trans-unit>
<trans-unit id="1812379335568847528" datatype="html">
<source>Subscriptions</source>
<target state="translated">Inscrições</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">19</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-user/manage-user.component.ts</context>
<context context-type="linenumber">21</context>
</context-group>
</trans-unit>
<trans-unit id="a4a4a5f03d7d0831ccf6774094e66a9507a42b58" datatype="html">
<source>Clear downloads</source>
<target state="translated">Limpar downloads</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">91</context>
</context-group>
<note priority="1" from="description">Clear downloads</note>
</trans-unit>
<trans-unit id="8485375438204712002" datatype="html">
<source>Finished downloads</source>
<target state="translated">Downloads terminados</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">182</context>
</context-group>
</trans-unit>
<trans-unit id="5801924165267871854" datatype="html">
<source>Paused downloads</source>
<target state="translated">Downloads pausados</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">186</context>
</context-group>
</trans-unit>
<trans-unit id="2723988842145709249" datatype="html">
<source>Errored downloads</source>
<target state="translated">Downloads com erro</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">190</context>
</context-group>
</trans-unit>
<trans-unit id="7114033980971410157" datatype="html">
<source>Failed to pause download! See server logs for more info.</source>
<target state="translated">Não foi possível pausar o downloads! Veja os logs do servidor para mais informações.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">214</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">266</context>
</context-group>
</trans-unit>
<trans-unit id="5223827577229167333" datatype="html">
<source>Failed to pause all downloads! See server logs for more info.</source>
<target state="translated">Não foi possível pausar todos os downloads! Veja os logs do servidor para mais informações.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">222</context>
</context-group>
</trans-unit>
<trans-unit id="5823550543348347814" datatype="html">
<source>Cleared downloads!</source>
<target state="translated">Downloads removidos!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">203</context>
</context-group>
</trans-unit>
<trans-unit id="5456775416888155476" datatype="html">
<source>Failed to resume download! See server logs for more info.</source>
<target state="translated">Não foi possível retomar o download! Veja os logs do servidor para mais informações.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">230</context>
</context-group>
</trans-unit>
<trans-unit id="6268791413935580107" datatype="html">
<source>Failed to resume all downloads! See server logs for more info.</source>
<target state="translated">Não foi possível retomar todos os downloads! Veja os logs do servidor para mais informações.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">238</context>
</context-group>
</trans-unit>
<trans-unit id="571023367671104036" datatype="html">
<source>Failed to restart download! See server logs for more info.</source>
<target state="translated">Não foi possível reiniciar o download! Veja os logs do servidor para mais informações.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">246</context>
</context-group>
</trans-unit>
<trans-unit id="4529487534884306633" datatype="html">
<source>Failed to cancel download! See server logs for more info.</source>
<target state="translated">Não foi possível cancelar o download! Veja os logs do servidor para mais informações.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">258</context>
</context-group>
</trans-unit>
<trans-unit id="c1b7e6d75ff4285c7636c67e5ef259629b81725b" datatype="html">
<source>Confirm Password</source>
<target state="translated">Confirme a Senha</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/login/login.component.html</context>
<context context-type="linenumber">32</context>
</context-group>
<note priority="1" from="description">Confirm Password</note>
</trans-unit>
<trans-unit id="1019978815798793544" datatype="html">
<source>Failed to retrieve logs!</source>
<target state="translated">Não foi possível recuperar os logs!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.ts</context>
<context context-type="linenumber">46</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.ts</context>
<context context-type="linenumber">51</context>
</context-group>
</trans-unit>
<trans-unit id="87406377200084623" datatype="html">
<source>Logs copied to clipboard!</source>
<target state="translated">Logs copiados para a área de transferência!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.ts</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>
<trans-unit id="c748ac656af9f13998206ef2c52018dd418b0483" datatype="html">
<source>Archives</source>
<target state="translated">Arquivos</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">26</context>
</context-group>
<note priority="1" from="description">Archives menu label</note>
</trans-unit>
<trans-unit id="45cc8ca94b5a50842a9a8ef804a5ab089a38ae5c" datatype="html">
<source>ID</source>
<target state="translated">ID</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">47</context>
</context-group>
<note priority="1" from="description">ID</note>
</trans-unit>
<trans-unit id="28da11220a3377ddce3c7948825d33101f142782" datatype="html">
<source>Extractor</source>
<target state="translated">Extrator</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">57</context>
</context-group>
<note priority="1" from="description">Extractor</note>
</trans-unit>
<trans-unit id="5ca707824ab93066c7d9b44e1b8bf216725c2c22" datatype="html">
<source>Filter</source>
<target state="translated">Filtrar</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
<note priority="1" from="description">Filter</note>
</trans-unit>
<trans-unit id="a2f14a73f7a6e94479f67423cc51102da8d6f524" datatype="html">
<source>None</source>
<target state="translated">Nenhum</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">84</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">126</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">27</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/generate-rss-url/generate-rss-url.component.html</context>
<context context-type="linenumber">36</context>
</context-group>
<note priority="1" from="description">None</note>
</trans-unit>
<trans-unit id="c41475a25c9f9d9639db9efa56637603a77528b4" datatype="html">
<source>Download archive</source>
<target state="translated">Baixar arquivo</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">80</context>
</context-group>
<note priority="1" from="description">Download archive</note>
</trans-unit>
<trans-unit id="8953483585652369683" datatype="html">
<source>Archive successfully imported!</source>
<target state="translated">Arquivo importado com sucesso!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">130</context>
</context-group>
</trans-unit>
<trans-unit id="3159807825117518005" datatype="html">
<source>Delete archives</source>
<target state="translated">Deletar arquivos</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">152</context>
</context-group>
</trans-unit>
<trans-unit id="8425787787095143143" datatype="html">
<source>Would you like to delete <x id="selected archives amount" equiv-text="this.selection.selected.length"/> archive(s)?</source>
<target state="needs-translation">Você gostaria de apagar <x id="selected archives amount" equiv-text="this.selection.selected.length"/>arquivo(s)?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">153</context>
</context-group>
</trans-unit>
<trans-unit id="2525880134753073592" datatype="html">
<source>Successfully deleted archive items!</source>
<target state="translated">Itens deletados com sucesso!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">172</context>
</context-group>
</trans-unit>
<trans-unit id="8224301330941792118" datatype="html">
<source>Failed to delete archive items!</source>
<target state="translated">Não foi possível deletar os arquivos!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">174</context>
</context-group>
</trans-unit>
<trans-unit id="2070856663109337061" datatype="html">
<source>ERROR: failed to create playlist!</source>
<target state="translated">ERRO: Não foi possível criar a playlist!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/custom-playlists/custom-playlists.component.ts</context>
<context context-type="linenumber">58</context>
</context-group>
</trans-unit>
<trans-unit id="820184305380634591" datatype="html">
<source>Playlist successfully removed.</source>
<target state="translated">Playlist removida com sucesso.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/custom-playlists/custom-playlists.component.ts</context>
<context context-type="linenumber">100</context>
</context-group>
</trans-unit>
<trans-unit id="3299455901271096793" datatype="html">
<source>Clear downloads</source>
<target state="translated">Limpar downloads</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">175</context>
</context-group>
</trans-unit>
<trans-unit id="5215119607776782829" datatype="html">
<source>Select downloads to clear</source>
<target state="translated">Selecione os downloads para limpar</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">176</context>
</context-group>
</trans-unit>
<trans-unit id="2293081271355999967" datatype="html">
<source>Logs successfully cleared!</source>
<target state="translated">Logs limpos com sucesso!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/logs-viewer/logs-viewer.component.ts</context>
<context context-type="linenumber">75</context>
</context-group>
</trans-unit>
<trans-unit id="019d4bd6a5690f0cfa0ecf346a4e6bf7f0d8debb" datatype="html">
<source>Remove</source>
<target state="translated">Remover</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.html</context>
<context context-type="linenumber">23</context>
</context-group>
<note priority="1" from="description">Remove</note>
</trans-unit>
<trans-unit id="6219551536751479443" datatype="html">
<source>Finished downloading</source>
<target state="translated">Download terminado</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
<trans-unit id="5947241266456580665" datatype="html">
<source>Download failed</source>
<target state="translated">Download falhou</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">18</context>
</context-group>
</trans-unit>
<trans-unit id="5a105e7bd7e7db6ea211fe950fc9f317379acceb" datatype="html">
<source>No notifications available</source>
<target state="translated">Nenhuma notificação disponível</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
<note priority="1" from="description">No notifications available</note>
</trans-unit>
<trans-unit id="6876310993601590130" datatype="html">
<source>Download completed</source>
<target state="translated">Download terminado</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications/notifications.component.ts</context>
<context context-type="linenumber">23</context>
</context-group>
</trans-unit>
<trans-unit id="3640026747176198246" datatype="html">
<source>Watch content</source>
<target state="translated">Ver conteúdo</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">50</context>
</context-group>
</trans-unit>
<trans-unit id="8456659390937171831" datatype="html">
<source>Show error</source>
<target state="translated">Mostrar erro</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>
<trans-unit id="8443034725057696949" datatype="html">
<source>Task finished</source>
<target state="translated">Tarefa terminada</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">19</context>
</context-group>
</trans-unit>
<trans-unit id="8564202903947049539" datatype="html">
<source>Play</source>
<target state="needs-translation">Play</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">30</context>
</context-group>
</trans-unit>
<trans-unit id="8643601595923420698" datatype="html">
<source>Retry download</source>
<target state="translated">Tentar baixar novamente</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">31</context>
</context-group>
</trans-unit>
<trans-unit id="8571838164752006148" datatype="html">
<source>View error</source>
<target state="translated">Ver erro</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">32</context>
</context-group>
</trans-unit>
<trans-unit id="5709555629190115111" datatype="html">
<source>View task</source>
<target state="translated">Ver tarefa</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/notifications-list/notifications-list.component.ts</context>
<context context-type="linenumber">33</context>
</context-group>
</trans-unit>
<trans-unit id="51a161ce175abcd44f6c6cbd0e996681bf553ac3" datatype="html">
<source>Delete selected</source>
<target state="translated">Apagar selecionados</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">77</context>
</context-group>
<note priority="1" from="description">Delete selected</note>
</trans-unit>
<trans-unit id="4b3972c3e9485218508a95f7a4ce7758e3f09ced" datatype="html">
<source>Upload</source>
<target state="translated">Upload</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.html</context>
<context context-type="linenumber">137</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/dialogs/cookies-uploader-dialog/cookies-uploader-dialog.component.html</context>
<context context-type="linenumber">30</context>
</context-group>
<note priority="1" from="description">Upload</note>
</trans-unit>
<trans-unit id="6549265851868599441" datatype="html">
<source>Video</source>
<target state="translated">Vídeo</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/archive-viewer/archive-viewer.component.ts</context>
<context context-type="linenumber">40</context>
</context-group>
</trans-unit>
<trans-unit id="6080b77234e92ad41bb52653b239c4c4f851317d" datatype="html">
<source>Error</source>
<target state="translated">Erro</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.html</context>
<context context-type="linenumber">39</context>
</context-group>
<note priority="1" from="description">Error</note>
</trans-unit>
<trans-unit id="3961621815065792326" datatype="html">
<source>Failed to clear finished downloads!</source>
<target state="translated">Não foi possível limpar os downloads terminados!</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/downloads/downloads.component.ts</context>
<context context-type="linenumber">201</context>
</context-group>
</trans-unit>
<trans-unit id="4744991787069301975" datatype="html">
<source>Share files</source>
<target state="translated">Compartilhar arquivos</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">20</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-user/manage-user.component.ts</context>
<context context-type="linenumber">22</context>
</context-group>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
export const environment = {
production: false,
codespaces: true
};

View File

@@ -1,3 +1,4 @@
export const environment = { export const environment = {
production: true production: true,
codespaces: false
}; };

View File

@@ -4,5 +4,6 @@
// The list of which env maps to which file can be found in `.angular-cli.json`. // The list of which env maps to which file can be found in `.angular-cli.json`.
export const environment = { export const environment = {
production: true production: false,
codespaces: false
}; };

View File

@@ -1,4 +1,5 @@
/* You can add global styles to this file, and also import other style files */ /* You can add global styles to this file, and also import other style files */
@use '@angular/material' as mat;
@import '~material-icons/iconfont/material-icons.css'; @import '~material-icons/iconfont/material-icons.css';
@@ -12,7 +13,6 @@
html, body { height: 100%; } html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
@import '@angular/material/theming';
// Plus imports for other components in your app. // Plus imports for other components in your app.
/*// Typography /*// Typography
@@ -24,22 +24,22 @@ $custom-typography: mat-typography-config(
@include angular-material-typography($custom-typography); @include angular-material-typography($custom-typography);
*/ */
// Default colors // Default colors
$my-app-primary: mat-palette($mat-light-blue, 700, 100, 800); $my-app-primary: mat.define-palette(mat.$light-blue-palette, 700, 100, 800); // mat-palette($mat-light-blue, 700, 100, 800);
$my-app-accent: mat-palette($mat-blue, 700, 100, 800); $my-app-accent: mat.define-palette(mat.$blue-palette, 700, 100, 800);
$my-app-warn: mat-palette($mat-red, 700, 100, 800); $my-app-warn: mat.define-palette(mat.$red-palette, 700, 100, 800);
$my-app-theme: mat-light-theme($my-app-primary, $my-app-accent, $my-app-warn); $my-app-theme: mat.define-light-theme((color: (primary: $my-app-primary, accent: $my-app-accent, warn: $my-app-warn)));
@include angular-material-theme($my-app-theme); @include mat.all-component-themes($my-app-theme);
// Dark theme // Dark theme
$dark-primary: mat-palette($mat-light-blue, 700, 100, 800); $dark-primary: mat.define-palette(mat.$light-blue-palette, 700, 100, 800);
$dark-accent: mat-palette($mat-blue); $dark-accent: mat.define-palette(mat.$blue-palette);
$dark-warn: mat-palette($mat-deep-orange); $dark-warn: mat.define-palette(mat.$deep-orange-palette);
$dark-theme: mat-dark-theme($dark-primary, $dark-accent, $dark-warn); $dark-theme: mat.define-dark-theme((color: (primary: $dark-primary, accent: $dark-accent, warn: $dark-warn)));
.dark-theme { .dark-theme {
@include angular-material-theme($dark-theme); @include mat.all-component-themes($dark-theme);
} }
.mat-mdc-outlined-button, .mat-mdc-raised-button, .mat-mdc-flat-button { .mat-mdc-outlined-button, .mat-mdc-raised-button, .mat-mdc-flat-button {
@@ -47,14 +47,14 @@ $dark-theme: mat-dark-theme($dark-primary, $dark-accent, $dark-warn);
} }
// Light theme // Light theme
$light-primary: mat-palette($mat-grey, 200, 500, 300); $light-primary: mat.define-palette(mat.$grey-palette, 200, 500, 300);
$light-accent: mat-palette($mat-brown, 200); $light-accent: mat.define-palette(mat.$brown-palette, 200);
$light-warn: mat-palette($mat-deep-orange, 200); $light-warn: mat.define-palette(mat.$deep-orange-palette, 200);
$light-theme: mat-light-theme($light-primary, $light-accent, $light-warn); $light-theme: mat.define-light-theme((color: (primary: $light-primary, accent: $light-accent, warn: $light-warn)));
.light-theme { .light-theme {
@include angular-material-theme($light-theme); @include mat.all-component-themes($light-theme);
} }
.no-outline { .no-outline {
@@ -65,9 +65,7 @@ $light-theme: mat-light-theme($light-primary, $light-accent, $light-warn);
// Include the common styles for Angular Material. We include this here so that you only // Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app. // have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once! // Be sure that you only ever include this mixin once!
@include mat-core(); @include mat.core();
// @import '../node_modules/@angular/material/theming';
.centered { .centered {
margin: 0 auto; margin: 0 auto;