Compare commits

...

16 Commits

Author SHA1 Message Date
Isaac Abadi
9d1f93acfb Added translations for manage-role component 2022-06-30 00:44:11 -04:00
Isaac Abadi
077a0d8fdb Added migration to add tasks manager permission for admin role
All routes are now properly protected against logged in users w/o permissions
2022-06-30 00:43:57 -04:00
Tzahi12345
703848e4e5 Merge pull request #675 from FoxxMD/unknownFormatsFix
Fixed parsing expected file size for videos with no known formats
2022-06-29 11:31:32 -04:00
FoxxMD
74315b8c76 Fixed parsing expected file size for videos with no known formats 2022-06-29 09:37:26 -04:00
Glassed Silver
a9e95c5bb8 Merge pull request #672 from weblate/weblate-youtubedl-material-ytdl-material
Translations update from Hosted Weblate
2022-06-28 07:20:04 +02:00
Sebastian
fe45a889c9 Added translation using Weblate (Swedish) 2022-06-27 22:14:59 +02:00
Isaac Abadi
e726e991cc Partially reverted fecefde3ad 2022-06-27 13:12:53 -04:00
Isaac Abadi
940267651d Hotfix for bug where a sub with missing videos would cause a crash during migration #670 2022-06-27 03:52:05 -04:00
Isaac Abadi
2dc68139f7 Streaming-only subs are now actually paused
DB transfers in any direction now generate backups and associated logs are set to info
2022-06-27 00:08:41 -04:00
Isaac Abadi
301451d021 Fixed issue where tasks would not initialize properly after migration until server restart
streaming-only subscriptions now autopause due to deprecated
2022-06-26 23:47:03 -04:00
Isaac Abadi
a7f8795e7e Fixed missing latest tag on docker-release 2022-06-26 23:10:47 -04:00
Isaac Abadi
162094a9b9 File type filter now only appears with the paginator 2022-06-26 23:06:15 -04:00
Isaac Abadi
e843b4c97f Fixed crash during docker-release action 2022-06-26 22:07:21 -04:00
Isaac Abadi
c784091ad6 Fixed issue where docker-release action couldn't accept inputs (2) 2022-06-26 22:04:42 -04:00
Isaac Abadi
fb404d3cee Fixed issue where docker-release action couldn't accept inputs 2022-06-26 22:02:23 -04:00
Tzahi12345
68c2ee26ff Merge pull request #657 from Tzahi12345/4.3-prep
4.3 Prep
2022-06-26 21:13:51 -04:00
15 changed files with 2998 additions and 39 deletions

View File

@@ -36,7 +36,7 @@ jobs:
- name: Set image tag - name: Set image tag
id: tags id: tags
run: | run: |
if [ ${{ github.event.action }} == "workflow_dispatch" ]; then if [ "${{ github.event.inputs.tags }}" != "" ]; then
echo "::set-output name=tags::${{ github.event.inputs.tags }}" echo "::set-output name=tags::${{ github.event.inputs.tags }}"
elif [ ${{ github.event.action }} == "release" ]; then elif [ ${{ github.event.action }} == "release" ]; then
echo "::set-output name=tags::${{ github.event.release.tag_name }}" echo "::set-output name=tags::${{ github.event.release.tag_name }}"
@@ -53,8 +53,8 @@ jobs:
${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }} ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }}
ghcr.io/${{ github.repository_owner }}/${{ secrets.DOCKERHUB_REPO }} ghcr.io/${{ github.repository_owner }}/${{ secrets.DOCKERHUB_REPO }}
tags: | tags: |
raw=${{ steps.tags.outputs.tags }} type=raw,value=${{ steps.tags.outputs.tags }}
raw=latest type=raw,value=latest
- name: setup platform emulator - name: setup platform emulator
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v1

View File

@@ -47,7 +47,8 @@ RUN npm config set strict-ssl false && \
# Final image # Final image
FROM base FROM base
RUN apt update && \ RUN npm install -g pm2 && \
apt update && \
apt install -y --no-install-recommends gosu python3-minimal python-is-python3 python3-pip atomicparsley && \ apt install -y --no-install-recommends gosu python3-minimal python-is-python3 python3-pip atomicparsley && \
apt clean && \ apt clean && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*

View File

