Compare commits

...

10 Commits

Author SHA1 Message Date
Isaac Abadi
2756cfae17 Login component is now a lot prettier 2021-08-01 20:41:36 -06:00
Isaac Abadi
dac5919ffb Updated look of buttons and several home page elements 2021-08-01 20:41:13 -06:00
Isaac Abadi
34245bd339 Updated styling for settings page
Fixed issue where redirects to home occured when reloading the settings page

Fixed errors that occured when loading the settings page
2021-08-01 20:40:29 -06:00
Isaac Abadi
8d6ec819e6 Merge branch 'master' of https://github.com/Tzahi12345/YoutubeDL-Material into cleaner-playlists-and-settings 2021-08-01 17:27:35 -06:00
Isaac Abadi
b03b4d173b Fixed issue where testing the connecting string would fail if local DB was being used
Fixed issue where blacklisting video in with archiving would not work

Cleaned up unused functions in app.js
2021-08-01 14:38:34 -06:00
Tzahi12345
c8f219d5b0 See previous commit, MongoDB is no longer on by default for all installs 2021-08-01 06:24:47 -06:00
Tzahi12345
ec3ab17507 MongoDB is no longer the default in config, this will just be set through the docker-compose.yml 2021-08-01 06:23:27 -06:00
Tzahi12345
5124e3b333 Update issue templates 2021-07-29 21:35:33 -06:00
Isaac Abadi
d09b244bc2 Fixed bug where unsubscribing from a channel would clear the entire files table
Fixed issue where yt-dlp did not work with subscriptions
2021-07-28 19:44:05 -06:00
Isaac Abadi
73b9cf7893 Settings is now a route instead of a dialog 2021-07-26 18:18:07 -07:00
25 changed files with 176 additions and 154 deletions

View File

@@ -28,4 +28,4 @@ If applicable, add screenshots to help explain your problem.
- Docker tag: <tag> (optional)
**Additional context**
Add any other context about the problem here.
Add any other context about the problem here. For example, a YouTube link.

View File

