Compare commits

..

6 Commits

Author SHA1 Message Date
Isaac Abadi
ab5cd409bb Attempt to reduce docker image size 2022-05-05 03:04:30 -04:00
Isaac Abadi
e0509e8091 Merge branch 'master' of https://github.com/Tzahi12345/YoutubeDL-Material into docker-ubuntu 2022-05-05 03:03:44 -04:00
Isaac Abadi
0c1568b38d Renamed postbuild.mjs to postbuild.js 2022-04-30 23:30:56 -04:00
Isaac Abadi
f8a0d14968 Updated Dockerfile to support ubuntu 2022-04-30 17:17:07 -04:00
Isaac Abadi
f5894e6bc0 apk add -> apt-get 2022-04-30 13:50:41 -04:00
Isaac Abadi
f205f8e58e Switched from alpine to ubuntu 2022-04-30 13:38:26 -04:00
167 changed files with 883 additions and 7325 deletions

View File

@@ -1,18 +0,0 @@
version: 2
updates:
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/.github/workflows"
schedule:
interval: "daily"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "npm"
directory: "/backend/"
schedule:
interval: "daily"

View File

@@ -1,38 +0,0 @@
name: No Response
# Both `issue_comment` and `scheduled` event types are required for this Action
# to work properly.
on:
issue_comment:
types: [created]
schedule:
# Schedule for five minutes after the hour, every hour
- cron: '5 * * * *'
# By specifying the access of one of the scopes, all of those that are not
# specified are set to 'none'.
permissions:
issues: write
jobs:
noResponse:
runs-on: ubuntu-latest
if: ${{ github.repository == 'Tzahi12345/YoutubeDL-Material' }}
steps:
- uses: lee-dohm/no-response@v0.5.0
with:
token: ${{ github.token }}
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
closeComment: >
This issue has been automatically closed because there has been no response
to our request for more information from the original author. With only the
information that is currently in the issue, we don't have enough information
to take action. Please reach out if you have or find the answers we need so
that we can investigate further. We will re-open this issue if you provide us
with the requested information with a comment under this issue.
Thank you for your understanding and for trying to help make this application
a better one!
# Number of days of inactivity before an issue is closed for lack of response.
daysUntilClose: 21
# Label requiring a response.
responseRequiredLabel: "💬 response-needed"

View File

@@ -6,25 +6,19 @@ on:
tags:
description: 'Docker tags'
required: true
release:
types: [published]
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
@@ -32,49 +26,15 @@ jobs:
name: "version.json"
json: '{"type": "docker", "tag": "latest", "commit": "${{ steps.vars.outputs.sha_short }}", "date": "${{ steps.date.outputs.date }}"}'
dir: 'backend/'
- name: Set image tag
id: tags
run: |
if [ ${{ github.event.action }} == "workflow_dispatch" ]; then
echo "::set-output name=tags::${{ github.event.inputs.tags }}"
elif [ ${{ github.event.action }} == "release" ]; then
echo "::set-output name=tags::${{ github.event.release.tag_name }}"
else
echo "Unknown workflow trigger: ${{ github.event.action }}! Cannot determine default tag."
exit 1
fi
- name: Generate Docker image metadata
id: docker-meta
uses: docker/metadata-action@v4
with:
images: |
${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }}
ghcr.io/${{ github.repository_owner }}/${{ secrets.DOCKERHUB_REPO }}
tags: |
raw=${{ steps.tags.outputs.tags }}
raw=latest
- name: setup platform emulator
uses: docker/setup-qemu-action@v1
- name: setup multi-arch docker build
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: build & push images
uses: docker/build-push-action@v2
with:
@@ -82,5 +42,4 @@ jobs:
file: ./Dockerfile
platforms: linux/amd64,linux/arm,linux/arm64/v8
push: true
tags: ${{ steps.docker-meta.outputs.tags }}
labels: ${{ steps.docker-meta.outputs.labels }}
tags: ${{ github.event.inputs.tags }}

View File

@@ -20,15 +20,12 @@ jobs:
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
@@ -36,42 +33,15 @@ jobs:
name: "version.json"
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
- name: setup multi-arch docker build
uses: docker/setup-buildx-action@v1
- name: Generate Docker image metadata
id: docker-meta
uses: docker/metadata-action@v4
# Defaults:
# DOCKERHUB_USERNAME : tzahi12345
# DOCKERHUB_REPO : youtubedl-material
# DOCKERHUB_MASTER_TAG: nightly
with:
images: |
${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }}
ghcr.io/${{ github.repository_owner }}/${{ secrets.DOCKERHUB_REPO }}
tags: |
type=raw,${{secrets.DOCKERHUB_MASTER_TAG}}-{{ date 'YYYY-MM-DD' }}
type=raw,${{secrets.DOCKERHUB_MASTER_TAG}}
type=sha,prefix=sha-,format=short
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: build & push images
uses: docker/build-push-action@v2
with:
@@ -79,5 +49,8 @@ jobs:
file: ./Dockerfile
platforms: linux/amd64,linux/arm,linux/arm64/v8
push: true
tags: ${{ steps.docker-meta.outputs.tags }}
labels: ${{ steps.docker-meta.outputs.labels }}
# 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,66 +1,77 @@
# Fetching our ffmpeg
FROM ubuntu:22.04 AS ffmpeg
ENV DEBIAN_FRONTEND=noninteractive
# Use script due local build compability
COPY ffmpeg-fetch.sh .
RUN sh ./ffmpeg-fetch.sh
RUN apt-get update && apt-get install -y software-properties-common
RUN add-apt-repository ppa:savoury1/ffmpeg4
RUN add-apt-repository ppa:savoury1/ffmpeg5 && apt-get update && apt-get install -y ffmpeg
#--------------# Stage 2
FROM ubuntu:22.04 as frontend
# Create our Ubuntu 22.04 with node 16
# Go to 20.04
FROM ubuntu:20.04 AS base
ENV DEBIAN_FRONTEND=noninteractive
ENV UID=1000
ENV GID=1000
ENV USER=youtube
ENV NO_UPDATE_NOTIFIER=true
ENV PM2_HOME=/app/pm2
RUN groupadd -g $GID $USER && useradd --system -m -g $USER --uid $UID $USER && \
apt update && \
apt install -y --no-install-recommends curl ca-certificates && \
curl -fsSL https://deb.nodesource.com/setup_16.x | bash - && \
apt install -y --no-install-recommends nodejs && \
npm -g install npm && \
apt clean && \
rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get -y install \
curl \
gnupg \
# Ubuntu 22.04 ships Node.JS 12 by default :)
nodejs \
# needed on 21.10 and before, maybe not on 22.04 YARN: brings along npm, solves dependency conflicts,
# spares us this spaghetti approach: https://stackoverflow.com/a/60547197
npm && \
apt-get install -f && \
npm config set strict-ssl false && \
npm install -g @angular/cli
# Build frontend
FROM base as frontend
RUN npm install -g @angular/cli
WORKDIR /build
COPY [ "package.json", "package-lock.json", "angular.json", "tsconfig.json", "/build/" ]
COPY [ "package.json", "package-lock.json", "/build/" ]
RUN npm install
COPY [ "angular.json", "tsconfig.json", "/build/" ]
COPY [ "src/", "/build/src/" ]
RUN npm install && \
npm run build && \
ls -al /build/backend/public
RUN npm run build
#--------------# Final Stage
FROM ubuntu:22.04
ENV UID=1000 \
GID=1000 \
USER=youtube \
NO_UPDATE_NOTIFIER=true
ENV DEBIAN_FRONTEND=noninteractive
RUN groupadd -g $GID $USER && useradd --system -g $USER --uid $UID $USER
RUN apt-get update && apt-get -y install \
npm \
python2 \
python3 \
gosu \
atomicparsley \
--no-install-recommends && \
apt-get install -f && \
apt-get autoremove --purge && \
apt-get autoremove && \
apt-get clean && \
rm -rf /var/lib/apt
# Install backend deps
FROM base as backend
WORKDIR /app
COPY [ "backend/","/app/" ]
RUN npm config set strict-ssl false && \
npm install --prod && \
ls -al
# Final image
FROM base
RUN npm install -g pm2 && \
apt update && \
apt install -y --no-install-recommends gosu python3-minimal python-is-python3 atomicparsley && \
apt clean && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
# User 1000 already exist from base image
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 --from=backend ["/app/","/app/"]
COPY --chown=$UID:$GID [ "backend/package.json", "backend/package-lock.json", "/app/" ]
ENV PM2_HOME=/app/pm2
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/" ]
# Add some persistence data
#VOLUME ["/app/appdata"]
COPY --chown=$UID:$GID [ "/backend/", "/app/" ]
EXPOSE 17442
ENTRYPOINT [ "/app/entrypoint.sh" ]
CMD [ "pm2-runtime","--raw","pm2.config.js" ]
# ENTRYPOINT [ "/app/entrypoint.sh" ]
CMD [ "pm2-runtime", "pm2.config.js" ]

