Compare commits

...

105 Commits

Author SHA1 Message Date
Isaac Abadi
d37287541f Reverted ubuntu version to 20.04 2022-05-05 21:37:02 -04:00
Glassed Silver
4d00960fcf Merge pull request #607 from Tzahi12345/fix-603
Startup crash hotfix
2022-05-05 16:06:58 +02:00
Tzahi12345
6e8ca9d843 Fixed bug that caused verifyBinaryExistsLinux to crash the server on startup 2022-05-05 09:27:56 -04:00
Tzahi12345
83a9d93ce5 Merge pull request #602 from Tzahi12345/heroku-fixes-and-repo-cleanup
Various bug fixes and heroku improvements
2022-05-05 02:47:45 -04:00
Isaac Abadi
2707b09627 Merge branch 'db-bug-fixes' of https://github.com/Tzahi12345/YoutubeDL-Material into heroku-fixes-and-repo-cleanup 2022-05-05 02:36:00 -04:00
Isaac Abadi
8118906b0a Binary path is now confirmed for linux, solves #601 2022-05-05 02:31:28 -04:00
Isaac Abadi
2ad42ebf27 Fixed issue where python couldn't be found 2022-05-05 02:29:31 -04:00
Isaac Abadi
2b3490e52c Updated heroku dockerfile 2022-05-05 01:08:43 -04:00
Isaac Abadi
55fd60acd3 Merge branch 'master' of https://github.com/Tzahi12345/YoutubeDL-Material into heroku-fixes-and-repo-cleanup 2022-05-05 01:00:35 -04:00
Isaac Abadi
664b635439 Updated English source translation file 2022-05-05 00:56:46 -04:00
Isaac Abadi
692d6eeaac Added edit button for playlist subscriptions 2022-05-04 22:02:43 -04:00
Isaac Abadi
9515d5a1b0 Fixed issue where additional args wouldn't properly inject 2022-05-04 22:01:51 -04:00
Isaac Abadi
24df238ff9 Merge branch 'master' of https://github.com/Tzahi12345/YoutubeDL-Material into db-bug-fixes 2022-05-04 19:08:06 -04:00
Glassed Silver
f5e6815200 Merge pull request #597 from weblate/weblate-youtubedl-material-ytdl-material
Translations update from Hosted Weblate
2022-05-05 00:03:36 +02:00
Glassed Silver
0e5efd003e Merge pull request #598 from GlassedSilver/master
Fixing Ubuntu-introduced issues and further improvements
2022-05-04 23:53:59 +02:00
GlassedSilver
3e7ef766de Best to just put fix-scripts in /backend 👍 2022-05-04 23:40:42 +02:00
GlassedSilver
17fdd0d788 update usage instr. for fix-script in comment 2022-05-04 20:57:13 +02:00
GlassedSilver
ce3e645129 for now: user has to DIY chmod +x fix-scripts 2022-05-04 20:54:21 +02:00
GlassedSilver
acca2d0de2 syntax devil struck again 2022-05-04 20:19:52 +02:00
GlassedSilver
31b4fcb9fc We're now using gosu to switch our user down :) 2022-05-04 19:58:00 +02:00
GlassedSilver
336d7a09bd set fix-scripts folder permissions more reliably 2022-05-04 18:31:28 +02:00
GlassedSilver
7c31a10498 ux/guidance improvements 2022-05-04 17:23:04 +02:00
GlassedSilver
a94b8f376e permission needs to be set with octal notation 2022-05-04 17:22:21 +02:00
GlassedSilver
84d33b0f82 fix missing execution permission for fix scripts 2022-05-04 17:21:06 +02:00
GlassedSilver
3abf8ecfc3 Merge branch 'master' of https://github.com/GlassedSilver/YoutubeDL-Material into master 2022-05-04 16:55:24 +02:00
GlassedSilver
5b919b2b18 Fix scripts folder: copy content AND parent folder 2022-05-04 16:55:22 +02:00
Glassed Silver
e290dc0a25 Fixing permissions of ffmpeg and ffprobe
Since we didn't specify UID and GID in copy command before, they were run as root causing permissions conflicts
The ffmpeg stage doesn't need the env variables henceforth
2022-05-04 15:11:35 +02:00
GlassedSilver
a54f07e93a remove white spaces from script & add usage instr. 2022-05-04 12:19:05 +02:00
GlassedSilver
8336d886e9 fix-scripts need to be owned and run by root 2022-05-04 12:16:58 +02:00
GlassedSilver
6a56b5b065 add fix-scripts to docker image 2022-05-04 11:59:45 +02:00
GlassedSilver
7aca8ab060 entrypoint updated for su 2022-05-04 10:18:06 +02:00
GlassedSilver
8cc5c4f733 no need for suexec anymore apparently 2022-05-04 10:16:49 +02:00
Heimen Stoffels
c5eacbb70c Translated using Weblate (Dutch)
Currently translated at 100.0% (302 of 302 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/nl/
2022-05-03 12:11:25 +02:00
Eric
7268242691 Translated using Weblate (Chinese (Simplified))
Currently translated at 83.7% (253 of 302 strings)

Translation: YoutubeDL-Material/ytdl-material
Translate-URL: https://hosted.weblate.org/projects/youtubedl-material/ytdl-material/zh_Hans/
2022-05-03 12:11:25 +02:00
GlassedSilver
d0f5518d31 suexec needs to be installed 2022-05-03 09:44:43 +02:00
GlassedSilver
713a97f75a reintegrate suexec 2022-05-03 08:44:55 +02:00
GlassedSilver
0bdcfa3244 Merge branch 'master' of https://github.com/GlassedSilver/YoutubeDL-Material into master 2022-05-03 08:27:49 +02:00
GlassedSilver
849c1927d3 Add fix script for interactive permission fixing. 2022-05-03 08:27:42 +02:00
GlassedSilver
06ca9cbe76 build excludes: now matches ANY *.md :) 2022-05-03 08:26:37 +02:00
GlassedSilver
8e4a3119ab 🚀 bye unnecessary build cleanups (not last stage) 2022-05-03 08:25:38 +02:00
Isaac Abadi
ec1ccf6888 Fixed bug that incorrectly told the UI that DB transfer failed 2022-05-03 00:35:02 -04:00
Isaac Abadi
c33e8010b3 Additional args now replace existing ones intelligently 2022-05-03 00:34:36 -04:00
Glassed Silver
57fd991d5c Merge pull request #595 from GlassedSilver/master
Permissions fixes
2022-05-02 17:27:32 +02:00
GlassedSilver
44c1a34c67 Permissions fix for ffmpeg executable 2022-05-02 13:33:20 +02:00
GlassedSilver
9f740020af possible fix 2022-05-02 13:14:57 +02:00
GlassedSilver
4d4bc76549 Use Ubuntu 22.04, use nodejs from ubuntu repo 2022-05-02 12:59:34 +02:00
GlassedSilver
93ce498e94 switch to ubuntu 21.10 as we wait for nodesource 2022-05-02 08:20:48 +02:00
Glassed Silver
e5b256b8df Merge pull request #592 from Tzahi12345/4.3-bug-fixes
Various bug fixes
2022-05-02 08:01:23 +02:00
Tzahi12345
05ea5a816f Merge pull request #591 from weblate/weblate-youtubedl-material-ytdl-material
Translations update from Hosted Weblate
2022-05-02 01:47:10 -04:00
Isaac Abadi
b3342d89c1 Fixed #563 where empty languages existed in language select 2022-05-02 01:46:25 -04:00
Isaac Abadi
7bfb441a00 Fixed bug where files couldn't be deleted with archive mode enabled fixes #487 2022-05-02 01:44:43 -04:00
Tzahi12345
01fd2fb990 Deleted translation using Weblate (Hindi) 2022-05-02 07:24:10 +02:00
Tzahi12345
1bb4d9dbf6 Deleted translation using Weblate (Basa (Cameroon)) 2022-05-02 07:22:08 +02:00
Glassed Silver
5e23932146 Merge pull request #589 from Tzahi12345/better-docker-pr-tests
Better docker PR tests
2022-05-02 05:42:49 +02:00
Glassed Silver
d6d3495c5b Merge pull request #590 from GlassedSilver/master
Adding ignore parameters to docker build-and-push
2022-05-02 05:27:16 +02:00
Isaac Abadi
a36761e96a Fixed frontend build error 2022-05-01 23:24:15 -04:00
Isaac Abadi
88c16d7195 All setIntervals on the frontend are now properly destroyed 2022-05-01 23:23:19 -04:00
Isaac Abadi
8a323f028d Fixed bug where subscription avatars were missing 2022-05-01 23:15:09 -04:00
Isaac Abadi
a68726e7cb Removed deprecated armhf.Dockerfile 2022-05-01 23:13:11 -04:00
Isaac Abadi
dab0b7a8b6 Updated Angular version in readme 2022-05-01 23:13:00 -04:00
Isaac Abadi
9f9054ed9d Removed secrets from docker-pr.yml 2022-05-01 22:59:55 -04:00
GlassedSilver
77e8cbc6b5 Adding ignore parameters to docker build-and-push 2022-05-02 04:21:12 +02:00
Isaac Abadi
4c06c430eb Converted docker-compose build to docker build for docker-pr GH action 2022-05-01 21:21:39 -04:00
Isaac Abadi
2981f843c3 Added docker build PR check 2022-05-01 21:11:21 -04:00
Isaac Abadi
3a48ff2d50 docker build and push action now uses secrets for DockerHub username, repo, and tag 2022-05-01 21:11:01 -04:00
Glassed Silver
ac2c3dc8a1 Merge pull request #588 from GlassedSilver/master
removing strict SSL from npm config
2022-05-02 03:00:39 +02:00
GlassedSilver
0abe252d1e we need to find a different build check solution 2022-05-02 02:59:25 +02:00
GlassedSilver
f5f00e1732 fix name 2022-05-02 02:12:10 +02:00
GlassedSilver
c309e41a91 Merge branch 'master' of https://github.com/GlassedSilver/YoutubeDL-Material into master 2022-05-02 02:09:44 +02:00
GlassedSilver
754d837059 adding docker-pr-check.yml 2022-05-02 02:09:37 +02:00
Glassed Silver
d5626f1dae Dockerfile: wget not needed 2022-05-01 23:29:51 +02:00
GlassedSilver
9c0733453a removing strict SSL from npm config 2022-05-01 23:00:01 +02:00
Glassed Silver
2a41028253 Update Dockerfile 2022-05-01 20:42:45 +02:00
Glassed Silver
67b2e480f8 Merge pull request #586 from dejan995/master
Clean up docker image
2022-05-01 19:38:39 +02:00
dejan.petrov@dapmn.com
2cdc1cee98 Fix for #585
Added the DEBIAN_FRONTEND=noninteractive variable to all stages. This should stop the build from failing.
Also added --no-install-recommends to install only the requested packages.
This might break stuff, but I'm not sure though.
2022-05-01 18:14:27 +02:00
dejan.petrov@dapmn.com
bd1ed2b705 Clean up docker image
Added some commands to clean up the image after apt-get does its thing.
It should shave off a couple of megabytes, nothing to big though.
2022-05-01 18:02:46 +02:00
Glassed Silver
33ca0f0817 Merge pull request #584 from GlassedSilver/master
wow that was a bunch of work, but...
2022-05-01 12:30:49 +02:00
GlassedSilver
d5ab0d7b96 I'm getting sleepy, why am I still pushing through 2022-05-01 11:54:19 +02:00
GlassedSilver
777aebe508 apparently we still need npm in the last stretch.. 2022-05-01 11:52:35 +02:00
GlassedSilver
efaecaa059 use yarn in apt installs instead of npm 2022-05-01 11:48:12 +02:00
GlassedSilver
39ddefab5c fix dependencies needed for our apt packages 2022-05-01 11:37:39 +02:00
GlassedSilver
60f2ab449f yea 2022-05-01 11:31:53 +02:00
GlassedSilver
958f80e200 the good? I learn a lot about Docker building 2022-05-01 11:28:34 +02:00
GlassedSilver
7aa5c1bf7f syyyyntax 2022-05-01 11:21:45 +02:00
GlassedSilver
3bcbe0d3e7 fix dependency node-gyp (>= 3.6.2~) needed 2022-05-01 11:04:59 +02:00
GlassedSilver
80fcecdaea it's a learning experience 2022-05-01 10:57:21 +02:00
GlassedSilver
0329cd9718 don't think we need to install curl twice lol 2022-05-01 10:51:20 +02:00
GlassedSilver
493e876a97 syntax fixes are fun 2022-05-01 10:48:27 +02:00
Glassed Silver
574edd74ab Merge pull request #583 from GlassedSilver/master
I did warn you I will test docker builds this way
2022-05-01 10:41:10 +02:00
GlassedSilver
fe91484f24 I did warn you I will test docker builds this way 2022-05-01 10:40:19 +02:00
Glassed Silver
dda6e40a42 Merge pull request #582 from GlassedSilver/master
fix docker-build.sh for ubuntu, what a ride
2022-05-01 10:16:03 +02:00
GlassedSilver
c0fb838931 fix docker-build.sh for ubuntu, what a ride 2022-05-01 10:11:32 +02:00
Glassed Silver
28924cc7a0 Merge pull request #581 from GlassedSilver/master
fix pipefail MIA in ubuntu without specifying bash
2022-05-01 09:36:27 +02:00
GlassedSilver
2527051eab fix pipefail MIA in ubuntu without specifying bash 2022-05-01 09:35:04 +02:00
Glassed Silver
fcf7d14f46 Merge pull request #580 from GlassedSilver/master
Fix for #480 - existing DLs still getting queued
2022-05-01 09:21:48 +02:00
GlassedSilver
0a8aba54d2 Fix for #480 - existing DLs still getting queued 2022-05-01 09:17:23 +02:00
Glassed Silver
2c6485acb2 Merge pull request #577 from GlassedSilver/master
Dockerfile uses Ubuntu 20.04, fix obtain ffmpeg
2022-05-01 09:14:13 +02:00
GlassedSilver
aea4f52267 revert postbuild.mjs file-extension change 2022-05-01 07:12:00 +02:00
GlassedSilver
5ac5fca482 adapt postbuild.mjs to postbuild.js 2022-05-01 06:37:12 +02:00
GlassedSilver
7874f1b71a curl is in fact missing in focal, my bad 2022-05-01 06:29:54 +02:00
Isaac Abadi
02e683add9 Cleaned up unused files 2021-09-21 22:35:49 -06:00
Isaac Abadi
d90b2d3687 Added back stack field in app.json 2021-09-21 21:01:53 -06:00
Isaac Abadi
32b68033e8 Removed stack field from app.json 2021-09-21 21:01:01 -06:00
Isaac Abadi
08027a5c0b Added run statement to heroku.yml 2021-09-21 20:49:38 -06:00
Isaac Abadi
25aac19cfb Replaced procfile with heroku.yml, added heroku-only Dockerfile and updated app.json 2021-09-21 20:33:42 -06:00
36 changed files with 2151 additions and 7686 deletions