@@ -18,19 +18,15 @@ var mergeFiles = require('merge-files');
const low = require('lowdb')
var ProgressBar = require('progress');
const NodeID3 = require('node-id3')
const downloader = require('youtube-dl/lib/downloader')
const fetch = require('node-fetch');
var URL = require('url').URL;
const shortid = require('shortid')
const url_api = require('url');
const CONSTS = require('./consts')
const { spawn } = require('child_process')
const read_last_lines = require('read-last-lines');
var ps = require('ps-node');
// needed if bin/details somehow gets deleted
const DETAILS_BIN_PATH = 'node_modules/youtube-dl/bin/details'
if (!fs.existsSync(DETAILS_BIN_PATH)) fs.writeJSONSync(DETAILS_BIN_PATH, {"version":"2000.06.06","path":"node_modules\\youtube-dl\\bin\\youtube-dl.exe","exec":"youtube-dl.exe","downloader":"youtube-dl"})
if (!fs.existsSync(CONSTS.DETAILS_BIN_PATH)) fs.writeJSONSync(CONSTS.DETAILS_BIN_PATH, {"version":"2000.06.06","path":"node_modules\\youtube-dl\\bin\\youtube-dl.exe","exec":"youtube-dl.exe","downloader":"youtube-dl"})
var youtubedl = require('youtube-dl');
@@ -754,67 +750,6 @@ function generateEnvVarConfigItem(key) {
return {key: key, value: process['env'][key]};
}
function getFileSizeMp3(name)
{
var jsonPath = audioFolderPath+name+".mp3.info.json";
if (fs.existsSync(jsonPath))
var obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
else
var obj = 0;
return obj.filesize;
}
function getFileSizeMp4(name)
{
var jsonPath = videoFolderPath+name+".info.json";
var filesize = 0;
if (fs.existsSync(jsonPath))
{
var obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
var format = obj.format.substring(0,3);
for (i = 0; i < obj.formats.length; i++)
{
if (obj.formats[i].format_id == format)
{
filesize = obj.formats[i].filesize;
}
}
}
return filesize;
}
function getAmountDownloadedMp3(name)
{
var partPath = audioFolderPath+name+".mp3.part";
if (fs.existsSync(partPath))
{
const stats = fs.statSync(partPath);
const fileSizeInBytes = stats.size;
return fileSizeInBytes;
}
else
return 0;
}
function getAmountDownloadedMp4(name)
{
var format = getVideoFormatID(name);
var partPath = videoFolderPath+name+".f"+format+".mp4.part";
if (fs.existsSync(partPath))
{
const stats = fs.statSync(partPath);
const fileSizeInBytes = stats.size;
return fileSizeInBytes;
}
else
return 0;
}
function getVideoFormatID(name)
{
var jsonPath = videoFolderPath+name+".info.json";
@@ -1180,7 +1115,7 @@ async function generateArgs(url, type, options) {
downloadConfig = downloadConfig.concat(globalArgs.split(',,'));
}
const default_downloader = getCurrentDownloader() || config_api.getConfigItem('ytdl_default_downloader');
const default_downloader = utils.getCurrentDownloader() || config_api.getConfigItem('ytdl_default_downloader');
if (default_downloader === 'yt-dlp') {
downloadConfig.push('--no-clean-infojson');
}
@@ -1284,17 +1219,6 @@ async function cropFile(file_path, start, end, ext) {
});
}
// archive helper functions
async function writeToBlacklist(type, line) {
let blacklistPath = path.join(archivePath, (type === 'audio') ? 'blacklist_audio.txt' : 'blacklist_video.txt');
// adds newline to the beginning of the line
line.replace('\n', '');
line.replace('\r', '');
line = '\n' + line;
await fs.appendFile(blacklistPath, line);
}
// download management functions
async function updateDownloads() {
@@ -1361,17 +1285,17 @@ async function autoUpdateYoutubeDL() {
const default_downloader = config_api.getConfigItem('ytdl_default_downloader');
const tags_url = download_sources[default_downloader]['tags_url'];
// get current version
let current_app_details_exists = fs.existsSync(DETAILS_BIN_PATH);
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 '${DETAILS_BIN_PATH}'. Generating file...`);
fs.writeJSONSync(DETAILS_BIN_PATH, {"version":"2020.00.00", "downloader": default_downloader});
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});
}
let current_app_details = JSON.parse(fs.readFileSync(DETAILS_BIN_PATH));
let current_app_details = JSON.parse(fs.readFileSync(CONSTS.DETAILS_BIN_PATH));
let current_version = current_app_details['version'];
let current_downloader = current_app_details['downloader'];
let stored_binary_path = current_app_details['path'];
if (!stored_binary_path || typeof stored_binary_path !== 'string') {
// logger.info(`INFO: Failed to get youtube-dl binary path at location: ${DETAILS_BIN_PATH}, attempting to guess actual path...`);
// logger.info(`INFO: Failed to get youtube-dl binary path at location: ${CONSTS.DETAILS_BIN_PATH}, attempting to guess actual path...`);
const guessed_base_path = 'node_modules/youtube-dl/bin/';
const guessed_file_path = guessed_base_path + 'youtube-dl' + (is_windows ? '.exe' : '');
if (fs.existsSync(guessed_file_path)) {
@@ -1454,15 +1378,10 @@ async function downloadLatestYoutubeDLPBinary(new_version) {
}
function updateDetailsJSON(new_version, downloader) {
const details_json = fs.readJSONSync(DETAILS_BIN_PATH);
const details_json = fs.readJSONSync(CONSTS.DETAILS_BIN_PATH);
if (new_version) details_json['version'] = new_version;
details_json['downloader'] = downloader;
fs.writeJSONSync(DETAILS_BIN_PATH, details_json);
}
function getCurrentDownloader() {
const details_json = fs.readJSONSync(DETAILS_BIN_PATH);
return details_json['downloader'];
fs.writeJSONSync(CONSTS.DETAILS_BIN_PATH, details_json);
}
async function checkExistsWithTimeout(filePath, timeout) {

View File

@@ -55,7 +55,7 @@
}
},
"Database": {
"use_local_db": false,
"use_local_db": true,
"mongodb_connection_string": "mongodb://127.0.0.1:27017/?compressors=zlib"
},
"Advanced": {

View File

@@ -232,7 +232,7 @@ DEFAULT_CONFIG = {
}
},
"Database": {
"use_local_db": false,
"use_local_db": true,
"mongodb_connection_string": "mongodb://127.0.0.1:27017/?compressors=zlib"
},
"Advanced": {

View File

@@ -207,8 +207,11 @@ AVAILABLE_PERMISSIONS = [
'downloads_manager'
];
const DETAILS_BIN_PATH = 'node_modules/youtube-dl/bin/details'
module.exports = {
CONFIG_ITEMS: CONFIG_ITEMS,
AVAILABLE_PERMISSIONS: AVAILABLE_PERMISSIONS,
CURRENT_VERSION: 'v4.2'
CURRENT_VERSION: 'v4.2',
DETAILS_BIN_PATH: DETAILS_BIN_PATH
}

View File

@@ -75,7 +75,7 @@ exports.initialize = (input_db, input_users_db, input_logger) => {
}
exports.connectToDB = async (retries = 5, no_fallback = false, custom_connection_string = null) => {
if (using_local_db) return;
if (using_local_db && !custom_connection_string) return;
const success = await exports._connectToDB(custom_connection_string);
if (success) return true;
@@ -256,6 +256,9 @@ function generateFileObject2(file_path, type) {
var jsonobj = utils.getJSON(file_path, type);
if (!jsonobj) {
return null;
} else if (!jsonobj['_filename']) {
logger.error(`Failed to get filename from info JSON! File ${jsonobj['title']} could not be added.`);
return null;
}
const ext = (type === 'audio') ? '.mp3' : '.mp4'
const true_file_path = utils.getTrueFileName(jsonobj['_filename'], type);
@@ -781,26 +784,26 @@ exports.removeRecord = async (table, filter_obj) => {
return !!(output['result']['ok']);
}
exports.removeAllRecords = async (table = null) => {
exports.removeAllRecords = async (table = null, filter_obj = null) => {
// local db override
const tables_to_remove = table ? [table] : tables_list;
logger.debug(`Removing all records from: ${tables_to_remove} with filter: ${JSON.stringify(filter_obj)}`)
if (using_local_db) {
logger.debug(`Removing all records from: ${tables_to_remove}`)
for (let i = 0; i < tables_to_remove.length; i++) {
const table_to_remove = tables_to_remove[i];
local_db.assign({[table_to_remove]: []}).write();
logger.debug(`Removed all records from ${table_to_remove}`);
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}`);
}
return true;
}
let success = true;
logger.debug(`Removing all records from: ${tables_to_remove}`)
for (let i = 0; i < tables_to_remove.length; i++) {
const table_to_remove = tables_to_remove[i];
const output = await database.collection(table_to_remove).deleteMany({});
logger.debug(`Removed all records from ${table_to_remove}`);
const output = await database.collection(table_to_remove).deleteMany(filter_obj ? filter_obj : {});
logger.debug(`Successfully removed records from ${table_to_remove}`);
success &= !!(output['result']['ok']);
}
return success;
@@ -988,6 +991,8 @@ exports.transferDB = async (local_to_remote) => {
config_api.setConfigItem('ytdl_use_local_db', using_local_db);
logger.debug('Transfer finished!');
return success;
}
@@ -1013,4 +1018,16 @@ const applyFilterLocalDB = (db_path, filter_obj, operation) => {
return filtered;
});
return return_val;
}
}
// archive helper functions
async function writeToBlacklist(type, line) {
const archivePath = path.join(__dirname, 'appdata', 'archives');
let blacklistPath = path.join(archivePath, (type === 'audio') ? 'blacklist_audio.txt' : 'blacklist_video.txt');
// adds newline to the beginning of the line
line.replace('\n', '');
line.replace('\r', '');
line = '\n' + line;
await fs.appendFile(blacklistPath, line);
}