View File

@@ -129,27 +129,6 @@ paths:
description: User is not authorized to view the file.
security:
- Auth query parameter: []
/api/updateFile:
post:
tags:
- files
summary: Updates file database object
description: Updates a file db object using its uid and a change object.
operationId: post-updateFile
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateFileRequest'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/SuccessObject'
security:
- Auth query parameter: []
/api/enableSharing:
post:
tags:
@@ -862,10 +841,17 @@ paths:
- Auth query parameter: []
tags:
- downloader
/api/clearDownloads:
/api/clearFinishedDownloads:
post:
summary: Clear multiple downloads
operationId: post-api-clear-downloads
tags:
- downloader
summary: Clear finished downloads
operationId: post-api-clear-finished-downloads
requestBody:
content:
application/json:
schema:
type: object
responses:
'200':
description: OK
@@ -873,17 +859,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SuccessObject'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ClearDownloadsRequest'
description: ''
description: "Clears multiple downloads based on a given filter."
security:
- Auth query parameter: []
tags:
- downloader
/api/getTask:
post:
summary: Get info for one task
@@ -1530,8 +1507,6 @@ components:
properties:
success:
type: boolean
error:
type: string
FileType:
type: string
enum:
@@ -1632,15 +1607,6 @@ components:
type: array
items:
$ref: '#/components/schemas/Download'
ClearDownloadsRequest:
type: object
properties:
clear_finished:
type: boolean
clear_paused:
type: boolean
clear_errors:
type: boolean
GetTaskRequest:
type: object
properties:
@@ -1761,18 +1727,6 @@ components:
type: boolean
file:
$ref: '#/components/schemas/DatabaseFile'
UpdateFileRequest:
required:
- uid
- change_obj
type: object
properties:
uid:
type: string
description: Video UID
change_obj:
type: object
description: Object with fields to update as keys and their new values
SharingToggle:
required:
- uid
@@ -2199,6 +2153,7 @@ components:
type: boolean
result:
allOf:
- $ref: '#/components/schemas/file'
- type: object
properties:
formats:
@@ -2356,9 +2311,6 @@ components:
type: string
thumbnailURL:
type: string
description: Backup if thumbnailPath is not defined
thumbnailPath:
type: string
isAudio:
type: boolean
duration:
@@ -2370,7 +2322,6 @@ components:
type: string
size:
type: number
description: In bytes
path:
type: string
upload_date:
@@ -2379,12 +2330,6 @@ components:
type: string
sharingEnabled:
type: boolean
category:
$ref: '#/components/schemas/Category'
view_count:
type: number
local_view_count:
type: number
Playlist:
required:
- uids
@@ -2414,8 +2359,6 @@ components:
type: number
user_uid:
type: string
auto:
type: boolean
Download:
required:
- url
@@ -2602,6 +2545,28 @@ components:
type: string
passhash:
type: string
files:
type: object
properties:
audio:
type: array
items:
$ref: '#/components/schemas/file'
video:
type: array
items:
$ref: '#/components/schemas/file'
playlists:
type: object
properties:
audio:
type: array
items:
$ref: '#/components/schemas/file'
video:
type: array
items:
$ref: '#/components/schemas/file'
subscriptions:
type: array
items:

View File