27
.github/workflows/docker-pr.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: docker-pr
on:
pull_request:
branches: [master]
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: checkout code
uses: actions/checkout@v2
- name: Set hash
id: vars
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Get current date
id: date
run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
- name: create-json
id: create-json
uses: jsdaniell/create-json@1.1.2
with:
name: "version.json"
json: '{"type": "docker", "tag": "nightly", "commit": "${{ steps.vars.outputs.sha_short }}", "date": "${{ steps.date.outputs.date }}"}'
dir: 'backend/'
- name: Build docker images
run: docker build . -t tzahi12345/youtubedl-material:nightly-pr

View File

@@ -3,6 +3,16 @@ name: docker
on:
push:
branches: [master]
paths-ignore:
- '.github/**'
- '.vscode/**'
- 'chrome-extension/**'
- 'releases/**'
- '**/**.md'
- '**.crx'
- '**.pem'
- '.dockerignore'
- '.gitignore'
jobs:
build-and-push:
@@ -21,7 +31,7 @@ jobs:
uses: jsdaniell/create-json@1.1.2
with:
name: "version.json"
json: '{"type": "docker", "tag": "nightly", "commit": "${{ steps.vars.outputs.sha_short }}", "date": "${{ steps.date.outputs.date }}"}'
json: '{"type": "docker", "tag": "${{secrets.DOCKERHUB_MASTER_TAG}}", "commit": "${{ steps.vars.outputs.sha_short }}", "date": "${{ steps.date.outputs.date }}"}'
dir: 'backend/'
- name: setup platform emulator
uses: docker/setup-qemu-action@v1
@@ -39,4 +49,8 @@ jobs:
file: ./Dockerfile
platforms: linux/amd64,linux/arm,linux/arm64/v8
push: true
tags: tzahi12345/youtubedl-material:nightly
# Defaults:
# DOCKERHUB_USERNAME : tzahi12345
# DOCKERHUB_REPO : youtubedl-material
# DOCKERHUB_MASTER_TAG: nightly
tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }}:${{secrets.DOCKERHUB_MASTER_TAG}}

View File