View File

@@ -413,6 +413,11 @@ async function generateArgsForSubscription(sub, user_uid, redownload = false, de
downloadConfig.push('--write-thumbnail');
}
const default_downloader = utils.getCurrentDownloader() || config_api.getConfigItem('ytdl_default_downloader');
if (default_downloader === 'yt-dlp') {
downloadConfig.push('--no-clean-infojson');
}
return downloadConfig;
}

View File

@@ -1,6 +1,7 @@
const fs = require('fs-extra')
const path = require('path')
const config_api = require('./config');
const CONSTS = require('./consts')
const archiver = require('archiver');
const is_windows = process.platform === 'win32';
@@ -315,6 +316,11 @@ function addUIDsToCategory(category, files) {
return files_that_match;
}
function getCurrentDownloader() {
const details_json = fs.readJSONSync(CONSTS.DETAILS_BIN_PATH);
return details_json['downloader'];
}
async function recFindByExt(base,ext,files,result)
{
files = files || (await fs.readdir(base))
@@ -390,6 +396,7 @@ module.exports = {
durationStringToNumber: durationStringToNumber,
getMatchingCategoryFiles: getMatchingCategoryFiles,
addUIDsToCategory: addUIDsToCategory,
getCurrentDownloader: getCurrentDownloader,
recFindByExt: recFindByExt,
removeFileExtension: removeFileExtension,
wait: wait,

View File

@@ -7,12 +7,14 @@ import { SubscriptionComponent } from './subscription/subscription/subscription.
import { PostsService } from './posts.services';
import { LoginComponent } from './components/login/login.component';
import { DownloadsComponent } from './components/downloads/downloads.component';
import { SettingsComponent } from './settings/settings.component';
const routes: Routes = [
{ path: 'home', component: MainComponent, canActivate: [PostsService] },
{ path: 'player', component: PlayerComponent, canActivate: [PostsService]},
{ path: 'subscriptions', component: SubscriptionsComponent, canActivate: [PostsService] },
{ path: 'subscription', component: SubscriptionComponent, canActivate: [PostsService] },
{ path: 'settings', component: SettingsComponent, canActivate: [PostsService] },
{ path: 'login', component: LoginComponent },
{ path: 'downloads', component: DownloadsComponent },
{ path: '', redirectTo: '/home', pathMatch: 'full' }

View File

@@ -23,10 +23,10 @@
<span i18n="Dark mode toggle label">Dark</span>
<mat-slide-toggle class="theme-slide-toggle" [checked]="postsService.theme.key === 'dark'"></mat-slide-toggle>
</button>
<button *ngIf="postsService.config && (!postsService.config.Advanced.multi_user_mode || (postsService.isLoggedIn && postsService.permissions.includes('settings')))" (click)="openSettingsDialog()" mat-menu-item>
<!-- <button *ngIf="postsService.config && (!postsService.config.Advanced.multi_user_mode || (postsService.isLoggedIn && postsService.permissions.includes('settings')))" (click)="openSettingsDialog()" mat-menu-item>
<mat-icon>settings</mat-icon>
<span i18n="Settings menu label">Settings</span>
</button>
</button> -->
<button (click)="openAboutDialog()" mat-menu-item>
<mat-icon>info</mat-icon>
<span i18n="About menu label">About</span>
@@ -42,10 +42,14 @@
<mat-nav-list>
<a *ngIf="postsService.config && (!postsService.config.Advanced.multi_user_mode || postsService.isLoggedIn)" mat-list-item (click)="postsService.sidepanel_mode === 'over' ? sidenav.close() : null" routerLink='/home'><ng-container i18n="Navigation menu Home Page title">Home</ng-container></a>
<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.config.Advanced.multi_user_mode || (postsService.isLoggedIn && postsService.permissions.includes('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.config.Advanced.multi_user_mode || (postsService.isLoggedIn && postsService.permissions.includes('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>
<ng-container *ngIf="postsService.config && allowSubscriptions && postsService.subscriptions && (!postsService.config.Advanced.multi_user_mode || (postsService.isLoggedIn && postsService.permissions.includes('subscriptions')))">
<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>
<ng-container *ngIf="postsService.config && postsService.hasPermission('settings')">
<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>
</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>
</ng-container>
</mat-nav-list>

View File

@@ -1,9 +1,6 @@
import { Component, OnInit, ElementRef, ViewChild, HostBinding, AfterViewInit } from '@angular/core';
import {MatDialogRef} from '@angular/material/dialog';
import {PostsService} from './posts.services';
import {FileCardComponent} from './file-card/file-card.component';
import { Observable } from 'rxjs/Observable';
import {FormControl, Validators} from '@angular/forms';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import { MatDialog } from '@angular/material/dialog';
import { MatSidenav } from '@angular/material/sidenav';
import { MatSnackBar } from '@angular/material/snack-bar';
@@ -16,7 +13,6 @@ import 'rxjs/add/operator/filter'
import 'rxjs/add/operator/debounceTime'
import 'rxjs/add/operator/do'
import 'rxjs/add/operator/switch'
import { YoutubeSearchService, Result } from './youtube-search.service';
import { Router, NavigationStart, NavigationEnd } from '@angular/router';
import { OverlayContainer } from '@angular/cdk/overlay';
import { THEMES_CONFIG } from '../themes';
@@ -28,7 +24,11 @@ import { SetDefaultAdminDialogComponent } from './dialogs/set-default-admin-dial
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
styleUrls: ['./app.component.css'],
providers: [{
provide: MatDialogRef,
useValue: {}
}]
})
export class AppComponent implements OnInit, AfterViewInit {

View File

@@ -1,5 +1,5 @@
<mat-card class="login-card">
<mat-tab-group [(selectedIndex)]="selectedTabIndex">
<mat-tab-group style="margin-bottom: 20px" [(selectedIndex)]="selectedTabIndex">
<mat-tab label="Login">
<div style="margin-top: 10px;">
<mat-form-field>
@@ -11,9 +11,6 @@
<input [(ngModel)]="loginPasswordInput" (keyup.enter)="login()" type="password" matInput placeholder="Password">
</mat-form-field>
</div>
<div style="margin-bottom: 10px; margin-top: 10px;">
<button [disabled]="loggingIn" color="primary" (click)="login()" mat-raised-button><ng-container i18n="Login">Login</ng-container></button>
</div>
</mat-tab>
<mat-tab *ngIf="registrationEnabled" label="Register">
<div style="margin-top: 10px;">
@@ -31,9 +28,14 @@
<input [(ngModel)]="registrationPasswordConfirmationInput" type="password" matInput placeholder="Confirm Password">
</mat-form-field>
</div>
<div style="margin-bottom: 10px; margin-top: 10px;">
<button [disabled]="registering" color="primary" (click)="register()" mat-raised-button><ng-container i18n="Register">Register</ng-container></button>
</div>
</mat-tab>
</mat-tab-group>
<div *ngIf="selectedTabIndex === 0" class="login-button-div">
<button [disabled]="loggingIn" color="primary" (click)="login()" mat-raised-button><ng-container i18n="Login">Login</ng-container></button>
<mat-progress-bar *ngIf="loggingIn" class="login-progress-bar" mode="indeterminate"></mat-progress-bar>
</div>
<div *ngIf="selectedTabIndex === 1" class="login-button-div">
<button [disabled]="registering" color="primary" (click)="register()" mat-raised-button><ng-container i18n="Register">Register</ng-container></button>
<mat-progress-bar *ngIf="registering" class="login-progress-bar" mode="indeterminate"></mat-progress-bar>
</div>
</mat-card>

View File

@@ -1,6 +1,33 @@
.login-card {
max-width: 600px;
max-width: 400px;
width: 80%;
margin: 0 auto;
margin-top: 20px;
padding-top: 8px;
}
.login-div {
height: calc(100% - 170px);
overflow-y: auto;
}
.login-button-div {
margin-bottom: 10px;
margin-top: 10px;
margin-left: -16px;
margin-right: -16px;
bottom: 0px;
width: 100%;
position: absolute;
}
.login-button-div > button {
width: 100%;
border-radius: 0px 0px 4px 4px !important;
}
.login-progress-bar {
position: absolute;
bottom: 0px;
border-radius: 0px 0px 4px 4px;
}

View File

@@ -1,4 +1,4 @@
<div style="height: 275px;">
<div style="height: 100%;">
<div *ngIf="logs_loading" style="z-index: 999; position: absolute; top: 40%; left: 50%">
<mat-spinner [diameter]="32"></mat-spinner>
</div>
@@ -10,7 +10,7 @@
</cdk-virtual-scroll-viewport>-->
<!-- Non-virtual mode (slow, bug-free) -->
<div style="height: 274px; overflow-y: auto">
<div style="height: 100%; overflow-y: auto">
<div *ngFor="let log of logs; let i = index" class="example-item">
<span [ngStyle]="{'color':log.color}">{{log.text}}</span>
</div>

View File

@@ -1,7 +1,7 @@
<div *ngIf="dataSource; else loading">
<div style="padding: 15px">
<div class="row">
<div class="table table-responsive px-5 pb-4 pt-2">
<div class="table table-responsive pb-4 pt-2">
<div class="example-header">
<mat-form-field>
<input matInput (keyup)="applyFilter($event.target.value)" placeholder="Search" i18n-placeholder="search field description">

View File

@@ -1,5 +1,4 @@
.edit-role {
position: relative;
top: -50px;
left: 35px;
}

View File

@@ -28,7 +28,7 @@
</div>
</div>
<div>
<div class="container">
<div class="container" style="margin-bottom: 16px">
<div class="row justify-content-center">
<ng-container *ngIf="normal_files_received && paged_data">
<div *ngFor="let file of paged_data; let i = index" class="mb-2 mt-2 d-flex justify-content-center" [ngClass]="[ postsService.card_size === 'small' ? 'col-2 small-col' : '', postsService.card_size === 'medium' ? 'col-6 col-lg-4 medium-col' : '', postsService.card_size === 'large' ? 'col-12 large-col' : '' ]">

View File

@@ -133,12 +133,16 @@ mat-form-field.mat-form-field {
top: -5px;
}
.border-radius-both {
border-radius: 16px;
}
.no-border-radius-bottom {
border-radius: 4px 4px 0px 0px;
border-radius: 16px 16px 0px 0px;
}
.no-border-radius-top {
border-radius: 0px 0px 4px 4px;
border-radius: 0px 0px 16px 16px;
}
@media (max-width: 576px) {

View File

@@ -1,6 +1,6 @@
<br/>
<div class="big demo-basic">
<mat-card id="card" style="margin-right: 20px; margin-left: 20px;" [ngClass]="(allowAdvancedDownload) ? 'no-border-radius-bottom' : null">
<mat-card id="card" style="margin-right: 20px; margin-left: 20px;" [ngClass]="(allowAdvancedDownload) ? 'no-border-radius-bottom' : 'border-radius-both'">
<mat-card-content style="padding: 0px 8px 0px 8px;">
<div style="position: relative; margin-right: 15px;">
<form class="example-form">

View File

@@ -230,7 +230,7 @@ export class MainComponent implements OnInit {
async loadConfig() {
// loading config
this.fileManagerEnabled = this.postsService.config['Extra']['file_manager_enabled']
&& (!this.postsService.isLoggedIn || this.postsService.permissions.includes('filemanager'));
&& this.postsService.hasPermission('filemanager');
this.downloadOnlyMode = this.postsService.config['Extra']['download_only_mode'];
this.allowMultiDownloadMode = this.postsService.config['Extra']['allow_multi_download_mode'];
this.audioFolderPath = this.postsService.config['Downloader']['path-audio'];
@@ -242,7 +242,7 @@ export class MainComponent implements OnInit {
this.youtubeAPIKey = this.youtubeSearchEnabled ? this.postsService.config['API']['youtube_API_key'] : null;
this.allowQualitySelect = this.postsService.config['Extra']['allow_quality_select'];
this.allowAdvancedDownload = this.postsService.config['Advanced']['allow_advanced_download']
&& (!this.postsService.isLoggedIn || this.postsService.permissions.includes('advanced_download'));
&& this.postsService.hasPermission('advanced_download');
this.useDefaultDownloadingAgent = this.postsService.config['Advanced']['use_default_downloading_agent'];
this.customDownloadingAgent = this.postsService.config['Advanced']['custom_downloading_agent'];

View File

@@ -511,6 +511,12 @@ export class PostsService implements CanActivate {
this.resetHttpParams();
}
hasPermission(permission) {
// assume not logged in users never have permission
if (this.config.Advanced.multi_user_mode && !this.isLoggedIn) return false;
return this.config.Advanced.multi_user_mode ? this.permissions.includes(permission) : true;
}
// user methods
register(username, password) {
const call = this.http.post(this.path + 'auth/register', {userid: username,

View File

@@ -1,13 +1,12 @@
<h4 i18n="Settings title" mat-dialog-title>Settings</h4>
<h4 class="settings-title" i18n="Settings title">Settings</h4>
<!-- <ng-container i18n="Allow subscriptions setting"></ng-container> -->
<mat-dialog-content>
<!-- Language
<div style="margin-bottom: 10px;">
</div> -->
<mat-tab-group>
<mat-tab-group style="height: 76vh" mat-align-tabs="center">
<!-- Server -->
<mat-tab label="Main" i18n-label="Main settings label">
<ng-template matTabContent style="padding: 15px;">
@@ -391,7 +390,7 @@
<app-updater></app-updater>
</div>
<mat-divider></mat-divider>
<div *ngIf="new_config" class="container">
<div *ngIf="new_config" class="container-fluid">
<div class="row">
<div class="col-12 mt-4">
<button (click)="restartServer()" mat-stroked-button color="warn"><ng-container i18n="Restart server button">Restart server</ng-container></button>
@@ -401,8 +400,7 @@
</ng-template>
</mat-tab>
<mat-tab *ngIf="postsService.config && postsService.config.Advanced.multi_user_mode" label="Users" i18n-label="Users settings label">
<div style="margin-left: 48px; margin-top: 24px; margin-bottom: -25px;">
<div *ngIf="new_config" style="margin-top: 24px; margin-bottom: -25px;">
<div>
<mat-checkbox color="accent" [(ngModel)]="new_config['Users']['allow_registration']"><ng-container i18n="Allow registration setting">Allow user registration</ng-container></mat-checkbox>
</div>
@@ -446,25 +444,23 @@
</div>
<mat-divider></mat-divider>
</div>
<app-modify-users></app-modify-users>
<app-modify-users *ngIf="new_config"></app-modify-users>
</mat-tab>
<mat-tab *ngIf="postsService.config" label="Logs" i18n-label="Logs settings label">
<ng-template matTabContent>
<div style="margin-left: 48px; margin-top: 24px; height: 340px">
<div style="margin-top: 15px; height: 84%;">
<app-logs-viewer></app-logs-viewer>
</div>
</ng-template>
</mat-tab>
</mat-tab-group>
</mat-dialog-content>
<mat-dialog-actions>
<div style="margin-bottom: 10px;">
<button color="accent" (click)="saveSettings()" [disabled]="settingsSame()" mat-raised-button><mat-icon>done</mat-icon>&nbsp;&nbsp;
<ng-container i18n="Settings save button">Save</ng-container>
</button>
<button mat-flat-button [mat-dialog-close]="false"><mat-icon>cancel</mat-icon>&nbsp;&nbsp;
<span i18n="Settings cancel and close button">{settingsAreTheSame + "", select, true {Close} false {Cancel} other {otha}}</span>
</button>
</div>
</mat-dialog-actions>
<div class="action-buttons">
<button style="margin-left: 10px; height: 37.3px" color="accent" (click)="saveSettings()" [disabled]="settingsSame()" mat-raised-button><mat-icon>done</mat-icon>&nbsp;&nbsp;
<ng-container i18n="Settings save button">Save</ng-container>
</button>
<button style="margin-left: 10px;" mat-flat-button (click)="cancelSettings()" [disabled]="settingsSame()"><mat-icon>cancel</mat-icon>&nbsp;&nbsp;
<span i18n="Settings cancel button">Cancel</span>
</button>
</div>

View File

@@ -2,6 +2,15 @@
margin-bottom: 20px;
}
.settings-title {
text-align: center;
margin-top: 15px;
}
::ng-deep .mat-tab-body {
margin-left: 15px;
}
.ext-divider {
margin-bottom: 14px;
}
@@ -90,4 +99,9 @@
.transfer-db-div {
margin-bottom: 10px;
}
.action-buttons {
position: absolute;
bottom: 15px;
}

View File

@@ -51,8 +51,17 @@ export class SettingsComponent implements OnInit {
private dialog: MatDialog) { }
ngOnInit() {
this.getConfig();
this.getDBInfo();
if (this.postsService.initialized) {
this.getConfig();
this.getDBInfo();
} else {
this.postsService.service_initialized.subscribe(init => {
if (init) {
this.getConfig();
this.getDBInfo();
}
});
}
this.generated_bookmarklet_code = this.sanitizer.bypassSecurityTrustUrl(this.generateBookmarkletCode());
@@ -85,6 +94,10 @@ export class SettingsComponent implements OnInit {
})
}
cancelSettings() {
this.new_config = JSON.parse(JSON.stringify(this.initial_config));
}
dropCategory(event: CdkDragDrop<string[]>) {
moveItemInArray(this.postsService.categories, event.previousIndex, event.currentIndex);
this.postsService.updateCategories(this.postsService.categories).subscribe(res => {

View File

@@ -42,6 +42,10 @@ $dark-theme: mat-dark-theme($dark-primary, $dark-accent, $dark-warn);
@include angular-material-theme($dark-theme);
}
.mat-stroked-button, .mat-raised-button, .mat-flat-button {
border-radius: 24px !important
}
// Light theme
$light-primary: mat-palette($mat-grey, 200, 500, 300);
$light-accent: mat-palette($mat-brown, 200);
@@ -50,7 +54,7 @@ $light-warn: mat-palette($mat-deep-orange, 200);
$light-theme: mat-light-theme($light-primary, $light-accent, $light-warn);
.light-theme {
@include angular-material-theme($light-theme)
@include angular-material-theme($light-theme);
}
.no-outline {