@@ -68,7 +68,8 @@ db.defaults(
configWriteFlag: false, configWriteFlag: false,
downloads: {}, downloads: {},
subscriptions: [], subscriptions: [],
files_to_db_migration_complete: false files_to_db_migration_complete: false,
tasks_manager_role_migration_complete: false
}).write(); }).write();
users_db.defaults( users_db.defaults(
@@ -187,13 +188,22 @@ async function checkMigrations() {
if (!new_db_system_migration_complete) { if (!new_db_system_migration_complete) {
logger.info('Beginning migration: 4.2->4.3+') logger.info('Beginning migration: 4.2->4.3+')
let success = await db_api.importJSONToDB(db.value(), users_db.value()); let success = await db_api.importJSONToDB(db.value(), users_db.value());
await tasks_api.setupTasks(); // necessary as tasks were not properly initialized at first
// sets migration to complete // sets migration to complete
db.set('new_db_system_migration_complete', true).write(); db.set('new_db_system_migration_complete', true).write();
if (success) { logger.info('4.2->4.3+ migration complete!'); } if (success) { logger.info('4.2->4.3+ migration complete!'); }
else { logger.error('Migration failed: 4.2->4.3+'); } else { logger.error('Migration failed: 4.2->4.3+'); }
} }
const tasks_manager_role_migration_complete = db.get('tasks_manager_role_migration_complete').value();
if (!tasks_manager_role_migration_complete) {
logger.info('Checking if tasks manager role permissions exist for admin user...');
const success = await auth_api.changeRolePermissions('admin', 'tasks_manager', 'yes');
if (success) logger.info('Task manager permissions check complete!');
else logger.error('Failed to auto add tasks manager permissions to admin role!');
db.set('tasks_manager_role_migration_complete', true).write();
}
return true; return true;
} }
@@ -575,7 +585,11 @@ async function watchSubscriptions() {
if (!subscriptions) return; if (!subscriptions) return;
const valid_subscriptions = subscriptions.filter(sub => !sub.paused); // auto pause deprecated streamingOnly mode
const streaming_only_subs = subscriptions.filter(sub => sub.streamingOnly);
subscriptions_api.updateSubscriptionPropertyMultiple(streaming_only_subs, {paused: true});
const valid_subscriptions = subscriptions.filter(sub => !sub.paused && !sub.streamingOnly);
let subscriptions_amount = valid_subscriptions.length; let subscriptions_amount = valid_subscriptions.length;
let delay_interval = calculateSubcriptionRetrievalDelay(subscriptions_amount); let delay_interval = calculateSubcriptionRetrievalDelay(subscriptions_amount);

View File

@@ -361,7 +361,6 @@ exports.userHasPermission = async function(user_uid, permission) {
logger.error('Invalid role ' + role); logger.error('Invalid role ' + role);
return false; return false;
} }
const role_permissions = (await db_api.getRecords('roles'))['permissions'];
const user_has_explicit_permission = user_obj['permissions'].includes(permission); const user_has_explicit_permission = user_obj['permissions'].includes(permission);
const permission_in_overrides = user_obj['permission_overrides'].includes(permission); const permission_in_overrides = user_obj['permission_overrides'].includes(permission);
@@ -376,7 +375,8 @@ exports.userHasPermission = async function(user_uid, permission) {
} }
// no overrides, let's check if the role has the permission // no overrides, let's check if the role has the permission
if (role_permissions.includes(permission)) { const role_has_permission = await exports.roleHasPermissions(role, permission);
if (role_has_permission) {
return true; return true;
} else { } else {
logger.verbose(`User ${user_uid} failed to get permission ${permission}`); logger.verbose(`User ${user_uid} failed to get permission ${permission}`);
@@ -384,6 +384,16 @@ exports.userHasPermission = async function(user_uid, permission) {
} }
} }
exports.roleHasPermissions = async function(role, permission) {
const role_obj = await db_api.getRecord('roles', {key: role})
if (!role) {
logger.error(`Role ${role} does not exist!`);
}
const role_permissions = role_obj['permissions'];
if (role_permissions && role_permissions.includes(permission)) return true;
else return false;
}
exports.userPermissions = async function(user_uid) { exports.userPermissions = async function(user_uid) {
let user_permissions = []; let user_permissions = [];
const user_obj = await db_api.getRecord('users', ({uid: user_uid})); const user_obj = await db_api.getRecord('users', ({uid: user_uid}));

View File

@@ -221,7 +221,8 @@ exports.AVAILABLE_PERMISSIONS = [
'subscriptions', 'subscriptions',
'sharing', 'sharing',
'advanced_download', 'advanced_download',
'downloads_manager' 'downloads_manager',
'tasks_manager'
]; ];
exports.DETAILS_BIN_PATH = 'node_modules/youtube-dl/bin/details' exports.DETAILS_BIN_PATH = 'node_modules/youtube-dl/bin/details'

View File

@@ -925,6 +925,7 @@ exports.importJSONToDB = async (db_json, users_json) => {
const createFilesRecords = (files, subscriptions) => { const createFilesRecords = (files, subscriptions) => {
for (let i = 0; i < subscriptions.length; i++) { for (let i = 0; i < subscriptions.length; i++) {
const subscription = subscriptions[i]; const subscription = subscriptions[i];
if (!subscription['videos']) continue;
subscription['videos'] = subscription['videos'].map(file => ({ ...file, sub_id: subscription['id'], user_uid: subscription['user_uid'] ? subscription['user_uid'] : undefined})); subscription['videos'] = subscription['videos'].map(file => ({ ...file, sub_id: subscription['id'], user_uid: subscription['user_uid'] ? subscription['user_uid'] : undefined}));
files = files.concat(subscriptions[i]['videos']); files = files.concat(subscriptions[i]['videos']);
} }
@@ -985,7 +986,7 @@ exports.backupDB = async () => {
const backup_file_name = `${using_local_db ? 'local' : 'remote'}_db.json.${Date.now()/1000}.bak`; const backup_file_name = `${using_local_db ? 'local' : 'remote'}_db.json.${Date.now()/1000}.bak`;
const path_to_backups = path.join(backup_dir, backup_file_name); const path_to_backups = path.join(backup_dir, backup_file_name);
logger.verbose(`Backing up ${using_local_db ? 'local' : 'remote'} DB to ${path_to_backups}`); logger.info(`Backing up ${using_local_db ? 'local' : 'remote'} DB to ${path_to_backups}`);
const table_to_records = {}; const table_to_records = {};
for (let i = 0; i < tables_list.length; i++) { for (let i = 0; i < tables_list.length; i++) {
@@ -1032,10 +1033,11 @@ exports.transferDB = async (local_to_remote) => {
table_to_records[table] = await exports.getRecords(table); table_to_records[table] = await exports.getRecords(table);
} }
logger.info('Backup up DB...');
await exports.backupDB(); // should backup always
using_local_db = !local_to_remote; using_local_db = !local_to_remote;
if (local_to_remote) { if (local_to_remote) {
logger.debug('Backup up DB...');
await exports.backupDB();
const db_connected = await exports.connectToDB(5, true); const db_connected = await exports.connectToDB(5, true);
if (!db_connected) { if (!db_connected) {
logger.error('Failed to transfer database - could not connect to MongoDB. Verify that your connection URL is valid.'); logger.error('Failed to transfer database - could not connect to MongoDB. Verify that your connection URL is valid.');

View File

@@ -45,7 +45,6 @@
"passport-jwt": "^4.0.0", "passport-jwt": "^4.0.0",
"passport-ldapauth": "^3.0.1", "passport-ldapauth": "^3.0.1",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"pm2": "^5.2.0",
"progress": "^2.0.3", "progress": "^2.0.3",
"ps-node": "^0.1.6", "ps-node": "^0.1.6",
"read-last-lines": "^1.7.2", "read-last-lines": "^1.7.2",

View File

@@ -458,7 +458,7 @@ async function updateSubscription(sub) {
async function updateSubscriptionPropertyMultiple(subs, assignment_obj) { async function updateSubscriptionPropertyMultiple(subs, assignment_obj) {
subs.forEach(async sub => { subs.forEach(async sub => {
await updateSubscriptionProperty(sub, assignment_obj, sub.user_uid); await updateSubscriptionProperty(sub, assignment_obj);
}); });
} }

View File

@@ -172,11 +172,13 @@ function getExpectedFileSize(input_info_jsons) {
const formats = info_json['format_id'].split('+'); const formats = info_json['format_id'].split('+');
let individual_expected_filesize = 0; let individual_expected_filesize = 0;
formats.forEach(format_id => { formats.forEach(format_id => {
info_json.formats.forEach(available_format => { if (info_json.formats !== undefined) {
if (available_format.format_id === format_id && (available_format.filesize || available_format.filesize_approx)) { info_json.formats.forEach(available_format => {
if (available_format.format_id === format_id && (available_format.filesize || available_format.filesize_approx)) {
individual_expected_filesize += (available_format.filesize ? available_format.filesize : available_format.filesize_approx); individual_expected_filesize += (available_format.filesize ? available_format.filesize : available_format.filesize_approx);
} }
}); });
}
}); });
expected_filesize += individual_expected_filesize; expected_filesize += individual_expected_filesize;
}); });
@@ -210,7 +212,7 @@ function deleteJSONFile(file_path, type) {
const ext = type === 'audio' ? '.mp3' : '.mp4'; const ext = type === 'audio' ? '.mp3' : '.mp4';
const file_path_no_extension = removeFileExtension(file_path); const file_path_no_extension = removeFileExtension(file_path);
let json_path = file_path_no_extension + '.info.json'; let json_path = file_path_no_extension + '.info.json';
let alternate_json_path = file_path_no_extension + ext + '.info.json'; let alternate_json_path = file_path_no_extension + ext + '.info.json';
@@ -331,9 +333,9 @@ function createEdgeNGrams(str) {
if (str && str.length > 3) { if (str && str.length > 3) {
const minGram = 3 const minGram = 3
const maxGram = str.length const maxGram = str.length
return str.split(" ").reduce((ngrams, token) => { return str.split(" ").reduce((ngrams, token) => {
if (token.length > minGram) { if (token.length > minGram) {
for (let i = minGram; i <= maxGram && i <= token.length; ++i) { for (let i = minGram; i <= maxGram && i <= token.length; ++i) {
ngrams = [...ngrams, token.substr(0, i)] ngrams = [...ngrams, token.substr(0, i)]
} }
@@ -343,7 +345,7 @@ function createEdgeNGrams(str) {
return ngrams return ngrams
}, []).join(" ") }, []).join(" ")
} }
return str return str
} }
@@ -459,7 +461,7 @@ function injectArgs(original_args, new_args) {
for (let i = 0; i < new_args.length; i++) { for (let i = 0; i < new_args.length; i++) {
const new_arg = new_args[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 (!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 (CONSTS.YTDL_ARGS_WITH_VALUES.has(new_arg)) {
if (original_args.includes(new_arg)) { if (original_args.includes(new_arg)) {
const original_index = original_args.indexOf(new_arg); const original_index = original_args.indexOf(new_arg);

View File

@@ -44,7 +44,7 @@
<a *ngIf="postsService.config && postsService.config.Advanced.multi_user_mode && !postsService.isLoggedIn" mat-list-item (click)="sidenav.close()" routerLink='/login'><ng-container i18n="Navigation menu Login Page title">Login</ng-container></a> <a *ngIf="postsService.config && postsService.config.Advanced.multi_user_mode && !postsService.isLoggedIn" mat-list-item (click)="sidenav.close()" routerLink='/login'><ng-container i18n="Navigation menu Login Page title">Login</ng-container></a>
<a *ngIf="postsService.config && allowSubscriptions && postsService.hasPermission('subscriptions')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/subscriptions'><ng-container i18n="Navigation menu Subscriptions Page title">Subscriptions</ng-container></a> <a *ngIf="postsService.config && allowSubscriptions && postsService.hasPermission('subscriptions')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/subscriptions'><ng-container i18n="Navigation menu Subscriptions Page title">Subscriptions</ng-container></a>
<a *ngIf="postsService.config && enableDownloadsManager && postsService.hasPermission('downloads_manager')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/downloads'><ng-container i18n="Navigation menu Downloads Page title">Downloads</ng-container></a> <a *ngIf="postsService.config && enableDownloadsManager && postsService.hasPermission('downloads_manager')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/downloads'><ng-container i18n="Navigation menu Downloads Page title">Downloads</ng-container></a>
<a *ngIf="postsService.config && enableDownloadsManager && postsService.hasPermission('downloads_manager')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/tasks'><ng-container i18n="Navigation menu Tasks Page title">Tasks</ng-container></a> <a *ngIf="postsService.config && postsService.hasPermission('tasks_manager')" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/tasks'><ng-container i18n="Navigation menu Tasks Page title">Tasks</ng-container></a>
<ng-container *ngIf="postsService.config && postsService.hasPermission('settings')"> <ng-container *ngIf="postsService.config && postsService.hasPermission('settings')">
<mat-divider></mat-divider> <mat-divider></mat-divider>
<a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/settings'><ng-container i18n="Settings menu label">Settings</ng-container></a> <a mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/settings'><ng-container i18n="Settings menu label">Settings</ng-container></a>

View File

@@ -14,12 +14,13 @@ export class ManageRoleComponent implements OnInit {
permissions = null; permissions = null;
permissionToLabel = { permissionToLabel = {
'filemanager': 'File manager', 'filemanager': $localize`File manager`,
'settings': 'Settings access', 'settings': $localize`Settings access`,
'subscriptions': 'Subscriptions', 'subscriptions': $localize`Subscriptions`,
'sharing': 'Share files', 'sharing': $localize`Share files`,
'advanced_download': 'Use advanced download mode', 'advanced_download': $localize`Use advanced download mode`,
'downloads_manager': 'Use downloads manager' 'downloads_manager': $localize`Use downloads manager`,
'tasks_manager': $localize`Use tasks manager`,
} }
constructor(public postsService: PostsService, private dialogRef: MatDialogRef<ManageRoleComponent>, constructor(public postsService: PostsService, private dialogRef: MatDialogRef<ManageRoleComponent>,

View File

@@ -95,7 +95,7 @@
</mat-tab-group> </mat-tab-group>
</div> </div>
<div style="position: relative;" *ngIf="usePaginator && selectedIndex > 0"> <div style="position: relative;" *ngIf="paged_data && paged_data.length > 0 && usePaginator && selectedIndex > 0">
<div style="position: absolute; margin-left: 8px; margin-top: 5px; scale: 0.8"> <div style="position: absolute; margin-left: 8px; margin-top: 5px; scale: 0.8">
<mat-form-field> <mat-form-field>
<mat-label><ng-container i18n="File type">File type</ng-container></mat-label> <mat-label><ng-container i18n="File type">File type</ng-container></mat-label>

View File

@@ -4,7 +4,7 @@ import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch'; import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw'; import 'rxjs/add/observable/throw';
import { THEMES_CONFIG } from '../themes'; import { THEMES_CONFIG } from '../themes';
import { Router, CanActivate } from '@angular/router'; import { Router, CanActivate, ActivatedRouteSnapshot } from '@angular/router';
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
@@ -229,12 +229,15 @@ export class PostsService implements CanActivate {
} }
} }
canActivate(route, state): Promise<boolean> { canActivate(route: ActivatedRouteSnapshot, state): Promise<boolean> {
return new Promise(resolve => { const PATH_TO_REQUIRED_PERM = {
resolve(true); settings: 'settings',
}) subscriptions: 'subscriptions',
console.log(route); downloads: 'downloads_manager',
throw new Error('Method not implemented.'); tasks: 'tasks_manager'
}
const required_perm = PATH_TO_REQUIRED_PERM[route.routeConfig.path];
return required_perm ? this.hasPermission(required_perm) : true;
} }
setTheme(theme) { setTheme(theme) {

View File

@@ -645,6 +645,55 @@
</context-group> </context-group>
<note priority="1" from="description">Close</note> <note priority="1" from="description">Close</note>
</trans-unit> </trans-unit>
<trans-unit id="4250042184551786923" datatype="html">
<source>File manager</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
<trans-unit id="8525406024045742391" datatype="html">
<source>Settings access</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">18</context>
</context-group>
</trans-unit>
<trans-unit id="1812379335568847528" datatype="html">
<source>Subscriptions</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">19</context>
</context-group>
</trans-unit>
<trans-unit id="4744991787069301975" datatype="html">
<source>Share files</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">20</context>
</context-group>
</trans-unit>
<trans-unit id="5029464708330583545" datatype="html">
<source>Use advanced download mode</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">21</context>
</context-group>
</trans-unit>
<trans-unit id="4278268519633335280" datatype="html">
<source>Use downloads manager</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">22</context>
</context-group>
</trans-unit>
<trans-unit id="3426988753455920032" datatype="html">
<source>Use tasks manager</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage-role/manage-role.component.ts</context>
<context context-type="linenumber">23</context>
</context-group>
</trans-unit>
<trans-unit id="2bd201aea09e43fbfd3cd15ec0499b6755302329" datatype="html"> <trans-unit id="2bd201aea09e43fbfd3cd15ec0499b6755302329" datatype="html">
<source>Manage user</source> <source>Manage user</source>
<context-group purpose="location"> <context-group purpose="location">

File diff suppressed because it is too large Load Diff