@@ -1,18 +1,24 @@
FROM ubuntu:20.04 AS ffmpeg
ENV DEBIAN_FRONTEND=noninteractive
COPY docker-build.sh .
RUN sh ./docker-build.sh
#--------------# Stage 2
FROM ubuntu:20.04 as frontend
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get -y install \
wget \
gnupg && \
curl -sL https://deb.nodesource.com/setup_12.x | bash - && \
apt-get -y install \
curl && \
curl -sL https://deb.nodesource.com/setup_12.x | bash - && \
apt-get install -y gnupg \
nodejs \
npm && \
yarn && \
apt-get install -f && \
npm config set strict-ssl false && \
npm install -g @angular/cli
WORKDIR /build
@@ -23,7 +29,7 @@ COPY [ "angular.json", "tsconfig.json", "/build/" ]
COPY [ "src/", "/build/src/" ]
RUN npm run build
#--------------#
#--------------# Final Stage
FROM ubuntu:20.04
@@ -32,26 +38,37 @@ ENV UID=1000 \
USER=youtube \
NO_UPDATE_NOTIFIER=true
ENV DEBIAN_FRONTEND=noninteractive
RUN groupadd -g $GID $USER && useradd --system -g $USER --uid $UID $USER
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - && \
apt-get update && apt-get -y install \
RUN apt-get update && apt-get -y install \
npm \
python2 \
python3 \
atomicparsley
gosu \
atomicparsley && \
apt-get install -f && \
apt-get autoremove --purge && \
apt-get autoremove && \
apt-get clean && \
rm -rf /var/lib/apt
WORKDIR /app
COPY --from=ffmpeg /usr/local/bin/ffmpeg /usr/local/bin/ffmpeg
COPY --from=ffmpeg /usr/local/bin/ffprobe /usr/local/bin/ffprobe
COPY --chown=$UID:$GID --from=ffmpeg [ "/usr/local/bin/ffmpeg", "/usr/local/bin/ffmpeg" ]
COPY --chown=$UID:$GID --from=ffmpeg [ "/usr/local/bin/ffprobe", "/usr/local/bin/ffprobe" ]
COPY --chown=$UID:$GID [ "backend/package.json", "backend/package-lock.json", "/app/" ]
ENV PM2_HOME=/app/pm2
RUN npm install pm2 -g && \
RUN npm config set strict-ssl false && \
npm install pm2 -g && \
npm install && chown -R $UID:$GID ./
# needed for ubuntu, see #596
RUN ln -s /usr/bin/python3 /usr/bin/python
COPY --chown=$UID:$GID --from=frontend [ "/build/backend/public/", "/app/public/" ]
COPY --chown=$UID:$GID [ "/backend/", "/app/" ]
EXPOSE 17442
# ENTRYPOINT [ "/app/entrypoint.sh" ]
ENTRYPOINT [ "/app/entrypoint.sh" ]
CMD [ "pm2-runtime", "pm2.config.js" ]

2
Dockerfile.heroku Normal file
View File

@@ -0,0 +1,2 @@
FROM tzahi12345/youtubedl-material:nightly
CMD [ "pm2-runtime", "pm2.config.js" ]

View File