@@ -30,8 +30,7 @@
"src/backend"
],
"styles": [
"src/styles.scss",
"src/bootstrap.min.css"
"src/styles.scss"
],
"scripts": [],
"vendorChunk": true,
@@ -119,8 +118,7 @@
"src/backend"
],
"styles": [
"src/styles.scss",
"src/bootstrap.min.css"
"src/styles.scss"
],
"scripts": []
},
@@ -153,8 +151,7 @@
"tsConfig": "src/tsconfig.spec.json",
"scripts": [],
"styles": [
"src/styles.scss",
"src/bootstrap.min.css"
"src/styles.scss"
],
"assets": [
"src/assets",

View File

@@ -933,34 +933,23 @@ app.post('/api/getAllFiles', optionalJwt, async function (req, res) {
else if (file_type_filter === 'video_only') filter_obj['isAudio'] = false;
files = await db_api.getRecords('files', filter_obj, false, sort, range, text_search);
const file_count = await db_api.getRecords('files', filter_obj, true);
let file_count = await db_api.getRecords('files', filter_obj, true);
playlists = await db_api.getRecords('playlists', {user_uid: uuid});
const categories = await categories_api.getCategoriesAsPlaylists(files);
if (categories) {
playlists = playlists.concat(categories);
}
files = JSON.parse(JSON.stringify(files));
res.send({
files: files,
file_count: file_count,
playlists: playlists
});
});
app.post('/api/updateFile', optionalJwt, async function (req, res) {
const uid = req.body.uid;
const change_obj = req.body.change_obj;
const file = await db_api.updateRecord('files', {uid: uid}, change_obj);
if (!file) {
res.send({
success: false,
error: 'File could not be found'
});
} else {
res.send({
success: true
});
}
});
app.post('/api/checkConcurrentStream', async (req, res) => {
const uid = req.body.uid;
@@ -1376,7 +1365,7 @@ app.post('/api/getPlaylists', optionalJwt, async (req, res) => {
let playlists = await db_api.getRecords('playlists', {user_uid: uuid});
if (include_categories) {
const categories = await categories_api.getCategoriesAsPlaylists();
const categories = await categories_api.getCategoriesAsPlaylists(files);
if (categories) {
playlists = playlists.concat(categories);
}
@@ -1680,15 +1669,9 @@ app.post('/api/download', optionalJwt, async (req, res) => {
}
});
app.post('/api/clearDownloads', optionalJwt, async (req, res) => {
app.post('/api/clearFinishedDownloads', optionalJwt, async (req, res) => {
const user_uid = req.isAuthenticated() ? req.user.uid : null;
const clear_finished = req.body.clear_finished;
const clear_paused = req.body.clear_paused;
const clear_errors = req.body.clear_errors;
let success = true;
if (clear_finished) success &= await db_api.removeAllRecords('download_queue', {finished: true, user_uid: user_uid});
if (clear_paused) success &= await db_api.removeAllRecords('download_queue', {paused: true, user_uid: user_uid});
if (clear_errors) success &= await db_api.removeAllRecords('download_queue', {error: {$ne: null}, user_uid: user_uid});
const success = db_api.removeAllRecords('download_queue', {finished: true, user_uid: user_uid});
res.send({success: success});
});

View File

@@ -171,12 +171,8 @@ exports.registerUser = async function(req, res) {
exports.login = async (username, password) => {
// even if we're using LDAP, we still want users to be able to login using internal credentials
const user = await db_api.getRecord('users', {name: username});
if (!user) {
if (config_api.getConfigItem('ytdl_auth_method') === 'internal') logger.error(`User ${username} not found`);
return false;
}
if (!user) { logger.error(`User ${username} not found`); return false }
if (user.auth_method && user.auth_method !== 'internal') { return false }
return await bcrypt.compare(password, user.passhash) ? user : false;
}

View File

@@ -55,18 +55,17 @@ async function getCategories() {
return categories ? categories : null;
}
async function getCategoriesAsPlaylists() {
async function getCategoriesAsPlaylists(files = null) {
const categories_as_playlists = [];
const available_categories = await getCategories();
if (available_categories) {
if (available_categories && files) {
for (let category of available_categories) {
const files_that_match = await db_api.getRecords('files', {'category.uid': category['uid']});
const files_that_match = utils.addUIDsToCategory(category, files);
if (files_that_match && files_that_match.length > 0) {
category['thumbnailURL'] = files_that_match[0].thumbnailURL;
category['thumbnailPath'] = files_that_match[0].thumbnailPath;
category['duration'] = files_that_match.reduce((a, b) => a + utils.durationStringToNumber(b.duration), 0);
category['id'] = category['uid'];
category['auto'] = true;
categories_as_playlists.push(category);
}
}

View File

@@ -127,7 +127,7 @@ function setConfigItem(key, value) {
success = setConfigFile(config_json);
return success;
}
};
function setConfigItems(items) {
let success = false;

View File

@@ -387,9 +387,9 @@ exports.getPlaylist = async (playlist_id, user_uid = null, require_sharing = fal
if (!playlist) {
playlist = await exports.getRecord('categories', {uid: playlist_id});
if (playlist) {
const uids = (await exports.getRecords('files', {'category.uid': playlist_id})).map(file => file.uid);
playlist['uids'] = uids;
playlist['auto'] = true;
// category found
const files = await exports.getFiles(user_uid);
utils.addUIDsToCategory(playlist, files);
}
}
@@ -629,7 +629,7 @@ exports.bulkInsertRecordsIntoTable = async (table, docs) => {
exports.getRecord = async (table, filter_obj) => {
// local db override
if (using_local_db) {
return exports.applyFilterLocalDB(local_db.get(table), filter_obj, 'find').value();
return applyFilterLocalDB(local_db.get(table), filter_obj, 'find').value();
}
return await database.collection(table).findOne(filter_obj);
@@ -638,7 +638,7 @@ exports.getRecord = async (table, filter_obj) => {
exports.getRecords = async (table, filter_obj = null, return_count = false, sort = null, range = null) => {
// local db override
if (using_local_db) {
let cursor = filter_obj ? exports.applyFilterLocalDB(local_db.get(table), filter_obj, 'filter').value() : local_db.get(table).value();
let cursor = filter_obj ? applyFilterLocalDB(local_db.get(table), filter_obj, 'filter').value() : local_db.get(table).value();
if (sort) {
cursor = cursor.sort((a, b) => (a[sort['by']] > b[sort['by']] ? sort['order'] : sort['order']*-1));
}
@@ -664,7 +664,7 @@ exports.getRecords = async (table, filter_obj = null, return_count = false, sort
exports.updateRecord = async (table, filter_obj, update_obj) => {
// local db override
if (using_local_db) {
exports.applyFilterLocalDB(local_db.get(table), filter_obj, 'find').assign(update_obj).write();
applyFilterLocalDB(local_db.get(table), filter_obj, 'find').assign(update_obj).write();
return true;
}
@@ -677,7 +677,7 @@ exports.updateRecord = async (table, filter_obj, update_obj) => {
exports.updateRecords = async (table, filter_obj, update_obj) => {
// local db override
if (using_local_db) {
exports.applyFilterLocalDB(local_db.get(table), filter_obj, 'filter').assign(update_obj).write();
applyFilterLocalDB(local_db.get(table), filter_obj, 'filter').assign(update_obj).write();
return true;
}
@@ -722,7 +722,7 @@ exports.bulkUpdateRecords = async (table, key_label, update_obj) => {
exports.pushToRecordsArray = async (table, filter_obj, key, value) => {
// local db override
if (using_local_db) {
exports.applyFilterLocalDB(local_db.get(table), filter_obj, 'find').get(key).push(value).write();
applyFilterLocalDB(local_db.get(table), filter_obj, 'find').get(key).push(value).write();
return true;
}
@@ -733,7 +733,7 @@ exports.pushToRecordsArray = async (table, filter_obj, key, value) => {
exports.pullFromRecordsArray = async (table, filter_obj, key, value) => {
// local db override
if (using_local_db) {
exports.applyFilterLocalDB(local_db.get(table), filter_obj, 'find').get(key).pull(value).write();
applyFilterLocalDB(local_db.get(table), filter_obj, 'find').get(key).pull(value).write();
return true;
}
@@ -746,7 +746,7 @@ exports.pullFromRecordsArray = async (table, filter_obj, key, value) => {
exports.removeRecord = async (table, filter_obj) => {
// local db override
if (using_local_db) {
exports.applyFilterLocalDB(local_db.get(table), filter_obj, 'remove').write();
applyFilterLocalDB(local_db.get(table), filter_obj, 'remove').write();
return true;
}
@@ -757,7 +757,7 @@ exports.removeRecord = async (table, filter_obj) => {
// exports.removeRecordsByUIDBulk = async (table, uids) => {
// // local db override
// if (using_local_db) {
// exports.applyFilterLocalDB(local_db.get(table), filter_obj, 'remove').write();
// applyFilterLocalDB(local_db.get(table), filter_obj, 'remove').write();
// return true;
// }
@@ -821,7 +821,7 @@ exports.removeAllRecords = async (table = null, filter_obj = null) => {
if (using_local_db) {
for (let i = 0; i < tables_to_remove.length; i++) {
const table_to_remove = tables_to_remove[i];
if (filter_obj) exports.applyFilterLocalDB(local_db.get(table), filter_obj, 'remove').write();
if (filter_obj) applyFilterLocalDB(local_db.get(table), filter_obj, 'remove').write();
else local_db.assign({[table_to_remove]: []}).write();
logger.debug(`Successfully removed records from ${table_to_remove}`);
}
@@ -1075,13 +1075,8 @@ exports.transferDB = async (local_to_remote) => {
This function is necessary to emulate mongodb's ability to search for null or missing values.
A filter of null or undefined for a property will find docs that have that property missing, or have it
null or undefined. We want that same functionality for the local DB as well
error: {$ne: null}
^ ^
| |
filter_prop filter_prop_value
*/
exports.applyFilterLocalDB = (db_path, filter_obj, operation) => {
const applyFilterLocalDB = (db_path, filter_obj, operation) => {
const filter_props = Object.keys(filter_obj);
const return_val = db_path[operation](record => {
if (!filter_props) return true;
@@ -1090,20 +1085,14 @@ exports.applyFilterLocalDB = (db_path, filter_obj, operation) => {
const filter_prop = filter_props[i];
const filter_prop_value = filter_obj[filter_prop];
if (filter_prop_value === undefined || filter_prop_value === null) {
filtered &= record[filter_prop] === undefined || record[filter_prop] === null;
filtered &= record[filter_prop] === undefined || record[filter_prop] === null
} else {
if (typeof filter_prop_value === 'object') {
if ('$regex' in filter_prop_value) {
if (filter_prop_value['$regex']) {
filtered &= (record[filter_prop].search(new RegExp(filter_prop_value['$regex'], filter_prop_value['$options'])) !== -1);
} else if ('$ne' in filter_prop_value) {
filtered &= filter_prop in record && record[filter_prop] !== filter_prop_value['$ne'];
}
} else {
// handle case of nested property check
if (filter_prop.includes('.'))
filtered &= utils.searchObjectByString(record, filter_prop) === filter_prop_value;
else
filtered &= record[filter_prop] === filter_prop_value;
filtered &= record[filter_prop] === filter_prop_value;
}
}
}

View File

@@ -108,7 +108,6 @@ exports.clearDownload = async (download_uid) => {
}
async function handleDownloadError(download_uid, error_message) {
if (!download_uid) return;
await db_api.updateRecord('download_queue', {uid: download_uid}, {error: error_message, finished: true, running: false});
}
@@ -187,7 +186,7 @@ async function collectInfo(download_uid) {
let args = await exports.generateArgs(url, type, options, download['user_uid']);
// get video info prior to download
let info = await exports.getVideoInfoByURL(url, args, download_uid);
let info = await getVideoInfoByURL(url, args, download_uid);
if (!info) {
// info failed, error presumably already recorded
@@ -204,11 +203,9 @@ async function collectInfo(download_uid) {
options.customOutput = category['custom_output'];
options.noRelativePath = true;
args = await exports.generateArgs(url, type, options, download['user_uid']);
info = await exports.getVideoInfoByURL(url, args, download_uid);
info = await getVideoInfoByURL(url, args, download_uid);
}
download['category'] = category;
// setup info required to calculate download progress
const expected_file_size = utils.getExpectedFileSize(info);
@@ -510,7 +507,7 @@ exports.generateArgs = async (url, type, options, user_uid = null, simulated = f
return downloadConfig;
}
exports.getVideoInfoByURL = async (url, args = [], download_uid = null) => {
async function getVideoInfoByURL(url, args = [], download_uid = null) {
return new Promise(resolve => {
// remove bad args
const new_args = [...args];

View File

@@ -148,7 +148,6 @@ exports.updateTaskSchedule = async (task_key, schedule) => {
await db_api.updateRecord('tasks', {key: task_key}, {schedule: schedule});
if (TASKS[task_key]['job']) {
TASKS[task_key]['job'].cancel();
TASKS[task_key]['job'] = null;
}
if (schedule) {
TASKS[task_key]['job'] = scheduleJob(task_key, schedule);

View File

@@ -42,25 +42,6 @@ const { uuid } = require('uuidv4');
db_api.initialize(db, users_db);
const sample_video_json = {
id: "Sample Video",
title: "Sample Video",
thumbnailURL: "https://sampleurl.jpg",
isAudio: false,
duration: 177.413,
url: "sampleurl.com",
uploader: "Sample Uploader",
size: 2838445,
path: "users\\admin\\video\\Sample Video.mp4",
upload_date: "2017-07-28",
description: null,
view_count: 230,
abr: 128,
thumbnailPath: null,
user_uid: "admin",
uid: "1ada04ab-2773-4dd4-bbdd-3e2d40761c50",
registered: 1628469039377
}
describe('Database', async function() {
describe('Import', async function() {
@@ -233,7 +214,7 @@ describe('Database', async function() {
for (let i = 0; i < NUM_RECORDS_TO_ADD; i++) {
const uid = uuid();
if (i === NUM_RECORDS_TO_ADD/2) random_uid = uid;
test_records.push({"id":"RandomTextRandomText","title":"RandomTextRandomTextRandomTextRandomTextRandomTextRandomTextRandomTextRandomText","thumbnailURL":"https://i.ytimg.com/vi/randomurl/maxresdefault.jpg","isAudio":true,"duration":312,"url":"https://www.youtube.com/watch?v=randomvideo","uploader":"randomUploader","size":5060157,"path":"audio\\RandomTextRandomText.mp3","upload_date":"2016-05-11","description":"RandomTextRandomTextRandomTextRandomTextRandomTextRandomTextRandomTextRandomTextRandomTextRandomTextRandomTextRandomText","view_count":118689353,"height":null,"abr":160,"uid": uid,"registered":1626672120632});
test_records.push({"id":"A$AP Mob - Yamborghini High (Official Music Video) ft. Juicy J","title":"A$AP Mob - Yamborghini High (Official Music Video) ft. Juicy J","thumbnailURL":"https://i.ytimg.com/vi/tt7gP_IW-1w/maxresdefault.jpg","isAudio":true,"duration":312,"url":"https://www.youtube.com/watch?v=tt7gP_IW-1w","uploader":"asapmobVEVO","size":5060157,"path":"audio\\A$AP Mob - Yamborghini High (Official Music Video) ft. Juicy J.mp3","upload_date":"2016-05-11","description":"A$AP Mob ft. Juicy J - \"Yamborghini High\" Get it now on:\niTunes: http://smarturl.it/iYAMH?IQid=yt\nListen on Spotify: http://smarturl.it/sYAMH?IQid=yt\nGoogle Play: http://smarturl.it/gYAMH?IQid=yt\nAmazon: http://smarturl.it/aYAMH?IQid=yt\n\nFollow A$AP Mob:\nhttps://www.facebook.com/asapmobofficial\nhttps://twitter.com/ASAPMOB\nhttp://instagram.com/asapmob \nhttp://www.asapmob.com/\n\n#AsapMob #YamborghiniHigh #Vevo #HipHop #OfficialMusicVideo #JuicyJ","view_count":118689353,"height":null,"abr":160,"uid": uid,"registered":1626672120632});
}
const insert_start = Date.now();
let success = await db_api.bulkInsertRecordsIntoTable('test', test_records);
@@ -254,30 +235,6 @@ describe('Database', async function() {
assert(success);
});
});
describe('Local DB Filters', async function() {
it('Basic', async function() {
const result = db_api.applyFilterLocalDB([{test: 'test'}, {test: 'test1'}], {test: 'test'}, 'find');
assert(result && result['test'] === 'test');
});
it('Regex', async function() {
const filter = {$regex: `\\w+\\d`, $options: 'i'};
const result = db_api.applyFilterLocalDB([{test: 'test'}, {test: 'test1'}], {test: filter}, 'find');
assert(result && result['test'] === 'test1');
});
it('Not equals', async function() {
const filter = {$ne: 'test'};
const result = db_api.applyFilterLocalDB([{test: 'test'}, {test: 'test1'}], {test: filter}, 'find');
assert(result && result['test'] === 'test1');
});
it('Nested', async function() {
const result = db_api.applyFilterLocalDB([{test1: {test2: 'test3'}}, {test4: 'test5'}], {'test1.test2': 'test3'}, 'find');
assert(result && result['test1']['test2'] === 'test3');
});
})
});
describe('Multi User', async function() {
@@ -296,12 +253,10 @@ describe('Multi User', async function() {
assert(user);
});
});
describe('Video player - normal', async function() {
await db_api.removeRecord('files', {uid: sample_video_json['uid']});
await db_api.insertRecordIntoTable('files', sample_video_json);
const video_to_test = sample_video_json['uid'];
describe('Video player - normal', function() {
const video_to_test = 'ebbcfffb-d6f1-4510-ad25-d1ec82e0477e';
it('Get video', async function() {
const video_obj = await db_api.getVideo(video_to_test);
const video_obj = db_api.getVideo(video_to_test, 'admin');
assert(video_obj);
});
@@ -386,9 +341,7 @@ describe('Downloader', function() {
});
it('Get file info', async function() {
this.timeout(300000);
const info = await downloader_api.getVideoInfoByURL(url);
assert(!!info);
});
it('Download file', async function() {
@@ -407,23 +360,20 @@ describe('Downloader', function() {
});
it('Pause file', async function() {
const returned_download = await downloader_api.createDownload(url, 'video', options);
await downloader_api.pauseDownload(returned_download['uid']);
const updated_download = await db_api.getRecord('download_queue', {uid: returned_download['uid']});
assert(updated_download['paused'] && !updated_download['running']);
});
it('Generate args', async function() {
const args = await downloader_api.generateArgs(url, 'video', options);
assert(args.length > 0);
console.log(args);
});
it('Generate args - subscription', async function() {
subscriptions_api.initialize(db_api, logger);
const sub = await subscriptions_api.getSubscription(sub_id);
const sub_options = subscriptions_api.generateOptionsForSubscriptionDownload(sub, 'admin');
const args_normal = await downloader_api.generateArgs(url, 'video', options);
const args_sub = await downloader_api.generateArgs(url, 'video', sub_options, 'admin');
console.log(JSON.stringify(args_normal) !== JSON.stringify(args_sub));
const args = await downloader_api.generateArgs(url, 'video', sub_options, 'admin');
console.log(args);
});
it('Generate kodi NFO file', async function() {
@@ -467,7 +417,7 @@ describe('Tasks', function() {
};
tasks_api.TASKS['dummy_task'] = dummy_task;
await tasks_api.setupTasks();
await tasks_api.initialize();
});
it('Backup db', async function() {
const backups_original = await utils.recFindByExt('appdata', 'bak');
@@ -479,13 +429,12 @@ describe('Tasks', function() {
});
it('Check for missing files', async function() {
this.timeout(300000);
await db_api.removeAllRecords('files', {uid: 'test'});
const test_missing_file = {uid: 'test', path: 'test/missing_file.mp4'};
await db_api.insertRecordIntoTable('files', test_missing_file);
await tasks_api.executeTask('missing_files_check');
const missing_file_db_record = await db_api.getRecord('files', {uid: 'test'});
assert(!missing_file_db_record, true);
const task_obj = await db_api.getRecord('tasks', {key: 'missing_files_check'});
assert(task_obj['data'] && task_obj['data']['uids'] && task_obj['data']['uids'].length >= 1, true);
});
it('Check for duplicate files', async function() {
@@ -498,13 +447,10 @@ describe('Tasks', function() {
await db_api.insertRecordIntoTable('files', test_duplicate_file1);
await db_api.insertRecordIntoTable('files', test_duplicate_file2);
await db_api.insertRecordIntoTable('files', test_duplicate_file3);
await tasks_api.executeRun('duplicate_files_check');
const task_obj = await db_api.getRecord('tasks', {key: 'duplicate_files_check'});
assert(task_obj['data'] && task_obj['data']['uids'] && task_obj['data']['uids'].length >= 1, true);
await tasks_api.executeTask('duplicate_files_check');
const task_obj = await db_api.getRecord('tasks', {key: 'duplicate_files_check'});
const duplicated_record_count = await db_api.getRecords('files', {path: 'test/missing_file.mp4'}, true);
assert(task_obj['data'] && task_obj['data']['uids'] && task_obj['data']['uids'].length >= 1, true);
assert(duplicated_record_count == 1, true);
});
@@ -529,36 +475,22 @@ describe('Tasks', function() {
});
it('Schedule and cancel task', async function() {
this.timeout(5000);
const today_one_year = new Date();
today_one_year.setFullYear(today_one_year.getFullYear() + 1);
const schedule_obj = {
type: 'timestamp',
data: { timestamp: today_one_year.getTime() }
}
await tasks_api.updateTaskSchedule('dummy_task', schedule_obj);
const dummy_task = await db_api.getRecord('tasks', {key: 'dummy_task'});
assert(!!tasks_api.TASKS['dummy_task']['job']);
assert(!!dummy_task['schedule']);
const today_4_hours = new Date();
today_4_hours.setHours(today_4_hours.getHours() + 4);
await tasks_api.updateTaskSchedule('dummy_task', today_4_hours);
assert(!!tasks_api.TASKS['dummy_task']['job'], true);
await tasks_api.updateTaskSchedule('dummy_task', null);
const dummy_task_updated = await db_api.getRecord('tasks', {key: 'dummy_task'});
assert(!tasks_api.TASKS['dummy_task']['job']);
assert(!dummy_task_updated['schedule']);
assert(!!tasks_api.TASKS['dummy_task']['job'], false);
});
it('Schedule and run task', async function() {
this.timeout(5000);
const today_1_second = new Date();
today_1_second.setSeconds(today_1_second.getSeconds() + 1);
const schedule_obj = {
type: 'timestamp',
data: { timestamp: today_1_second.getTime() }
}
await tasks_api.updateTaskSchedule('dummy_task', schedule_obj);
assert(!!tasks_api.TASKS['dummy_task']['job']);
await tasks_api.updateTaskSchedule('dummy_task', today_1_second);
assert(!!tasks_api.TASKS['dummy_task']['job'], true);
await utils.wait(2000);
const dummy_task_obj = await db_api.getRecord('tasks', {key: 'dummy_task'});
assert(dummy_task_obj['data']);
assert(dummy_task_obj['data'], true);
});
});

View File

@@ -456,21 +456,6 @@ function injectArgs(original_args, new_args) {
return updated_args;
}
const searchObjectByString = function(o, s) {
s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
s = s.replace(/^\./, ''); // strip a leading dot
var a = s.split('.');
for (var i = 0, n = a.length; i < n; ++i) {
var k = a[i];
if (k in o) {
o = o[k];
} else {
return;
}
}
return o;
}
// objects
function File(id, title, thumbnailURL, isAudio, duration, url, uploader, size, path, upload_date, description, view_count, height, abr) {
@@ -504,6 +489,7 @@ module.exports = {
createContainerZipFile: createContainerZipFile,
durationStringToNumber: durationStringToNumber,
getMatchingCategoryFiles: getMatchingCategoryFiles,
addUIDsToCategory: addUIDsToCategory,
getCurrentDownloader: getCurrentDownloader,
recFindByExt: recFindByExt,
removeFileExtension: removeFileExtension,
@@ -515,6 +501,5 @@ module.exports = {
fetchFile: fetchFile,
restartServer: restartServer,
injectArgs: injectArgs,
searchObjectByString: searchObjectByString,
File: File
}

View File

@@ -90,7 +90,7 @@ exports.updateYoutubeDL = async (latest_update_version) => {
exports.verifyBinaryExistsLinux = () => {
const details_json = fs.readJSONSync(CONSTS.DETAILS_BIN_PATH);
if (!is_windows && details_json && (details_json['path'].includes('.exe') || !details_json['path'])) {
if (!is_windows && details_json && 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;

View File

@@ -40,4 +40,4 @@ 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
rm -rf /tmp/ffmpeg ffmpeg.txz

59
package-lock.json generated
View File

@@ -3295,12 +3295,65 @@
"safer-buffer": "~2.1.0"
}
},
"asn1.js": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
"dev": true,
"requires": {
"bn.js": "^4.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"safer-buffer": "^2.1.0"
},
"dependencies": {
"bn.js": {
"version": "4.11.9",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
"dev": true
}
}
},
"assert": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
"integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
"dev": true,
"requires": {
"object-assign": "^4.1.1",
"util": "0.10.3"
},
"dependencies": {
"inherits": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
"integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
"dev": true
},
"util": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
"integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
"dev": true,
"requires": {
"inherits": "2.0.1"
}
}
}
},
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
},
"assign-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
"integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
"dev": true
},
"ast-types-flow": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
@@ -3314,9 +3367,9 @@
"dev": true
},
"async": {
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"dev": true,
"requires": {
"lodash": "^4.17.14"

View File

@@ -6,7 +6,7 @@
"ng": "ng",
"start": "ng serve",
"build": "ng build --configuration production",
"prebuild": "node src/postbuild.mjs",
"prebuild": "node src/postbuild.js",
"heroku-postbuild": "npm install --prefix backend",
"test": "ng test",
"lint": "ng lint",

View File

@@ -4,7 +4,6 @@
export type { AddFileToPlaylistRequest } from './models/AddFileToPlaylistRequest';
export type { BaseChangePermissionsRequest } from './models/BaseChangePermissionsRequest';
export type { binary } from './models/binary';
export type { body_19 } from './models/body_19';
export type { body_20 } from './models/body_20';
export type { Category } from './models/Category';
@@ -13,7 +12,6 @@ export type { ChangeRolePermissionsRequest } from './models/ChangeRolePermission
export type { ChangeUserPermissionsRequest } from './models/ChangeUserPermissionsRequest';
export type { CheckConcurrentStreamRequest } from './models/CheckConcurrentStreamRequest';
export type { CheckConcurrentStreamResponse } from './models/CheckConcurrentStreamResponse';
export type { ClearDownloadsRequest } from './models/ClearDownloadsRequest';
export type { ConcurrentStream } from './models/ConcurrentStream';
export type { Config } from './models/Config';
export type { ConfigResponse } from './models/ConfigResponse';
@@ -25,7 +23,6 @@ export type { CropFileSettings } from './models/CropFileSettings';
export type { DatabaseFile } from './models/DatabaseFile';
export { DBBackup } from './models/DBBackup';
export type { DBInfoResponse } from './models/DBInfoResponse';
export type { DeleteAllFilesResponse } from './models/DeleteAllFilesResponse';
export type { DeleteCategoryRequest } from './models/DeleteCategoryRequest';
export type { DeleteMp3Mp4Request } from './models/DeleteMp3Mp4Request';
export type { DeletePlaylistRequest } from './models/DeletePlaylistRequest';
@@ -39,6 +36,7 @@ export type { DownloadResponse } from './models/DownloadResponse';
export type { DownloadTwitchChatByVODIDRequest } from './models/DownloadTwitchChatByVODIDRequest';
export type { DownloadTwitchChatByVODIDResponse } from './models/DownloadTwitchChatByVODIDResponse';
export type { DownloadVideosForSubscriptionRequest } from './models/DownloadVideosForSubscriptionRequest';
export type { File } from './models/File';
export { FileType } from './models/FileType';
export type { GenerateArgsResponse } from './models/GenerateArgsResponse';
export type { GenerateNewApiKeyResponse } from './models/GenerateNewApiKeyResponse';
@@ -100,7 +98,6 @@ export type { UpdateCategoriesRequest } from './models/UpdateCategoriesRequest';
export type { UpdateCategoryRequest } from './models/UpdateCategoryRequest';
export type { UpdateConcurrentStreamRequest } from './models/UpdateConcurrentStreamRequest';
export type { UpdateConcurrentStreamResponse } from './models/UpdateConcurrentStreamResponse';
export type { UpdateFileRequest } from './models/UpdateFileRequest';
export type { UpdatePlaylistRequest } from './models/UpdatePlaylistRequest';
export type { UpdaterStatus } from './models/UpdaterStatus';
export type { UpdateServerRequest } from './models/UpdateServerRequest';

View File

@@ -2,7 +2,8 @@
/* tslint:disable */
/* eslint-disable */
export type AddFileToPlaylistRequest = {
export interface AddFileToPlaylistRequest {
file_uid: string;
playlist_id: string;
};
}

View File

@@ -2,10 +2,10 @@
/* tslint:disable */
/* eslint-disable */
import type { UserPermission } from './UserPermission';
import type { YesNo } from './YesNo';
import { UserPermission } from './UserPermission';
import { YesNo } from './YesNo';
export type BaseChangePermissionsRequest = {
export interface BaseChangePermissionsRequest {
permission: UserPermission;
new_value: YesNo;
};
}

View File

@@ -2,9 +2,9 @@
/* tslint:disable */
/* eslint-disable */
import type { CategoryRule } from './CategoryRule';
import { CategoryRule } from './CategoryRule';
export type Category = {
export interface Category {
name?: string;
uid?: string;
rules?: Array<CategoryRule>;
@@ -12,4 +12,4 @@ export type Category = {
* Overrides file output for downloaded files in category
*/
custom_output?: string;
};
}

View File

@@ -2,10 +2,11 @@
/* tslint:disable */
/* eslint-disable */
export type CategoryRule = {
export interface CategoryRule {
preceding_operator?: CategoryRule.preceding_operator;
comparator?: CategoryRule.comparator;
};
}
export namespace CategoryRule {

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { BaseChangePermissionsRequest } from './BaseChangePermissionsRequest';
import { BaseChangePermissionsRequest } from './BaseChangePermissionsRequest';
export type ChangeRolePermissionsRequest = (BaseChangePermissionsRequest & {
role: string;
});
export interface ChangeRolePermissionsRequest extends BaseChangePermissionsRequest {
role: string;
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { BaseChangePermissionsRequest } from './BaseChangePermissionsRequest';
import { BaseChangePermissionsRequest } from './BaseChangePermissionsRequest';
export type ChangeUserPermissionsRequest = (BaseChangePermissionsRequest & {
user_uid: string;
});
export interface ChangeUserPermissionsRequest extends BaseChangePermissionsRequest {
user_uid: string;
}

View File

@@ -2,9 +2,10 @@
/* tslint:disable */
/* eslint-disable */
export type CheckConcurrentStreamRequest = {
export interface CheckConcurrentStreamRequest {
/**
* UID of the concurrent stream
*/
uid: string;
};
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { ConcurrentStream } from './ConcurrentStream';
import { ConcurrentStream } from './ConcurrentStream';
export type CheckConcurrentStreamResponse = {
export interface CheckConcurrentStreamResponse {
stream: ConcurrentStream;
};
}

View File

@@ -1,9 +0,0 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type ClearDownloadsRequest = {
clear_finished?: boolean;
clear_paused?: boolean;
clear_errors?: boolean;
};

View File

@@ -2,8 +2,9 @@
/* tslint:disable */
/* eslint-disable */
export type ConcurrentStream = {
export interface ConcurrentStream {
playback_timestamp?: number;
unix_timestamp?: number;
playing?: boolean;
};
}

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type Config = {
export interface Config {
YoutubeDLMaterial: any;
};
}

View File

@@ -2,9 +2,9 @@
/* tslint:disable */
/* eslint-disable */
import type { Config } from './Config';
import { Config } from './Config';
export type ConfigResponse = {
export interface ConfigResponse {
config_file: Config;
success: boolean;
};
}

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type CreateCategoryRequest = {
export interface CreateCategoryRequest {
name: string;
};
}

View File

@@ -2,9 +2,9 @@
/* tslint:disable */
/* eslint-disable */
import type { Category } from './Category';
import { Category } from './Category';
export type CreateCategoryResponse = {
export interface CreateCategoryResponse {
new_category?: Category;
success?: boolean;
};
}

View File

@@ -2,11 +2,11 @@
/* tslint:disable */
/* eslint-disable */
import type { FileType } from './FileType';
import { FileType } from './FileType';
export type CreatePlaylistRequest = {
export interface CreatePlaylistRequest {
playlistName: string;
uids: Array<string>;
type: FileType;
thumbnailURL: string;
};
}

View File

@@ -2,9 +2,9 @@
/* tslint:disable */
/* eslint-disable */
import type { Playlist } from './Playlist';
import { Playlist } from './Playlist';
export type CreatePlaylistResponse = {
export interface CreatePlaylistResponse {
new_playlist: Playlist;
success: boolean;
};
}

View File

@@ -2,7 +2,8 @@
/* tslint:disable */
/* eslint-disable */
export type CropFileSettings = {
export interface CropFileSettings {
cropFileStart: number;
cropFileEnd: number;
};
}

View File

@@ -2,12 +2,13 @@
/* tslint:disable */
/* eslint-disable */
export type DBBackup = {
export interface DBBackup {
name: string;
timestamp: number;
size: number;
source: DBBackup.source;
};
}
export namespace DBBackup {

View File

@@ -2,17 +2,17 @@
/* tslint:disable */
/* eslint-disable */
import type { TableInfo } from './TableInfo';
import { TableInfo } from './TableInfo';
export type DBInfoResponse = {
export interface DBInfoResponse {
using_local_db?: boolean;
stats_by_table?: {
files?: TableInfo;
playlists?: TableInfo;
categories?: TableInfo;
subscriptions?: TableInfo;
users?: TableInfo;
roles?: TableInfo;
download_queue?: TableInfo;
files?: TableInfo,
playlists?: TableInfo,
categories?: TableInfo,
subscriptions?: TableInfo,
users?: TableInfo,
roles?: TableInfo,
download_queue?: TableInfo,
};
};
}

View File

@@ -2,16 +2,11 @@
/* tslint:disable */
/* eslint-disable */
import type { Category } from './Category';
export type DatabaseFile = {
export interface DatabaseFile {
id: string;
title: string;
/**
* Backup if thumbnailPath is not defined
*/
thumbnailURL: string;
thumbnailPath?: string;
isAudio: boolean;
/**
* In seconds
@@ -19,15 +14,9 @@ export type DatabaseFile = {
duration: number;
url: string;
uploader: string;
/**
* In bytes
*/
size: number;
path: string;
upload_date: string;
uid: string;
sharingEnabled?: boolean;
category?: Category;
view_count?: number;
local_view_count?: number;
};
}

View File

@@ -1,14 +0,0 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DeleteAllFilesResponse = {
/**
* Number of files found matching search parameters
*/
file_count?: number;
/**
* Number of files removed
*/
delete_count?: number;
};

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type DeleteCategoryRequest = {
export interface DeleteCategoryRequest {
category_uid: string;
};
}

View File

@@ -2,7 +2,8 @@
/* tslint:disable */
/* eslint-disable */
export type DeleteMp3Mp4Request = {
export interface DeleteMp3Mp4Request {
uid: string;
blacklistMode?: boolean;
};
}

View File

@@ -2,9 +2,9 @@
/* tslint:disable */
/* eslint-disable */
import type { FileType } from './FileType';
import { FileType } from './FileType';
export type DeletePlaylistRequest = {
export interface DeletePlaylistRequest {
playlist_id: string;
type: FileType;
};
}

View File

@@ -2,9 +2,9 @@
/* tslint:disable */
/* eslint-disable */
import type { SubscriptionRequestData } from './SubscriptionRequestData';
import { SubscriptionRequestData } from './SubscriptionRequestData';
export type DeleteSubscriptionFileRequest = {
export interface DeleteSubscriptionFileRequest {
file: string;
file_uid?: string;
sub: SubscriptionRequestData;
@@ -12,4 +12,4 @@ export type DeleteSubscriptionFileRequest = {
* If true, does not remove id from archive. Only valid if youtube-dl archive is enabled in settings.
*/
deleteForever?: boolean;
};
}

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type DeleteUserRequest = {
export interface DeleteUserRequest {
uid: string;
};
}

View File

@@ -0,0 +1,7 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type Dictionary<T> = {
[key: string]: T;
}

View File

@@ -2,7 +2,8 @@
/* tslint:disable */
/* eslint-disable */
export type Download = {
export interface Download {
uid: string;
ui_uid?: string;
running: boolean;
@@ -22,4 +23,4 @@ export type Download = {
user_uid?: string;
sub_id?: string;
sub_name?: string;
};
}

View File

@@ -2,8 +2,9 @@
/* tslint:disable */
/* eslint-disable */
export type DownloadArchiveRequest = {
export interface DownloadArchiveRequest {
sub: {
archive_dir: string;
archive_dir: string,
};
};
}

View File

@@ -2,13 +2,13 @@
/* tslint:disable */
/* eslint-disable */
import type { FileType } from './FileType';
import { FileType } from './FileType';
export type DownloadFileRequest = {
export interface DownloadFileRequest {
uid?: string;
uuid?: string;
sub_id?: string;
playlist_id?: string;
url?: string;
type?: FileType;
};
}

View File

@@ -2,10 +2,10 @@
/* tslint:disable */
/* eslint-disable */
import type { CropFileSettings } from './CropFileSettings';
import type { FileType } from './FileType';
import { CropFileSettings } from './CropFileSettings';
import { FileType } from './FileType';
export type DownloadRequest = {
export interface DownloadRequest {
url: string;
/**
* Video format code. Overrides other quality options.
@@ -41,4 +41,4 @@ export type DownloadRequest = {
maxBitrate?: string;
type?: FileType;
cropFileSettings?: CropFileSettings;
};
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { Download } from './Download';
import { Download } from './Download';
export type DownloadResponse = {
export interface DownloadResponse {
download?: Download;
};
}

View File

@@ -2,10 +2,10 @@
/* tslint:disable */
/* eslint-disable */
import type { FileType } from './FileType';
import type { Subscription } from './Subscription';
import { FileType } from './FileType';
import { Subscription } from './Subscription';
export type DownloadTwitchChatByVODIDRequest = {
export interface DownloadTwitchChatByVODIDRequest {
/**
* File ID
*/
@@ -20,4 +20,4 @@ export type DownloadTwitchChatByVODIDRequest = {
*/
uuid?: string;
sub?: Subscription;
};
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { TwitchChatMessage } from './TwitchChatMessage';
import { TwitchChatMessage } from './TwitchChatMessage';
export type DownloadTwitchChatByVODIDResponse = {
export interface DownloadTwitchChatByVODIDResponse {
chat: Array<TwitchChatMessage>;
};
}

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type DownloadVideosForSubscriptionRequest = {
export interface DownloadVideosForSubscriptionRequest {
subID: string;
};
}

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type binary = {
export interface File {
id?: string;
};
}

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export enum FileType {
AUDIO = 'audio',
VIDEO = 'video',

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type GenerateArgsResponse = {
export interface GenerateArgsResponse {
args?: Array<string>;
};
}

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type GenerateNewApiKeyResponse = {
export interface GenerateNewApiKeyResponse {
new_api_key: string;
};
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { Category } from './Category';
import { Category } from './Category';
export type GetAllCategoriesResponse = {
export interface GetAllCategoriesResponse {
categories: Array<Category>;
};
}

View File

@@ -2,9 +2,10 @@
/* tslint:disable */
/* eslint-disable */
export type GetAllDownloadsRequest = {
export interface GetAllDownloadsRequest {
/**
* Filters downloads with the array
*/
uids?: Array<string> | null;
};
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { Download } from './Download';
import { Download } from './Download';
export type GetAllDownloadsResponse = {
export interface GetAllDownloadsResponse {
downloads?: Array<Download>;
};
}

View File

@@ -2,13 +2,13 @@
/* tslint:disable */
/* eslint-disable */
import type { DatabaseFile } from './DatabaseFile';
import type { Playlist } from './Playlist';
import { DatabaseFile } from './DatabaseFile';
import { Playlist } from './Playlist';
export type GetAllFilesResponse = {
export interface GetAllFilesResponse {
files: Array<DatabaseFile>;
/**
* All video playlists
*/
playlists: Array<Playlist>;
};
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { Subscription } from './Subscription';
import { Subscription } from './Subscription';
export type GetAllSubscriptionsResponse = {
export interface GetAllSubscriptionsResponse {
subscriptions: Array<Subscription>;
};
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { Task } from './Task';
import { Task } from './Task';
export type GetAllTasksResponse = {
export interface GetAllTasksResponse {
tasks?: Array<Task>;
};
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { DBBackup } from './DBBackup';
import { DBBackup } from './DBBackup';
export type GetDBBackupsResponse = {
export interface GetDBBackupsResponse {
tasks?: Array<DBBackup>;
};
}

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type GetDownloadRequest = {
export interface GetDownloadRequest {
download_uid: string;
};
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { Download } from './Download';
import { Download } from './Download';
export type GetDownloadResponse = {
export interface GetDownloadResponse {
download?: Download;
};
}

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type GetFileFormatsRequest = {
export interface GetFileFormatsRequest {
url?: string;
};
}

View File

@@ -2,9 +2,11 @@
/* tslint:disable */
/* eslint-disable */
export type GetFileFormatsResponse = {
import { File } from './File';
export interface GetFileFormatsResponse {
success: boolean;
result: {
formats?: Array<any>;
formats?: Array<any>,
};
};
}

View File

@@ -2,9 +2,9 @@
/* tslint:disable */
/* eslint-disable */
import type { FileType } from './FileType';
import { FileType } from './FileType';
export type GetFileRequest = {
export interface GetFileRequest {
/**
* Video UID
*/
@@ -14,4 +14,4 @@ export type GetFileRequest = {
* User UID
*/
uuid?: string;
};
}

View File

@@ -2,9 +2,9 @@
/* tslint:disable */
/* eslint-disable */
import type { DatabaseFile } from './DatabaseFile';
import { DatabaseFile } from './DatabaseFile';
export type GetFileResponse = {
export interface GetFileResponse {
success: boolean;
file?: DatabaseFile;
};
}

View File

@@ -2,10 +2,10 @@
/* tslint:disable */
/* eslint-disable */
import type { FileType } from './FileType';
import type { Subscription } from './Subscription';
import { FileType } from './FileType';
import { Subscription } from './Subscription';
export type GetFullTwitchChatRequest = {
export interface GetFullTwitchChatRequest {
/**
* File ID
*/
@@ -16,4 +16,4 @@ export type GetFullTwitchChatRequest = {
*/
uuid?: string;
sub?: Subscription;
};
}

View File

@@ -2,7 +2,8 @@
/* tslint:disable */
/* eslint-disable */
export type GetFullTwitchChatResponse = {
export interface GetFullTwitchChatResponse {
success: boolean;
error?: string;
};
}

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type GetLogsRequest = {
export interface GetLogsRequest {
lines?: number;
};
}

View File

@@ -2,10 +2,11 @@
/* tslint:disable */
/* eslint-disable */
export type GetLogsResponse = {
export interface GetLogsResponse {
/**
* Number of lines to retrieve from the bottom
*/
logs?: string;
success?: boolean;
};
}

View File

@@ -2,13 +2,13 @@
/* tslint:disable */
/* eslint-disable */
import type { DatabaseFile } from './DatabaseFile';
import type { Playlist } from './Playlist';
import { DatabaseFile } from './DatabaseFile';
import { Playlist } from './Playlist';
export type GetMp3sResponse = {
export interface GetMp3sResponse {
mp3s: Array<DatabaseFile>;
/**
* All audio playlists
*/
playlists: Array<Playlist>;
};
}

View File

@@ -2,13 +2,13 @@
/* tslint:disable */
/* eslint-disable */
import type { DatabaseFile } from './DatabaseFile';
import type { Playlist } from './Playlist';
import { DatabaseFile } from './DatabaseFile';
import { Playlist } from './Playlist';
export type GetMp4sResponse = {
export interface GetMp4sResponse {
mp4s: Array<DatabaseFile>;
/**
* All video playlists
*/
playlists: Array<Playlist>;
};
}

View File

@@ -2,11 +2,11 @@
/* tslint:disable */
/* eslint-disable */
import type { FileType } from './FileType';
import { FileType } from './FileType';
export type GetPlaylistRequest = {
export interface GetPlaylistRequest {
playlist_id: string;
type?: FileType;
uuid?: string;
include_file_metadata?: boolean;
};
}

View File

@@ -2,11 +2,11 @@
/* tslint:disable */
/* eslint-disable */
import type { FileType } from './FileType';
import type { Playlist } from './Playlist';
import { FileType } from './FileType';
import { Playlist } from './Playlist';
export type GetPlaylistResponse = {
export interface GetPlaylistResponse {
playlist: Playlist;
type: FileType;
success: boolean;
};
}

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type GetPlaylistsRequest = {
export interface GetPlaylistsRequest {
include_categories?: boolean;
};
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { Playlist } from './Playlist';
import { Playlist } from './Playlist';
export type GetPlaylistsResponse = {
export interface GetPlaylistsResponse {
playlists: Array<Playlist>;
};
}

View File

@@ -2,15 +2,15 @@
/* tslint:disable */
/* eslint-disable */
import type { UserPermission } from './UserPermission';
import { UserPermission } from './UserPermission';
export type GetRolesResponse = {
export interface GetRolesResponse {
roles: {
admin?: {
permissions?: Array<UserPermission>;
};
permissions?: Array<UserPermission>,
},
user?: {
permissions?: Array<UserPermission>;
permissions?: Array<UserPermission>,
},
};
};
};
}

View File

@@ -2,7 +2,8 @@
/* tslint:disable */
/* eslint-disable */
export type GetSubscriptionRequest = {
export interface GetSubscriptionRequest {
/**
* Subscription ID
*/
@@ -11,4 +12,4 @@ export type GetSubscriptionRequest = {
* Subscription name
*/
name?: string;
};
}

View File

@@ -2,9 +2,9 @@
/* tslint:disable */
/* eslint-disable */
import type { Subscription } from './Subscription';
import { Subscription } from './Subscription';
export type GetSubscriptionResponse = {
export interface GetSubscriptionResponse {
subscription: Subscription;
files: Array<any>;
};
}

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type GetTaskRequest = {
export interface GetTaskRequest {
task_key: string;
};
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { Task } from './Task';
import { Task } from './Task';
export type GetTaskResponse = {
export interface GetTaskResponse {
task?: Task;
};
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { User } from './User';
import { User } from './User';
export type GetUsersResponse = {
export interface GetUsersResponse {
users: Array<User>;
};
}

View File

@@ -2,11 +2,12 @@
/* tslint:disable */
/* eslint-disable */
export type IncrementViewCountRequest = {
export interface IncrementViewCountRequest {
file_uid: string;
sub_id?: string;
/**
* User UID
*/
uuid?: string;
};
}

View File

@@ -2,7 +2,8 @@
/* tslint:disable */
/* eslint-disable */
export type LoginRequest = {
export interface LoginRequest {
username: string;
password: string;
};
}

View File

@@ -2,12 +2,12 @@
/* tslint:disable */
/* eslint-disable */
import type { User } from './User';
import type { UserPermission } from './UserPermission';
import { User } from './User';
import { UserPermission } from './UserPermission';
export type LoginResponse = {
export interface LoginResponse {
user?: User;
token?: string;
permissions?: Array<UserPermission>;
available_permissions?: Array<UserPermission>;
};
}

View File

@@ -2,9 +2,9 @@
/* tslint:disable */
/* eslint-disable */
import type { FileType } from './FileType';
import { FileType } from './FileType';
export type Playlist = {
export interface Playlist {
name: string;
uids: Array<string>;
id: string;
@@ -13,5 +13,4 @@ export type Playlist = {
registered: number;
duration: number;
user_uid?: string;
auto?: boolean;
};
}

View File

@@ -2,8 +2,9 @@
/* tslint:disable */
/* eslint-disable */
export type RegisterRequest = {
export interface RegisterRequest {
userid: string;
username: string;
password: string;
};
}

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { User } from './User';
import { User } from './User';
export type RegisterResponse = {
export interface RegisterResponse {
user?: User;
};
}

View File

@@ -2,6 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type RestoreDBBackupRequest = {
export interface RestoreDBBackupRequest {
file_name: string;
};
}

View File

@@ -2,15 +2,16 @@
/* tslint:disable */
/* eslint-disable */
export type Schedule = {
export interface Schedule {
type: Schedule.type;
data: {
dayOfWeek?: Array<number>;
hour?: number;
minute?: number;
timestamp?: number;
};
dayOfWeek?: Array<number>,
hour?: number,
minute?: number,
timestamp?: number,
};
}
export namespace Schedule {

View File

@@ -2,8 +2,8 @@
/* tslint:disable */
/* eslint-disable */
import type { Config } from './Config';
import { Config } from './Config';
export type SetConfigRequest = {
export interface SetConfigRequest {
new_config_file: Config;
};
}

View File

@@ -2,7 +2,8 @@
/* tslint:disable */
/* eslint-disable */
export type SharingToggle = {
export interface SharingToggle {
uid: string;
is_playlist?: boolean;
};
}

View File

@@ -2,7 +2,8 @@
/* tslint:disable */
/* eslint-disable */
export type SubscribeRequest = {
export interface SubscribeRequest {
name: string;
url: string;
timerange?: string;
@@ -10,4 +11,4 @@ export type SubscribeRequest = {
customArgs?: string;
customFileOutput?: string;
maxQuality?: string;
};
}

Some files were not shown because too many files have changed in this diff Show More