@@ -1 +0,0 @@
web: npm start --prefix backend

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)
[![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 11](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 13](https://angular.io/) for the frontend, and [Node.js](https://nodejs.org/) on the backend.
Now with [Docker](#Docker) support!

View File

@@ -2,6 +2,7 @@
"name": "YoutubeDL-Material",
"description": "An open-source and self-hosted YouTube downloader based on Google's Material Design specifications.",
"repository": "https://github.com/Tzahi12345/YoutubeDL-Material",
"stack": "container",
"logo": "https://i.imgur.com/GPzvPiU.png",
"keywords": ["youtube-dl", "youtubedl-material", "nodejs"]
}

View File

@@ -1,49 +0,0 @@
FROM alpine:3.12 as frontend
RUN apk add --no-cache \
npm \
curl
RUN npm install -g @angular/cli
WORKDIR /build
RUN curl -L https://github.com/balena-io/qemu/releases/download/v3.0.0%2Bresin/qemu-3.0.0+resin-arm.tar.gz | tar zxvf - -C . && mv qemu-3.0.0+resin-arm/qemu-arm-static .
COPY [ "package.json", "package-lock.json", "/build/" ]
RUN npm install
COPY [ "angular.json", "tsconfig.json", "/build/" ]
COPY [ "src/", "/build/src/" ]
RUN ng build --prod
#--------------#
FROM arm32v7/alpine:3.12
COPY --from=frontend /build/qemu-arm-static /usr/bin
ENV UID=1000 \
GID=1000 \
USER=youtube
RUN addgroup -S $USER -g $GID && adduser -D -S $USER -G $USER -u $UID
RUN apk add --no-cache \
ffmpeg \
npm \
python2 \
su-exec \
&& apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing/ \
atomicparsley
WORKDIR /app
COPY --chown=$UID:$GID [ "backend/package.json", "backend/package-lock.json", "/app/" ]
RUN npm install && chown -R $UID:$GID ./
COPY --chown=$UID:$GID --from=frontend [ "/build/backend/public/", "/app/public/" ]
COPY --chown=$UID:$GID [ "/backend/", "/app/" ]
EXPOSE 17442
ENTRYPOINT [ "/app/entrypoint.sh" ]
CMD [ "node", "app.js" ]

View File

@@ -249,14 +249,6 @@ async function startServer() {
});
}
async function restartServer(is_update = false) {
logger.info(`${is_update ? 'Update complete! ' : ''}Restarting server...`);
// the following line restarts the server through nodemon
fs.writeFileSync(`restart${is_update ? '_update' : '_general'}.json`, 'internal use only');
process.exit(1);
}
async function updateServer(tag) {
// no tag provided means update to the latest version
if (!tag) {
@@ -297,7 +289,7 @@ async function updateServer(tag) {
updating: true,
'details': 'Update complete! Restarting server...'
}
restartServer(true);
utils.restartServer(true);
}, err => {
logger.error(err);
updaterStatus = {
@@ -676,6 +668,7 @@ async function getUrlInfos(url) {
async function startYoutubeDL() {
// auto update youtube-dl
youtubedl_api.verifyBinaryExistsLinux();
const update_available = await youtubedl_api.checkForYoutubeDLUpdate();
if (update_available) await youtubedl_api.updateYoutubeDL(update_available);
}
@@ -764,7 +757,7 @@ app.get('/api/versionInfo', (req, res) => {
app.post('/api/restartServer', optionalJwt, (req, res) => {
// delayed by a little bit so that the client gets a response
setTimeout(() => {restartServer()}, 100);
setTimeout(() => {utils.restartServer()}, 100);
res.send({success: true});
});
@@ -1802,6 +1795,7 @@ app.post('/api/updateTaskData', optionalJwt, async (req, res) => {
app.post('/api/getDBBackups', optionalJwt, async (req, res) => {
const backup_dir = path.join('appdata', 'db_backup');
fs.ensureDirSync(backup_dir);
const db_backups = [];
const candidate_backups = await utils.recFindByExt(backup_dir, 'bak', null, [], false);

View File

@@ -222,4 +222,83 @@ exports.AVAILABLE_PERMISSIONS = [
exports.DETAILS_BIN_PATH = 'node_modules/youtube-dl/bin/details'
// args that have a value after it (e.g. -o <output> or -f <format>)
const YTDL_ARGS_WITH_VALUES = [
'--default-search',
'--config-location',
'--proxy',
'--socket-timeout',
'--source-address',
'--geo-verification-proxy',
'--geo-bypass-country',
'--geo-bypass-ip-block',
'--playlist-start',
'--playlist-end',
'--playlist-items',
'--match-title',
'--reject-title',
'--max-downloads',
'--min-filesize',
'--max-filesize',
'--date',
'--datebefore',
'--dateafter',
'--min-views',
'--max-views',
'--match-filter',
'--age-limit',
'--download-archive',
'-r',
'--limit-rate',
'-R',
'--retries',
'--fragment-retries',
'--buffer-size',
'--http-chunk-size',
'--external-downloader',
'--external-downloader-args',
'-a',
'--batch-file',
'-o',
'--output',
'--output-na-placeholder',
'--autonumber-start',
'--load-info-json',
'--cookies',
'--cache-dir',
'--encoding',
'--user-agent',
'--referer',
'--add-header',
'--sleep-interval',
'--max-sleep-interval',
'-f',
'--format',
'--merge-output-format',
'--sub-format',
'--sub-lang',
'-u',
'--username',
'-p',
'--password',
'-2',
'--twofactor',
'--video-password',
'--ap-mso',
'--ap-username',
'--ap-password',
'--audio-format',
'--audio-quality',
'--recode-video',
'--postprocessor-args',
'--metadata-from-title',
'--fixup',
'--ffmpeg-location',
'--exec',
'--convert-subs'
];
// we're using a Set here for performance
exports.YTDL_ARGS_WITH_VALUES = new Set(YTDL_ARGS_WITH_VALUES);
exports.CURRENT_VERSION = 'v4.2';

View File

@@ -85,8 +85,6 @@ exports.initialize = (input_db, input_users_db) => {
}
exports.connectToDB = async (retries = 5, no_fallback = false, custom_connection_string = null) => {
using_local_db = config_api.getConfigItem('ytdl_use_local_db'); // verify
if (using_local_db && !custom_connection_string) return;
const success = await exports._connectToDB(custom_connection_string);
if (success) return true;
@@ -497,6 +495,7 @@ exports.deleteFile = async (uid, uuid = null, blacklistMode = false) => {
let useYoutubeDLArchive = config_api.getConfigItem('ytdl_use_youtubedl_archive');
if (useYoutubeDLArchive) {
const usersFileFolder = config_api.getConfigItem('ytdl_users_base_path');
const archive_path = uuid ? path.join(usersFileFolder, uuid, 'archives', `archive_${type}.txt`) : path.join('appdata', 'archives', `archive_${type}.txt`);
// get ID from JSON

View File

@@ -485,7 +485,7 @@ exports.generateArgs = async (url, type, options, user_uid = null, simulated = f
}
if (options.additionalArgs && options.additionalArgs !== '') {
downloadConfig = downloadConfig.concat(options.additionalArgs.split(',,'));
downloadConfig = utils.injectArgs(downloadConfig, options.additionalArgs.split(',,'));
}
const rate_limit = config_api.getConfigItem('ytdl_download_rate_limit');

View File

@@ -11,7 +11,7 @@ fi
# chown current working directory to current user
if [ "$*" = "$CMD" ] && [ "$(id -u)" = "0" ]; then
find . \! -user "$UID" -exec chown "$UID:$GID" -R '{}' + || echo "WARNING! Could not change directory ownership. If you manage permissions externally this is fine, otherwise you may experience issues when downloading or deleting videos."
exec su-exec "$UID:$GID" "$0" "$@"
exec gosu "$UID:$GID" "$0" "$@"
fi
exec "$@"

View File

@@ -0,0 +1,58 @@
#!/bin/sh
# INTERACTIVE PERMISSIONS FIX SCRIPT FOR YTDL-M
# Date: 2022-05-03
# If you want to run this script on a bare-metal installation instead of within Docker
# make sure that the paths configured below match your paths! (it's wise to use the full paths)
# USAGE: within your container's bash shell:
# chmod -R +x ./fix-scripts/
# ./fix-scripts/001-fix_download_permissions.sh
# User defines / Docker env defaults
PATH_SUBS=/app/subscriptions
PATH_AUDIO=/app/audio
PATH_VIDS=/app/video
clear -x
echo "\n"
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' - # horizontal line
echo "Welcome to the INTERACTIVE PERMISSIONS FIX SCRIPT FOR YTDL-M."
echo "This script will set YTDL-M's download paths' owner to ${USER} (${UID}:${GID})"
echo "and permissions to the default of 644."
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' - # horizontal line
echo "\n"
# check whether dirs exist
i=0
[ -d $PATH_SUBS ] && i=$((i+1)) && echo "✔ (${i}/3) Found Subscriptions directory at ${PATH_SUBS}"
[ -d $PATH_AUDIO ] && i=$((i+1)) && echo "✔ (${i}/3) Found Audio directory at ${PATH_AUDIO}"
[ -d $PATH_VIDS ] && i=$((i+1)) && echo "✔ (${i}/3) Found Video directory at ${PATH_VIDS}"
# Ask to proceed or cancel, exit on missing paths
case $i in
0)
echo "\nCouldn't find any download path to fix permissions for! \nPlease edit this script to configure!"
exit 2;;
3)
echo "\nFound all download paths to fix permissions for. \nProceed? (Y/N)";;
*)
echo "\nOnly found ${i} out of 3 download paths! Something about this script's config must be wrong. \nProceed anyways? (Y/N)";;
esac
old_stty_cfg=$(stty -g)
stty raw -echo ; answer=$(head -c 1) ; stty $old_stty_cfg # Careful playing with stty
if echo "$answer" | grep -iq "^y" ;then
echo "\n Running jobs now... (this may take a while)\n"
[ -d $PATH_SUBS ] && chown "$UID:$GID" -R $PATH_SUBS && echo "✔ Set owner of ${PATH_SUBS} to ${USER}."
[ -d $PATH_SUBS ] && chmod 644 -R $PATH_SUBS && echo "✔ Set permissions of ${PATH_SUBS} to 644."
[ -d $PATH_AUDIO ] && chown "$UID:$GID" -R $PATH_AUDIO && echo "✔ Set owner of ${PATH_AUDIO} to ${USER}."
[ -d $PATH_AUDIO ] && chmod 644 -R $PATH_AUDIO && echo "✔ Set permissions of ${PATH_AUDIO} to 644."
[ -d $PATH_VIDS ] && chown "$UID:$GID" -R $PATH_VIDS && echo "✔ Set owner of ${PATH_VIDS} to ${USER}."
[ -d $PATH_VIDS ] && chmod 644 -R $PATH_VIDS && echo "✔ Set permissions of ${PATH_VIDS} to 644."
echo "\n✔ Done."
echo "\n If you noticed file access errors those MAY be due to currently running downloads."
echo " Feel free to re-run this script, however download parts should have correct file permissions anyhow. :)"
exit
else
echo "\nOkay, bye."
fi

View File

@@ -141,6 +141,7 @@ async function unsubscribe(sub, deleteMode, user_uid = null) {
if (sub.archive && (await fs.pathExists(sub.archive))) {
const archive_file_path = path.join(sub.archive, 'archive.txt');
// deletes archive if it exists
// TODO: Keep entries in blacklist_video.txt by moving them to a global blacklist
if (await fs.pathExists(archive_file_path)) {
await fs.unlink(archive_file_path);
}
@@ -379,7 +380,11 @@ async function generateArgsForSubscription(sub, user_uid, redownload = false, de
if (useArchive && !redownload) {
if (sub.archive) {
archive_dir = sub.archive;
archive_path = path.join(archive_dir, 'archive.txt')
if (sub.type && sub.type === 'audio') {
archive_path = path.join(archive_dir, 'merged_audio.txt');
} else {
archive_path = path.join(archive_dir, 'merged_video.txt');
}
}
downloadConfig.push('--download-archive', archive_path);
}

View File

@@ -386,6 +386,21 @@ describe('Downloader', function() {
assert(fs.existsSync(nfo_file_path), true);
fs.unlinkSync(nfo_file_path);
});
it('Inject args', async function() {
const original_args1 = ['--no-resize-buffer', '-o', '%(title)s', '--no-mtime'];
const new_args1 = ['--age-limit', '25', '--yes-playlist', '--abort-on-error', '-o', '%(id)s'];
const updated_args1 = utils.injectArgs(original_args1, new_args1);
const expected_args1 = ['--no-resize-buffer', '--no-mtime', '--age-limit', '25', '--yes-playlist', '--abort-on-error', '-o', '%(id)s'];
assert(JSON.stringify(updated_args1), JSON.stringify(expected_args1));
const original_args2 = ['-o', '%(title)s.%(ext)s', '--write-info-json', '--print-json', '--audio-quality', '0', '-x', '--audio-format', 'mp3'];
const new_args2 = ['--add-metadata', '--embed-thumbnail', '--convert-thumbnails', 'jpg'];
const updated_args2 = utils.injectArgs(original_args2, new_args2);
const expected_args2 = ['-o', '%(title)s.%(ext)s', '--write-info-json', '--print-json', '--audio-quality', '0', '-x', '--audio-format', 'mp3', '--add-metadata', '--embed-thumbnail', '--convert_thumbnails', 'jpg'];
console.log(updated_args2);
assert(JSON.stringify(updated_args2), JSON.stringify(expected_args2));
});
});
describe('Tasks', function() {

View File

@@ -415,6 +415,47 @@ async function fetchFile(url, path, file_label) {
});
}
async function restartServer(is_update = false) {
logger.info(`${is_update ? 'Update complete! ' : ''}Restarting server...`);
// the following line restarts the server through nodemon
fs.writeFileSync(`restart${is_update ? '_update' : '_general'}.json`, 'internal use only');
process.exit(1);
}
// adds or replaces args according to the following rules:
// - if it already exists and has value, then replace both arg and value
// - if already exists and doesn't have value, ignore
// - if it doesn't exist and has value, add both arg and value
// - if it doesn't exist and doesn't have value, add arg
function injectArgs(original_args, new_args) {
const updated_args = original_args.slice();
try {
for (let i = 0; i < new_args.length; i++) {
const new_arg = new_args[i];
if (!new_arg.startsWith('-') && !new_arg.startsWith('--') && i > 0 && original_args.includes(new_args[i - 1])) continue;
if (CONSTS.YTDL_ARGS_WITH_VALUES.has(new_arg)) {
if (original_args.includes(new_arg)) {
const original_index = original_args.indexOf(new_arg);
original_args.splice(original_index, 2);
}
updated_args.push(new_arg, new_args[i + 1]);
} else {
if (!original_args.includes(new_arg)) {
updated_args.push(new_arg);
}
}
}
} catch (err) {
logger.warn(err);
logger.warn(`Failed to inject args (${new_args}) into (${original_args})`);
}
return updated_args;
}
// objects
function File(id, title, thumbnailURL, isAudio, duration, url, uploader, size, path, upload_date, description, view_count, height, abr) {
@@ -458,5 +499,7 @@ module.exports = {
wait: wait,
checkExistsWithTimeout: checkExistsWithTimeout,
fetchFile: fetchFile,
restartServer: restartServer,
injectArgs: injectArgs,
File: File
}

View File

@@ -6,6 +6,8 @@ const utils = require('./utils');
const CONSTS = require('./consts');
const config_api = require('./config.js');
const OUTDATED_VERSION = "2020.00.00";
const is_windows = process.platform === 'win32';
const download_sources = {
@@ -31,7 +33,7 @@ exports.checkForYoutubeDLUpdate = async () => {
let current_app_details_exists = fs.existsSync(CONSTS.DETAILS_BIN_PATH);
if (!current_app_details_exists) {
logger.warn(`Failed to get youtube-dl binary details at location '${CONSTS.DETAILS_BIN_PATH}'. Generating file...`);
fs.writeJSONSync(CONSTS.DETAILS_BIN_PATH, {"version":"2020.00.00", "downloader": default_downloader});
fs.writeJSONSync(CONSTS.DETAILS_BIN_PATH, {"version": OUTDATED_VERSION, "downloader": default_downloader});
}
let current_app_details = JSON.parse(fs.readFileSync(CONSTS.DETAILS_BIN_PATH));
let current_version = current_app_details['version'];
@@ -86,6 +88,18 @@ exports.updateYoutubeDL = async (latest_update_version) => {
await download_sources[default_downloader]['func'](latest_update_version);
}
exports.verifyBinaryExistsLinux = () => {
const details_json = fs.readJSONSync(CONSTS.DETAILS_BIN_PATH);
if (!is_windows && details_json && details_json['path'] && details_json['path'].includes('.exe')) {
details_json['path'] = 'node_modules/youtube-dl/bin/youtube-dl';
details_json['exec'] = 'youtube-dl';
details_json['version'] = OUTDATED_VERSION;
fs.writeJSONSync(CONSTS.DETAILS_BIN_PATH, details_json);
utils.restartServer();
}
}
async function downloadLatestYoutubeDLBinary(new_version) {
const file_ext = is_windows ? '.exe' : '';

View File

@@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDMX9Wk5SM5cIfY
6ReKX3ybY1rsbNbOzG8ceN7yyeXB0mor8pVsX1MOna2HewOyBuaaYNJRO4tJBxic
7a8zQErfgHL/i/QrVvVCpfJ7xKvq6zij5NYoqd/FBUwawqjeH5/voIcAp9z5Vmsr
kL0sxJUKy6b4IWNp3noU7Nvq2RwxnXQbKDhz8FrX6oQAnDC6gsG5a2OSPsaE4oqw
6nmonORJypmpP5hqyHY8ffXBT2lAxjHT7OnYbaCBe2TQP8+rH6rDBOhjVNtUJ089
ocTQL6LtQEPkcF4yKJmtcOwHl8OPGZs5l9i8xb4j9RuSPkm2lbzZX8sOsdGGoqJZ
q68nYhsHAgMBAAECggEAXmtKEzfPObq88B/kAcgSk+FngMHZzcmR7bgD3GwdSxnQ
dkRI9zvk7eQ35tcUwntAr4Lat6/ILjFqlBmVLxrdXHuF5Xz9jcZLYgKzz61xdYM9
dC6FKF0u5eGIIvbauGAo7jaeGFX1F3Zu5b4lP9kEOGwU1B7sxF0FzsQM5+dtCJgv
We/hWQeF+9gtoVnkCSS/Mq2p0UomXXHW0Bz4+HuHlTR9aiYbviYnotABiLUhZyzt
v5yUaktb9qniBfdLpRlq8cp06xYlTEA9gJpa4Pnok8OWUsbAiW6EiXUSaZ/cchVa
AnO8WWYvVOnnt6WHI3+QdFTnqVjE5TBX4N/7bVhHGQKBgQD0dtbFqp7vZK/jVYvE
z0WPdySOg2ZDmoSfk5ZlR1+Y9zWToHv0qu8zqoOjL8Ubxrh9fGlOow+cCVdkEuaa
jWC2AWetuRvW0Z5A3XMXr0/N/1fgOkTqtp3WNrUPjVJahEg3lN+90opgFoT8swSi
s1oxW0oLcVIlrjhGBXAPCfsAuQKBgQDWBLRhHsRAvGcK5wGuVnxVApTIyBOermsW
3bJt+7+UI+4sYrBAwkWdQG93IG0cQtn48TEPBgmR2fjRF5IFT9M4/u+QOeeByT7I
we7nVtHgSY5ByC9N0mjWbcmSg8fktz/LonjldNC4kWdOFb75fxGf8kOGS5rUaMA4
zHucfB6ZvwKBgQCPHJrysMXGY21MaqIeHzEboaX3ABl37hdBzAa5V6UxSVdGCydF
vmO2HVZey/JaJmWOoKyNaowSzq0oWqBBTg6VvhDR9JHFmoVId9uOvAS+FYN+Mt5x
gWK5KuGoLxVNBC+6yh6JY526TrSfsrU+Aj0Es+qO9FIg2PL8muZVB4S3kQKBgH/5
CDMaxpc/EQ5/2413wZjDllwI51J3USm3Hz6Mzp2ybnSz/lh60k2Zfg1polTH1Lb6
4i7tmUNRZ2sAARyUAuWN64n+VeRRhe1dqZFDZPQMh7fmEAMk0fOGaoXlrt2ghdEq
Mchi9Xun1nHmpu9hgBR4NNBU3RwuFuLfwvprbZDZAoGAWa62QJChE86xQGP1MrL2
SbIzw3cfeP5xdQ3MKldJiy5IkbMR7Z13WZ7FwvPTy0g/onLHD1rqlm1kUMsGRHpD
5vH06PNpKXQ6x8BYaRGtE6P39jLycO/X+WK/lYTrWo1bR+mGCebDh4B5XrwT3gI6
x4Gvz134pZCTyQCf5JCwbQs=
-----END PRIVATE KEY-----

View File

@@ -4,8 +4,6 @@
# and also optimizing some code with this commit.
# xoxo :D
set -xeuo pipefail
case $(uname -m) in
x86_64)
ARCH=amd64;;
@@ -22,15 +20,24 @@ case $(uname -m) in
exit 1
esac
echo "Architecture: $ARCH"
curl --connect-timeout 5 \
echo "(INFO) Architecture detected: $ARCH"
echo "(1/5) READY - Acquire temp dependencies in ffmpeg obtain layer"
apt-get update && apt-get -y install curl xz-utils
echo "(2/5) DOWNLOAD - Acquire latest ffmpeg and ffprobe from John van Sickle's master-sourced builds in ffmpeg obtain layer"
curl -o ffmpeg.txz \
--connect-timeout 5 \
--max-time 10 \
--retry 5 \
--retry-delay 0 \
--retry-max-time 40 \
"https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-${ARCH}-static.tar.xz" -o ffmpeg.txz
"https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-${ARCH}-static.tar.xz"
mkdir /tmp/ffmpeg
tar xf ffmpeg.txz -C /tmp/ffmpeg
echo "(3/5) CLEANUP - Remove temp dependencies from ffmpeg obtain layer"
apt-get -y remove curl xz-utils
apt-get -y autoremove
echo "(4/5) PROVISION - Provide ffmpeg and ffprobe from ffmpeg obtain layer"
cp /tmp/ffmpeg/*/ffmpeg /usr/local/bin/ffmpeg
cp /tmp/ffmpeg/*/ffprobe /usr/local/bin/ffprobe
echo "(5/5) CLEANUP - Remove temporary downloads from ffmpeg obtain layer"
rm -rf /tmp/ffmpeg ffmpeg.txz

3
heroku.yml Normal file
View File

@@ -0,0 +1,3 @@
build:
docker:
web: Dockerfile.heroku

View File

@@ -12,7 +12,8 @@
"lint": "ng lint",
"e2e": "ng e2e",
"electron": "ng build --base-href ./ && electron .",
"generate": "openapi --input ./\"Public API v1.yaml\" --output ./src/api-types --exportCore false --exportServices false --exportModels true"
"generate": "openapi --input ./\"Public API v1.yaml\" --output ./src/api-types --exportCore false --exportServices false --exportModels true",
"i18n-source": "ng extract-i18n --output-path=src/assets/i18n"
},
"engines": {
"node": "12.3.1",

View File

@@ -51,7 +51,7 @@
</ng-container>
<ng-container *ngIf="postsService.config && allowSubscriptions && postsService.subscriptions && postsService.hasPermission('subscriptions')">
<mat-divider *ngIf="postsService.subscriptions.length > 0"></mat-divider>
<a *ngFor="let subscription of postsService.subscriptions" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" [routerLink]="['/subscription', { id: subscription.id }]"><ngx-avatar [style.margin-right]="'10px'" size="32" [name]="subscription.name"></ngx-avatar>{{subscription.name}}</a>
<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.margin-right]="'10px'" size="32" [name]="subscription.name"></ngx-avatars>{{subscription.name}}</a>
</ng-container>
</mat-nav-list>
</mat-sidenav>

View File

@@ -42,6 +42,11 @@ export class ConcurrentStreamComponent implements OnInit {
}
ngOnDestroy(): void {
if (this.check_timeout) { clearInterval(this.check_timeout); }
if (this.update_timeout) { clearInterval(this.update_timeout); }
}
startServer() {
this.started = true;
this.server_started = true;

View File

@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, Input, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
import { Component, ElementRef, Input, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
import { PostsService } from 'app/posts.services';
@Component({
@@ -6,7 +6,7 @@ import { PostsService } from 'app/posts.services';
templateUrl: './twitch-chat.component.html',
styleUrls: ['./twitch-chat.component.scss']
})
export class TwitchChatComponent implements OnInit, AfterViewInit {
export class TwitchChatComponent implements OnInit, OnDestroy {
full_chat = null;
visible_chat = null;
@@ -33,7 +33,8 @@ export class TwitchChatComponent implements OnInit, AfterViewInit {
this.getFullChat();
}
ngAfterViewInit() {
ngOnDestroy(): void {
if (this.chat_check_interval_obj) { clearInterval(this.chat_check_interval_obj); }
}
private isUserNearBottom(): boolean {

View File

@@ -445,7 +445,7 @@ export const isoLangs = {
'name': 'Navajo, Navaho',
'nativeName': 'Diné bizaad, Dinékʼehǰí'
},
'nb': {
'nb-NO': {
'name': 'Norwegian Bokmål',
'nativeName': 'Norsk bokmål',
'ngID': 'nb'

View File

@@ -52,6 +52,8 @@ export class SubscriptionComponent implements OnInit, OnDestroy {
this.route.params.subscribe(params => {
this.id = params['id'];
if (this.sub_interval) { clearInterval(this.sub_interval); }
this.postsService.service_initialized.subscribe(init => {
if (init) {
this.getConfig();

View File

@@ -36,6 +36,9 @@
<ng-container i18n="Subscription playlist not available text">Name not available. Playlist retrieval in progress.</ng-container>
</div>
</a>
<button mat-icon-button (click)="editSubscription(sub)">
<mat-icon>edit</mat-icon>
</button>
<button mat-icon-button (click)="showSubInfo(sub)">
<mat-icon>info</mat-icon>
</button>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,222 +0,0 @@
{
"17f0ea5d2d7a262b0e875acc70475f102aee84e6": "Opprett en spilleliste",
"cff1428d10d59d14e45edec3c735a27b5482db59": "Navn",
"f0baeb8b69d120073b6d60d34785889b0c3232c8": "Lyd",
"2d1ea268a6a9f483dbc2cbfe19bf4256a57a6af4": "Video",
"f61c6867295f3b53d23557021f2f4e0aa1d0b8fc": "Type",
"f47e2d56dd8a145b2e9599da9730c049d52962a2": "Lydfiler",
"a52dae09be10ca3a65da918533ced3d3f4992238": "Videoer",
"d9e83ac17026e70ef6e9c0f3240a3b2450367f40": "Endre youtube-dl-argumenter",
"7fc1946abe2b40f60059c6cd19975d677095fd19": "Simulerte nye argumenter",
"0b71824ae71972f236039bed43f8d2323e8fd570": "Legg til argument",
"c8b0e59eb491f2ac7505f0fbab747062e6b32b23": "Søk etter kategori",
"9eeb91caef5a50256dd87e1c4b7b3e8216479377": "Bruk argument-verdi",
"25d8ad5eba2ec24e68295a27d6a4bb9b49e3dacd": "Argument-verdi",
"7de2451ed3fb8d8b847979bd3f0c740b970f167b": "Legg til argument",
"d7b35c384aecd25a516200d6921836374613dfe7": "Avbryt",
"b2623aee44b70c9a4ba1fce16c8a593b0a4c7974": "Endre",
"a38ae1082fec79ba1f379978337385a539a28e73": "Kvalitet",
"4be966a9dcfbc9b54dfcc604b831c0289f847fa4": "Bruk nettadresse",
"d3f02f845e62cebd75fde451ab8479d2a8ad784d": "Vis",
"4a9889d36910edc8323d7bab60858ab3da6d91df": "Kun lyd",
"96a01fafe135afc58b0f8071a4ab00234495ce18": "Multi-nedlastingsmodus",
"6a21ba5fb0ac804a525bf9ab168038c3ee88e661": "Last ned",
"6a3777f913cf3f288664f0632b9f24794fdcc24e": "Avbryt",
"322ed150e02666fe2259c5b4614eac7066f4ffa0": "Avansert",
"b7ffe7c6586d6f3f18a9246806a7c7d5538ab43e": "Simulert kommando:",
"4e4c721129466be9c3862294dc40241b64045998": "Bruk egendefinerte argumenter:",
"ad2f8ac8b7de7945b80c8e424484da94e597125f": "Egendefinerte argumenter",
"a6911c2157f1b775284bbe9654ce5eb30cf45d7f": "Du trenger ikke å inkludere nettadressen, kun alt etter. Argumenter skilles ved bruk av to komma, slik: ,,",
"3a92a3443c65a52f37ca7efb8f453b35dbefbf29": "Bruk defendefinert utdata",
"d9c02face477f2f9cdaae318ccee5f89856851fb": "Egendefinert utdata",
"fcfd4675b4c90f08d18d3abede9a9a4dff4cfdc7": "Dokumentasjon",
"19d1ae64d94d28a29b2c57ae8671aace906b5401": "Sti er relativ til oppsettsnedlastingsstien. Ikke inkluder utvidelse.",
"8fad10737d3e3735a6699a4d89cbf6c20f6bb55f": "Bruk identitetsbekreftelse",
"08c74dc9762957593b91f6eb5d65efdfc975bf48": "Brukernavn",
"c32ef07f8803a223a83ed17024b38e8d82292407": "Passord",
"616e206cb4f25bd5885fc35925365e43cf5fb929": "Navn:",
"c52db455cca9109ee47e1a612c3f4117c09eb71b": "Nettadresse:",
"c6eb45d085384903e53ab001a3513d1de6a1dbac": "Opplaster:",
"109c6f4a5e46efb933612ededfaf52a13178b7e0": "Filstørrelse:",
"bd630d8669b16e5f264ec4649d9b469fe03e5ff4": "Sti:",
"a67e7d843cef735c79d5ef1c8ba4af3e758912bb": "Opplastingsdato:",
"f4e529ae5ffd73001d1ff4bbdeeb0a72e342e5c8": "Lukk",
"4f389e41e4592f7f9bb76abdd8af4afdfb13f4f1": "Endre spilleliste",
"511b600ae4cf037e4eb3b7a58410842cd5727490": "Legg til mer innhold",
"52c9a103b812f258bcddc3d90a6e3f46871d25fe": "Lagre",
"ca3dbbc7f3e011bffe32a10a3ea45cc84f30ecf1": "ID:",
"e684046d73bcee88e82f7ff01e2852789a05fc32": "Antall:",
"28f86ffd419b869711aa13f5e5ff54be6d70731c": "Rediger",
"826b25211922a1b46436589233cb6f1a163d89b7": "Slett",
"321e4419a943044e674beb55b8039f42a9761ca5": "Info",
"34504b488c24c27e68089be549f0eeae6ebaf30b": "Slett og svartelist",
"ebadf946ae90f13ecd0c70f09edbc0f983af8a0f": "Last opp nye kaker",
"98a8a42e5efffe17ab786636ed0139b4c7032d0e": "Dra og slipp",
"a8b7b9c168fd936a75e500806a8c0d7755ef1198": "Merk: Oppasting av nye kaker overskriver tidligere. Merk deg også at kaker gjelder for hele instansen, ikke per bruker.",
"121cc5391cd2a5115bc2b3160379ee5b36cd7716": "Innstillinger",
"801b98c6f02fe3b32f6afa3ee854c99ed83474e6": "Nettadresse",
"54c512cca1923ab72faf1a0bd98d3d172469629a": "Nettadressen dette programmet nås fra, uten porten.",
"cb2741a46e3560f6bc6dfd99d385e86b08b26d72": "Port",
"22e8f1d0423a3b784fe40fab187b92c06541b577": "Ønsket port. Forvalet er 17442.",
"d4477669a560750d2064051a510ef4d7679e2f3e": "Multi-brukermodus",
"2eb03565fcdce7a7a67abc277a936a32fcf51557": "Bruker-basissti",
"a64505c41150663968e277ec9b3ddaa5f4838798": "Basissti for brukere og deres nedlastede videoer.",
"4e3120311801c4acd18de7146add2ee4a4417773": "Tillat abonnementer",
"4bee2a4bef2d26d37c9b353c278e24e5cd309ce3": "Abonnements-basissti",
"bc9892814ee2d119ae94378c905ea440a249b84a": "Basissti for videoer fra dine abonnementskanaler og spillelister. Den er relativ til YTDL-Material sin rotmappe.",
"5bef4b25ba680da7fff06b86a91b1fc7e6a926e3": "Sjekkintervall",
"0f56a7449b77630c114615395bbda4cab398efd8": "I sekunder, kun tall.",
"27a56aad79d8b61269ed303f11664cc78bcc2522": "Drakt",
"ff7cee38a2259526c519f878e71b964f41db4348": "Forvalg",
"adb4562d2dbd3584370e44496969d58c511ecb63": "Mørk",
"7a6bacee4c31cb5c0ac2d24274fb4610d8858602": "Tillat draktendring",
"fe46ccaae902ce974e2441abe752399288298619": "Språk",
"82421c3e46a0453a70c42900eab51d58d79e6599": "Generelt",
"ab2756805742e84ad0cc0468f4be2d8aa9f855a5": "Lydmappe",
"c2c89cdf45d46ea64d2ed2f9ac15dfa4d77e26ca": "Sti for lydbaserte nedlastinger. Den er relativ til YTDL-Material sin rotmappe.",
"46826331da1949bd6fb74624447057099c9d20cd": "Videomappe",
"17c92e6d47a213fa95b5aa344b3f258147123f93": "Sti for videonedlastinger. Den er relativ til YTDL-Material sin rotmappe.",
"6b995e7130b4d667eaab6c5f61b362ace486d26d": "Egendefinerte argumenter for nedlastninger på hjemmesiden for hele programmet. Argumenter skilles med to komma, slik: ,,",
"78e49b7339b4fa7184dd21bcaae107ce9b7076f6": "Bruk youtube-dl-arktivet",
"ffc19f32b1cba0daefc0e5668f89346db1db83ad": "Inkluder miniatyrbilde",
"384de8f8f112c9e6092eb2698706d391553f3e8d": "Inkluder metadata",
"fb35145bfb84521e21b6385363d59221f436a573": "Drep alle nedlastinger",
"0ba25ad86a240576c4f20a2fada4722ebba77b1e": "Nedlaster",
"61f8fd90b5f8cb20c70371feb2ee5e1fac5a9095": "Topptittel",
"78d3531417c0d4ba4c90f0d4ae741edc261ec8df": "Filbehandler påskrudd",
"a5a1be0a5df07de9eec57f5d2a86ed0204b2e75a": "Nedlastingsbehandler påskrudd",
"c33bd5392b39dbed36b8e5a1145163a15d45835f": "Tillat kvalitetsvalg",
"bda5508e24e0d77debb28bcd9194d8fefb1cfb92": "Modus kun for nedlasting",
"09d31c803a7252658694e1e3176b97f5655a3fe3": "Tillat multi-nedlastingsmodus",
"1c4dbce56d96b8974aac24a02f7ab2ee81415014": "Skru på offentlig API",
"23bd81dcc30b74d06279a26d7a42e8901c1b124e": "Offentlig API-nøkkel",
"41016a73d8ad85e6cb26dffa0a8fab9fe8f60d8e": "Vis dokumentasjon",
"1b258b258b4cc475ceb2871305b61756b0134f4a": "Generer",
"00a94f58d9eb2e3aa561440eabea616d0c937fa2": "Dette vil slette din gamle API-nøkkel!",
"d5d7c61349f3b0859336066e6d453fc35d334fe5": "Bruk YouTube-API",
"ce10d31febb3d9d60c160750570310f303a22c22": "YouTube-API-nøkkel",
"8602e313cdfa7c4cc475ccbe86459fce3c3fd986": "Å generere en nøkkel er lett!",
"9b3cedfa83c6d7acb3210953289d1be4aab115c7": "Klikk her",
"7f09776373995003161235c0c8d02b7f91dbc4df": "for å laste ned den offisielle Chrome-utvidelsen for YouTubeDL-Material selv.",
"5b5296423906ab3371fdb2b5a5aaa83acaa2ee52": "Du må manuelt laste ned utvidelsen og endre dens innstillinger for å sette skjermflate-nettadresse.",
"9a2ec6da48771128384887525bdcac992632c863": "for å installere den offisielle Firefox-utvidelsen for YouTubeDL-Material rett fra Firefox sin utvidelsesside.",
"eb81be6b49e195e5307811d1d08a19259d411f37": "Detaljert oppsettsinstruks",
"cb17ff8fe3961cf90f44bee97c88a3f3347a7e55": "Ikke mye kreves annet enn å endre utvidelses innstillinger for å sette skjermflate-nettadresse.",
"61b81b11aad0b9d970ece2fce18405f07eac69c2": "Dra lenken nedenfor til bokmerker. Naviger til YouTube-videoen du ønsker å laste ned og klikk på bokmerket.",
"c505d6c5de63cc700f0aaf8a4b31fae9e18024e5": "Genrer \"kun lyd\"-bookmerke",
"d5f69691f9f05711633128b5a3db696783266b58": "Ekstra",
"5fab47f146b0a4b809dcebf3db9da94df6299ea1": "Bruk forvalgt nedlastingsagent",
"ec71e08aee647ea4a71fd6b7510c54d84a797ca6": "Velg en nedlaster",
"0c43af932e6a4ee85500e28f01b3538b4eb27bc4": "Loggingsnivå",
"db6c192032f4cab809aad35215f0aa4765761897": "Innloggingsutløp",
"dc3d990391c944d1fbfc7cfb402f7b5e112fb3a8": "Tillat avansert nedlasting",
"431e5f3a0dde88768d1074baedd65266412b3f02": "Bruk kaker",
"80651a7ad1229ea6613557d3559f702cfa5aecf5": "Sett kaker",
"bc2e854e111ecf2bd7db170da5e3c2ed08181d88": "Avansert",
"37224420db54d4bc7696f157b779a7225f03ca9d": "Tillat brukerregistrering",
"4f56ced9d6b85aeb1d4346433361d47ea72dac1a": "Intern",
"e3d7c5f019e79a3235a28ba24df24f11712c7627": "LDAP",
"fa548cee6ea11c160a416cac3e6bdec0363883dc": "Autentiseringsmetode",
"1db9789b93069861019bd0ccaa5d4706b00afc61": "LDAP-nettadresse",
"f50fa6c09c8944aed504f6325f2913ee6c7a296a": "BIND-DN",
"080cc6abcba236390fc22e79792d0d3443a3bd2a": "BIND-identitetsdetaljer",
"cfa67d14d84fe0e9fadf251dc51ffc181173b662": "Søkebase",
"e01d54ecc1a0fcf9525a3c100ed8b83d94e61c23": "Søkefilter",
"4d13a9cd5ed3dcee0eab22cb25198d43886942be": "Brukere",
"eb3d5aefff38a814b76da74371cbf02c0789a1ef": "Logger",
"fe8fd36dbf5deee1d56564965787a782a66eba44": "{VAR_SELECT, select, true {Close} false {Cancel} other {otha} }",
"cec82c0a545f37420d55a9b6c45c20546e82f94e": "Om YouTubeDL-Material",
"199c17e5d6a419313af3c325f06dcbb9645ca618": "er en fri YouTube-nedlaster bygd i henhold til Google sine Materielle spesifikasjoner. Du kan sømløst laste ned dine favorittvideoer som video- eller lydfiler, og tilogmed abonnere på dine favorittkanaler og spillelister for å holde deg oppdatert med nye videoer.",
"bc0ad0ee6630acb7fcb7802ec79f5a0ee943c1a7": "har noen flotte funksjoner inkludert. Et vidtfavnende API, Docker-støtte, og lokalisering (oversettelser)-støtte. Les om alle støttede funksjoner ved å klikke på GitHub-ikonet ovenfor.",
"a45e3b05f0529dc5246d70ef62304c94426d4c81": "Installert versjon:",
"e22f3a5351944f3a1a10cfc7da6f65dfbe0037fe": "Ser etter oppdateringer …",
"a16e92385b4fd9677bb830a4b796b8b79c113290": "Oppdatering tilgjengelig",
"189b28aaa19b3c51c6111ad039c4fd5e2a22e370": "Du kan oppdatere fra innstillingsmenyen.",
"b33536f59b94ec935a16bd6869d836895dc5300c": "Funnet en feil eller har et forslag å komme med?",
"e1f398f38ff1534303d4bb80bd6cece245f24016": "for å opprette en feilrapport.",
"42ff677ec14f111e88bd6cdd30145378e994d1bf": "Din profil",
"ac9d09de42edca1296371e4d801349c9096ac8de": "UID:",
"a5ed099ffc9e96f6970df843289ade8a7d20ab9f": "Opprettet:",
"fa96f2137af0a24e6d6d54c598c0af7d5d5ad344": "Du er ikke innlogget.",
"6765b4c916060f6bc42d9bb69e80377dbcb5e4e9": "Logg inn",
"bb694b49d408265c91c62799c2b3a7e3151c824d": "Logg ut",
"a1dbca87b9f36d2b06a5cbcffb5814c4ae9b798a": "Opprett administratorkonto",
"2d2adf3ca26a676bca2269295b7455a26fd26980": "Fant ingen administratorkonto. Dette vil opprette og sette passord for en slik konto med brukernavn som «admin».",
"70a67e04629f6d412db0a12d51820b480788d795": "Opprett",
"994363f08f9fbfa3b3994ff7b35c6904fdff18d8": "Profil",
"004b222ff9ef9dd4771b777950ca1d0e4cd4348a": "Om",
"92eee6be6de0b11c924e3ab27db30257159c0a7c": "Hjem",
"357064ca9d9ac859eb618e28e8126fa32be049e2": "Abonnementer",
"822fab38216f64e8166d368b59fe756ca39d301b": "Nedlastinger",
"a249a5ae13e0835383885aaf697d2890cc3e53e9": "Del spilleliste",
"15da89490e04496ca9ea1e1b3d44fb5efd4a75d9": "Del video",
"1d540dcd271b316545d070f9d182c372d923aadd": "Del lyd",
"1f6d14a780a37a97899dc611881e6bc971268285": "Skru på deling",
"6580b6a950d952df847cb3d8e7176720a740adc8": "Bruk tidsstempel",
"4f2ed9e71a7c981db3e50ae2fedb28aff2ec4e6c": "Sekunder",
"3a6e5a6aa78ca864f6542410c5dafb6334538106": "Kopier til utklippstavle",
"5b3075e8dc3f3921ec316b0bd83b6d14a06c1a4f": "Lagre endringer",
"4d8a18b04a1f785ecd8021ac824e0dfd5881dbfc": "Nedlastet",
"348cc5d553b18e862eb1c1770e5636f6b05ba130": "En feil inntraff",
"4f8b2bb476981727ab34ed40fde1218361f92c45": "Detaljer",
"e9aff8e6df2e2bf6299ea27bb2894c70bc48bd4d": "En feil har inntruffet:",
"77b0c73840665945b25bd128709aa64c8f017e1c": "Nedlastingsstart:",
"08ff9375ec078065bcdd7637b7ea65fce2979266": "Nedlastingsslutt:",
"ad127117f9471612f47d01eae09709da444a36a4": "Filsti(er):",
"a9806cf78ce00eb2613eeca11354a97e033377b8": "Abonner på en spilleliste eller kanal",
"93efc99ae087fc116de708ecd3ace86ca237cf30": "Spilleliste- eller kanal-nettadressen",
"08f5d0ef937ae17feb1b04aff15ad88911e87baf": "Egendefinert navn",
"ea30873bd3f0d5e4fb2378eec3f0a1db77634a28": "Last ned alle opplastinger",
"28a678e9cabf86e44c32594c43fa0e890135c20f": "Last ned videoer oppdatert siste",
"c76a955642714b8949ff3e4b4990864a2e2cac95": "Kun lyd-modus",
"408ca4911457e84a348cecf214f02c69289aa8f1": "Kun strømming-modus",
"f432e1a8d6adb12e612127978ce2e0ced933959c": "Disse legges til etter standard-argumentene.",
"98b6ec9ec138186d663e64770267b67334353d63": "Egendefinert filutdata",
"d0336848b0c375a1c25ba369b3481ee383217a4f": "Abonner",
"e78c0d60ac39787f62c9159646fe0b3c1ed55a1d": "Type:",
"a44d86aa1e6c20ced07aca3a7c081d8db9ded1c6": "Arkiv:",
"8efc77bf327659c0fec1f518cf48a98cdcd9dddf": "Eksporter arkiv",
"3042bd3ad8dffcfeca5fd1ae6159fd1047434e95": "Opphev abonnement",
"e2319dec5b4ccfb6ed9f55ccabd63650a8fdf547": "Dine abonnementer",
"807cf11e6ac1cde912496f764c176bdfdd6b7e19": "Kanaler",
"29b89f751593e1b347eef103891b7a1ff36ec03f": "Navn ikke tilgjengelig. Henter kanal …",
"4636cd4a1379c50d471e98786098c4d39e1e82ad": "Du har ingen kanalabonnementer.",
"47546e45bbb476baaaad38244db444c427ddc502": "Spillelister",
"2e0a410652cb07d069f576b61eab32586a18320d": "Navn ikke tilgjengelig. Henter spilleliste …",
"587b57ced54965d8874c3fd0e9dfedb987e5df04": "Du har ingen spillelisteabonnement.",
"3697f8583ea42868aa269489ad366103d94aece7": "Redigering",
"7e892ba15f2c6c17e83510e273b3e10fc32ea016": "Søk",
"2054791b822475aeaea95c0119113de3200f5e1c": "Lengde:",
"94e01842dcee90531caa52e4147f70679bac87fe": "Slett og last ned igjen",
"2031adb51e07a41844e8ba7704b054e98345c9c1": "Slett for alltid",
"91ecce65f1d23f9419d1c953cd6b7bc7f91c110e": "Oppdaterer",
"1372e61c5bd06100844bd43b98b016aabc468f62": "Velg en versjon:",
"cfc2f436ec2beffb042e7511a73c89c372e86a6c": "Regustrer",
"a1ad8b1be9be43b5183bd2c3186d4e19496f2a0b": "Økt-ID:",
"eb98135e35af26a9a326ee69bd8ff104d36dd8ec": "(nåværende)",
"b6c453e0e61faea184bbaf5c5b0a1e164f4de2a2": "Tøm alle nedlastinger",
"7117fc42f860e86d983bfccfcf2654e5750f3406": "Ingen nedlastninger tilgjengelige!",
"b7ff2e2b909c53abe088fe60b9f4b6ac7757247f": "Registrer en bruker",
"024886ca34a6f309e3e51c2ed849320592c3faaa": "Brukernavn",
"2bd201aea09e43fbfd3cd15ec0499b6755302329": "Håndter bruker",
"29c97c8e76763bb15b6d515648fa5bd1eb0f7510": "Bruker-UID:",
"e70e209561583f360b1e9cefd2cbb1fe434b6229": "Nytt passord",
"6498fa1b8f563988f769654a75411bb8060134b9": "Sett nytt passord",
"544e09cdc99a8978f48521d45f62db0da6dcf742": "Bruk rolle-forvalg",
"4f20f2d5a6882190892e58b85f6ccbedfa737952": "Ja",
"3d3ae7deebc5949b0c1c78b9847886a94321d9fd": "Nei",
"57c6c05d8ebf4ef1180c2705033c044f655bb2c4": "Håndter rolle",
"746f64ddd9001ac456327cd9a3d5152203a4b93c": "Brukernavn",
"52c1447c1ec9570a2a3025c7e566557b8d19ed92": "Rolle",
"59a8c38db3091a63ac1cb9590188dc3a972acfb3": "Handlinger",
"632e8b20c98e8eec4059a605a4b011bb476137af": "Rediger bruker",
"95b95a9c79e4fd9ed41f6855e37b3b06af25bcab": "Slett bruker",
"4d92a0395dd66778a931460118626c5794a3fc7a": "Legg til brukere",
"b0d7dd8a1b0349622d6e0c6e643e24a9ea0efa1d": "Rediger rolle",
"5009630cdf32ab4f1c78737b9617b8773512c05a": "Linjer:",
"8a0bda4c47f10b2423ff183acefbf70d4ab52ea2": "Tøm logger",
"ccf5ea825526ac490974336cb5c24352886abc07": "Åpne fil",
"5656a06f17c24b2d7eae9c221567b209743829a9": "Åpne fil i ny fane",
"a0720c36ee1057e5c54a86591b722485c62d7b1a": "Gå til abonnement",
"d02888c485d3aeab6de628508f4a00312a722894": "Mine videoer"
}

View File

@@ -1545,8 +1545,8 @@
<note priority="1" from="description">Custom args input placeholder</note>
</trans-unit>
<trans-unit id="6b995e7130b4d667eaab6c5f61b362ace486d26d" datatype="html">
<source>Global custom args for downloads on the home page. Args are delimited using two commas like so: ,,</source>
<target>Algemene aanvullende opties voor downloads op de overzichtspagina. Scheidt deze met komma's: ,,</target>
<source>Global custom args for downloads on the home page. (Set args for subscriptions for each subscriptions separately!) Args are delimited using two commas like so: ,,</source>
<target>Algemene aanvullende opties voor downloads op de overzichtspagina. (Stel de opties per abonnement in!) Scheidt deze met komma's: ,,</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/settings/settings.component.html</context>
<context context-type="linenumber">134</context>

View File

@@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en-US" datatype="plaintext" original="ng2.template" target-language="zh-Hans">
<body>
@@ -902,8 +902,8 @@
<note priority="1" from="description">Video path setting input hint</note>
</trans-unit>
<trans-unit id="6b995e7130b4d667eaab6c5f61b362ace486d26d" datatype="html">
<source>Global custom args for downloads on the home page. Args are delimited using two commas like so: ,,</source>
<target>开始页面上用于下载的全局自定义参数。参数由两个逗号分隔:,,</target>
<source>Global custom args for downloads on the home page. (Set args for subscriptions for each subscriptions separately!) Args are delimited using two commas like so: ,,</source>
<target>开始页面上用于下载的全局自定义参数。(单独为每个订阅设置订阅参数!) 参数由两个逗号分隔:,,</target>
<context-group purpose="location">
<context context-type="sourcefile">app/settings/settings.component.html</context>
<context context-type="linenumber">146</context>

View File

@@ -33,7 +33,18 @@ async function createLocalizationJSON() {
for (let i = 0; i < files.length; i++) {
const file = path.basename(files[i]);
const file_parts = file.split('.');
locales.push(file_parts[1]);
if (file_parts.length !== 3 || file_parts[1] === 'en') continue;
try {
const locale_json = fs.readJSONSync(files[i]);
const locale_json_keys = Object.keys(locale_json);
let has_defined_keys = false;
for (let i = 0; i < locale_json_keys.length; i++) {
if (locale_json[locale_json_keys[i]] !== '') has_defined_keys = true;
}
if (has_defined_keys) locales.push(file_parts[1]);
} catch (err) {
console.error(err);
}
}
fs.unlinkSync('src/assets/i18n/messages.en.json');