mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-23 13:10:59 +03:00
Subscription file cards are now replaced with unified file cards
GetAllFiles can now filter by sub_id Improved API models and added request body docs for GetAllFiles
This commit is contained in:
@@ -97,6 +97,11 @@ paths:
|
|||||||
summary: Get all files
|
summary: Get all files
|
||||||
description: Gets all files and playlists stored in the db
|
description: Gets all files and playlists stored in the db
|
||||||
operationId: get-getAllFiles
|
operationId: get-getAllFiles
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GetAllFilesRequest'
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: OK
|
description: OK
|
||||||
@@ -1724,6 +1729,41 @@ components:
|
|||||||
description: All video playlists
|
description: All video playlists
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/Playlist'
|
$ref: '#/components/schemas/Playlist'
|
||||||
|
GetAllFilesRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
sort:
|
||||||
|
$ref: '#/components/schemas/Sort'
|
||||||
|
range:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: number
|
||||||
|
description: Two elements allowed, start index and end index
|
||||||
|
minItems: 2
|
||||||
|
maxItems: 2
|
||||||
|
text_search:
|
||||||
|
type: string
|
||||||
|
description: Filter files by title
|
||||||
|
file_type_filter:
|
||||||
|
$ref: '#/components/schemas/FileTypeFilter'
|
||||||
|
sub_id:
|
||||||
|
type: string
|
||||||
|
description: Include if you want to filter by subscription
|
||||||
|
Sort:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
by:
|
||||||
|
type: string
|
||||||
|
description: Property to sort by
|
||||||
|
order:
|
||||||
|
type: number
|
||||||
|
description: 1 for ascending, -1 for descending
|
||||||
|
FileTypeFilter:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- audio_only
|
||||||
|
- video_only
|
||||||
|
- both
|
||||||
GetAllFilesResponse:
|
GetAllFilesResponse:
|
||||||
required:
|
required:
|
||||||
- files
|
- files
|
||||||
|
|||||||
@@ -912,11 +912,11 @@ app.post('/api/getFile', optionalJwt, async function (req, res) {
|
|||||||
app.post('/api/getAllFiles', optionalJwt, async function (req, res) {
|
app.post('/api/getAllFiles', optionalJwt, async function (req, res) {
|
||||||
// these are returned
|
// these are returned
|
||||||
let files = null;
|
let files = null;
|
||||||
let playlists = null;
|
const sort = req.body.sort;
|
||||||
let sort = req.body.sort;
|
const range = req.body.range;
|
||||||
let range = req.body.range;
|
const text_search = req.body.text_search;
|
||||||
let text_search = req.body.text_search;
|
const file_type_filter = req.body.file_type_filter;
|
||||||
let file_type_filter = req.body.file_type_filter;
|
const sub_id = req.body.sub_id;
|
||||||
const uuid = req.isAuthenticated() ? req.user.uid : null;
|
const uuid = req.isAuthenticated() ? req.user.uid : null;
|
||||||
|
|
||||||
const filter_obj = {user_uid: uuid};
|
const filter_obj = {user_uid: uuid};
|
||||||
@@ -929,6 +929,10 @@ app.post('/api/getAllFiles', optionalJwt, async function (req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sub_id) {
|
||||||
|
filter_obj['sub_id'] = sub_id;
|
||||||
|
}
|
||||||
|
|
||||||
if (file_type_filter === 'audio_only') filter_obj['isAudio'] = true;
|
if (file_type_filter === 'audio_only') filter_obj['isAudio'] = true;
|
||||||
else if (file_type_filter === 'video_only') filter_obj['isAudio'] = false;
|
else if (file_type_filter === 'video_only') filter_obj['isAudio'] = false;
|
||||||
|
|
||||||
|
|||||||
@@ -40,11 +40,13 @@ export type { DownloadTwitchChatByVODIDRequest } from './models/DownloadTwitchCh
|
|||||||
export type { DownloadTwitchChatByVODIDResponse } from './models/DownloadTwitchChatByVODIDResponse';
|
export type { DownloadTwitchChatByVODIDResponse } from './models/DownloadTwitchChatByVODIDResponse';
|
||||||
export type { DownloadVideosForSubscriptionRequest } from './models/DownloadVideosForSubscriptionRequest';
|
export type { DownloadVideosForSubscriptionRequest } from './models/DownloadVideosForSubscriptionRequest';
|
||||||
export { FileType } from './models/FileType';
|
export { FileType } from './models/FileType';
|
||||||
|
export { FileTypeFilter } from './models/FileTypeFilter';
|
||||||
export type { GenerateArgsResponse } from './models/GenerateArgsResponse';
|
export type { GenerateArgsResponse } from './models/GenerateArgsResponse';
|
||||||
export type { GenerateNewApiKeyResponse } from './models/GenerateNewApiKeyResponse';
|
export type { GenerateNewApiKeyResponse } from './models/GenerateNewApiKeyResponse';
|
||||||
export type { GetAllCategoriesResponse } from './models/GetAllCategoriesResponse';
|
export type { GetAllCategoriesResponse } from './models/GetAllCategoriesResponse';
|
||||||
export type { GetAllDownloadsRequest } from './models/GetAllDownloadsRequest';
|
export type { GetAllDownloadsRequest } from './models/GetAllDownloadsRequest';
|
||||||
export type { GetAllDownloadsResponse } from './models/GetAllDownloadsResponse';
|
export type { GetAllDownloadsResponse } from './models/GetAllDownloadsResponse';
|
||||||
|
export type { GetAllFilesRequest } from './models/GetAllFilesRequest';
|
||||||
export type { GetAllFilesResponse } from './models/GetAllFilesResponse';
|
export type { GetAllFilesResponse } from './models/GetAllFilesResponse';
|
||||||
export type { GetAllSubscriptionsResponse } from './models/GetAllSubscriptionsResponse';
|
export type { GetAllSubscriptionsResponse } from './models/GetAllSubscriptionsResponse';
|
||||||
export type { GetAllTasksResponse } from './models/GetAllTasksResponse';
|
export type { GetAllTasksResponse } from './models/GetAllTasksResponse';
|
||||||
@@ -82,6 +84,7 @@ export type { RestoreDBBackupRequest } from './models/RestoreDBBackupRequest';
|
|||||||
export { Schedule } from './models/Schedule';
|
export { Schedule } from './models/Schedule';
|
||||||
export type { SetConfigRequest } from './models/SetConfigRequest';
|
export type { SetConfigRequest } from './models/SetConfigRequest';
|
||||||
export type { SharingToggle } from './models/SharingToggle';
|
export type { SharingToggle } from './models/SharingToggle';
|
||||||
|
export type { Sort } from './models/Sort';
|
||||||
export type { SubscribeRequest } from './models/SubscribeRequest';
|
export type { SubscribeRequest } from './models/SubscribeRequest';
|
||||||
export type { SubscribeResponse } from './models/SubscribeResponse';
|
export type { SubscribeResponse } from './models/SubscribeResponse';
|
||||||
export type { Subscription } from './models/Subscription';
|
export type { Subscription } from './models/Subscription';
|
||||||
|
|||||||
9
src/api-types/models/FileTypeFilter.ts
Normal file
9
src/api-types/models/FileTypeFilter.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
export enum FileTypeFilter {
|
||||||
|
AUDIO_ONLY = 'audio_only',
|
||||||
|
VIDEO_ONLY = 'video_only',
|
||||||
|
BOTH = 'both',
|
||||||
|
}
|
||||||
20
src/api-types/models/GetAllFilesRequest.ts
Normal file
20
src/api-types/models/GetAllFilesRequest.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
import type { FileTypeFilter } from './FileTypeFilter';
|
||||||
|
import type { Sort } from './Sort';
|
||||||
|
|
||||||
|
export type GetAllFilesRequest = {
|
||||||
|
sort?: Sort;
|
||||||
|
range?: Array<number>;
|
||||||
|
/**
|
||||||
|
* Filter files by title
|
||||||
|
*/
|
||||||
|
text_search?: string;
|
||||||
|
file_type_filter?: FileTypeFilter;
|
||||||
|
/**
|
||||||
|
* Include if you want to filter by subscription
|
||||||
|
*/
|
||||||
|
sub_id?: string;
|
||||||
|
};
|
||||||
14
src/api-types/models/Sort.ts
Normal file
14
src/api-types/models/Sort.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
export type Sort = {
|
||||||
|
/**
|
||||||
|
* Property to sort by
|
||||||
|
*/
|
||||||
|
by?: string;
|
||||||
|
/**
|
||||||
|
* 1 for ascending, -1 for descending
|
||||||
|
*/
|
||||||
|
order?: number;
|
||||||
|
};
|
||||||
@@ -49,7 +49,6 @@ import { CreatePlaylistComponent } from './create-playlist/create-playlist.compo
|
|||||||
import { SubscriptionsComponent } from './subscriptions/subscriptions.component';
|
import { SubscriptionsComponent } from './subscriptions/subscriptions.component';
|
||||||
import { SubscribeDialogComponent } from './dialogs/subscribe-dialog/subscribe-dialog.component';
|
import { SubscribeDialogComponent } from './dialogs/subscribe-dialog/subscribe-dialog.component';
|
||||||
import { SubscriptionComponent } from './subscription//subscription/subscription.component';
|
import { SubscriptionComponent } from './subscription//subscription/subscription.component';
|
||||||
import { SubscriptionFileCardComponent } from './subscription/subscription-file-card/subscription-file-card.component';
|
|
||||||
import { SubscriptionInfoDialogComponent } from './dialogs/subscription-info-dialog/subscription-info-dialog.component';
|
import { SubscriptionInfoDialogComponent } from './dialogs/subscription-info-dialog/subscription-info-dialog.component';
|
||||||
import { SettingsComponent } from './settings/settings.component';
|
import { SettingsComponent } from './settings/settings.component';
|
||||||
import { MatChipsModule } from '@angular/material/chips';
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
@@ -102,7 +101,6 @@ registerLocaleData(es, 'es');
|
|||||||
SubscriptionsComponent,
|
SubscriptionsComponent,
|
||||||
SubscribeDialogComponent,
|
SubscribeDialogComponent,
|
||||||
SubscriptionComponent,
|
SubscriptionComponent,
|
||||||
SubscriptionFileCardComponent,
|
|
||||||
SubscriptionInfoDialogComponent,
|
SubscriptionInfoDialogComponent,
|
||||||
SettingsComponent,
|
SettingsComponent,
|
||||||
AboutDialogComponent,
|
AboutDialogComponent,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 order-1 col-sm-4 order-sm-2 d-flex justify-content-center">
|
<div class="col-12 order-1 col-sm-4 order-sm-2 d-flex justify-content-center">
|
||||||
<h4 class="my-videos-title" i18n="My videos title">My videos</h4>
|
<h4 class="my-videos-title" i18n="My files title">My files</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 order-3 col-sm-4 order-sm-3 d-flex justify-content-center">
|
<div class="col-12 order-3 col-sm-4 order-sm-3 d-flex justify-content-center">
|
||||||
<mat-form-field [ngClass]="searchIsFocused ? 'search-bar-focused' : 'search-bar-unfocused'" class="search-bar" color="accent">
|
<mat-form-field [ngClass]="searchIsFocused ? 'search-bar-focused' : 'search-bar-unfocused'" class="search-bar" color="accent">
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<app-unified-file-card [index]="i" [card_size]="postsService.card_size" [locale]="postsService.locale" (goToFile)="goToFile($event)" (goToSubscription)="goToSubscription($event)" [file_obj]="file" [use_youtubedl_archive]="postsService.config['Downloader']['use_youtubedl_archive']" [availablePlaylists]="playlists" (addFileToPlaylist)="addFileToPlaylist($event)" [loading]="false" (deleteFile)="deleteFile($event)" [baseStreamPath]="postsService.path" [jwtString]="postsService.isLoggedIn ? this.postsService.token : ''"></app-unified-file-card>
|
<app-unified-file-card [index]="i" [card_size]="postsService.card_size" [locale]="postsService.locale" (goToFile)="goToFile($event)" (goToSubscription)="goToSubscription($event)" [file_obj]="file" [use_youtubedl_archive]="postsService.config['Downloader']['use_youtubedl_archive']" [availablePlaylists]="playlists" (addFileToPlaylist)="addFileToPlaylist($event)" [loading]="false" (deleteFile)="deleteFile($event)" [baseStreamPath]="postsService.path" [jwtString]="postsService.isLoggedIn ? this.postsService.token : ''"></app-unified-file-card>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="paged_data.length === 0">
|
<div *ngIf="paged_data.length === 0">
|
||||||
<ng-container i18n="No videos found">No videos found.</ng-container>
|
<ng-container i18n="No files found">No files found.</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="!normal_files_received && loading_files && loading_files.length > 0">
|
<ng-container *ngIf="!normal_files_received && loading_files && loading_files.length > 0">
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div *ngIf="usePaginator">
|
||||||
<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>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||||
import { PostsService } from 'app/posts.services';
|
import { PostsService } from 'app/posts.services';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { FileType } from '../../../api-types';
|
import { FileType, FileTypeFilter } from '../../../api-types';
|
||||||
import { MatPaginator } from '@angular/material/paginator';
|
import { MatPaginator } from '@angular/material/paginator';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { distinctUntilChanged } from 'rxjs/operators';
|
import { distinctUntilChanged } from 'rxjs/operators';
|
||||||
@@ -13,6 +13,9 @@ import { distinctUntilChanged } from 'rxjs/operators';
|
|||||||
})
|
})
|
||||||
export class RecentVideosComponent implements OnInit {
|
export class RecentVideosComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() usePaginator = true;
|
||||||
|
@Input() sub_id = null;
|
||||||
|
|
||||||
cached_file_count = 0;
|
cached_file_count = 0;
|
||||||
loading_files = null;
|
loading_files = null;
|
||||||
|
|
||||||
@@ -104,7 +107,7 @@ export class RecentVideosComponent implements OnInit {
|
|||||||
|
|
||||||
// set file type filter to cached value
|
// set file type filter to cached value
|
||||||
const cached_file_type_filter = localStorage.getItem('file_type_filter');
|
const cached_file_type_filter = localStorage.getItem('file_type_filter');
|
||||||
if (cached_file_type_filter) {
|
if (this.usePaginator && cached_file_type_filter) {
|
||||||
this.fileTypeFilter = cached_file_type_filter;
|
this.fileTypeFilter = cached_file_type_filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +166,7 @@ export class RecentVideosComponent implements OnInit {
|
|||||||
const current_file_index = (this.paginator?.pageIndex ? this.paginator.pageIndex : 0)*this.pageSize;
|
const current_file_index = (this.paginator?.pageIndex ? this.paginator.pageIndex : 0)*this.pageSize;
|
||||||
const sort = {by: this.filterProperty['property'], order: this.descendingMode ? -1 : 1};
|
const sort = {by: this.filterProperty['property'], order: this.descendingMode ? -1 : 1};
|
||||||
const range = [current_file_index, current_file_index + this.pageSize];
|
const range = [current_file_index, current_file_index + this.pageSize];
|
||||||
this.postsService.getAllFiles(sort, range, this.search_mode ? this.search_text : null, this.fileTypeFilter).subscribe(res => {
|
this.postsService.getAllFiles(sort, range, this.search_mode ? this.search_text : null, this.fileTypeFilter as FileTypeFilter, this.sub_id).subscribe(res => {
|
||||||
this.file_count = res['file_count'];
|
this.file_count = res['file_count'];
|
||||||
this.paged_data = res['files'];
|
this.paged_data = res['files'];
|
||||||
for (let i = 0; i < this.paged_data.length; i++) {
|
for (let i = 0; i < this.paged_data.length; i++) {
|
||||||
|
|||||||
@@ -97,7 +97,10 @@ import {
|
|||||||
Schedule,
|
Schedule,
|
||||||
ClearDownloadsRequest,
|
ClearDownloadsRequest,
|
||||||
Category,
|
Category,
|
||||||
UpdateFileRequest
|
UpdateFileRequest,
|
||||||
|
Sort,
|
||||||
|
FileTypeFilter,
|
||||||
|
GetAllFilesRequest
|
||||||
} from '../api-types';
|
} from '../api-types';
|
||||||
import { isoLangs } from './settings/locales_list';
|
import { isoLangs } from './settings/locales_list';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
@@ -355,8 +358,9 @@ export class PostsService implements CanActivate {
|
|||||||
return this.http.post<GetFileResponse>(this.path + 'getFile', body, this.httpOptions);
|
return this.http.post<GetFileResponse>(this.path + 'getFile', body, this.httpOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllFiles(sort, range, text_search, file_type_filter) {
|
getAllFiles(sort: Sort, range: number[], text_search: string, file_type_filter: FileTypeFilter, sub_id: string) {
|
||||||
return this.http.post<GetAllFilesResponse>(this.path + 'getAllFiles', {sort: sort, range: range, text_search: text_search, file_type_filter: file_type_filter}, this.httpOptions);
|
const body: GetAllFilesRequest = {sort: sort, range: range, text_search: text_search, file_type_filter: file_type_filter, sub_id: sub_id};
|
||||||
|
return this.http.post<GetAllFilesResponse>(this.path + 'getAllFiles', body, this.httpOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFile(uid: string, change_obj: Object) {
|
updateFile(uid: string, change_obj: Object) {
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
<div style="position: relative; width: fit-content;">
|
|
||||||
<div class="duration-time">
|
|
||||||
<ng-container i18n="Video duration label">Length:</ng-container> {{formattedDuration}}
|
|
||||||
</div>
|
|
||||||
<button [matMenuTriggerFor]="action_menu" class="menuButton" mat-icon-button><mat-icon>more_vert</mat-icon></button>
|
|
||||||
<mat-menu #action_menu="matMenu">
|
|
||||||
<button (click)="openSubscriptionInfoDialog()" mat-menu-item><mat-icon>info</mat-icon><ng-container i18n="Subscription video info button">Info</ng-container></button>
|
|
||||||
<button (click)="deleteAndRedownload()" mat-menu-item><mat-icon>restore</mat-icon><ng-container i18n="Delete and redownload subscription video button">Delete and redownload</ng-container></button>
|
|
||||||
<button (click)="deleteForever()" mat-menu-item *ngIf="sub.archive && use_youtubedl_archive"><mat-icon>delete_forever</mat-icon><ng-container i18n="Delete forever subscription video button">Delete forever</ng-container></button>
|
|
||||||
</mat-menu>
|
|
||||||
<mat-card (click)="goToFile()" matRipple class="example-card mat-elevation-z6">
|
|
||||||
<div style="padding:5px">
|
|
||||||
<div *ngIf="!image_errored && file.thumbnailURL" class="img-div">
|
|
||||||
<img class="image" (error)="onImgError($event)" [src]="file.thumbnailURL" alt="Thumbnail">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span class="max-two-lines"><strong>{{file.title}}</strong></span>
|
|
||||||
</div>
|
|
||||||
</mat-card>
|
|
||||||
</div>
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
.example-card {
|
|
||||||
width: 200px;
|
|
||||||
height: 200px;
|
|
||||||
padding: 0px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menuButton {
|
|
||||||
right: 0px;
|
|
||||||
top: -1px;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 999;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Coerce the <span> icon container away from display:inline */
|
|
||||||
.mat-icon-button .mat-button-wrapper {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image {
|
|
||||||
width: 200px;
|
|
||||||
height: 112.5px;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.example-full-width-height {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%
|
|
||||||
}
|
|
||||||
|
|
||||||
.centered {
|
|
||||||
margin: 0 auto;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.img-div {
|
|
||||||
max-height: 80px;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 32px 0px 0px -5px;
|
|
||||||
width: calc(100% + 5px + 5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.max-two-lines {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -moz-box;
|
|
||||||
max-height: 2.4em;
|
|
||||||
line-height: 1.2em;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
bottom: 5px;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.duration-time {
|
|
||||||
position: absolute;
|
|
||||||
left: 5px;
|
|
||||||
top: 5px;
|
|
||||||
z-index: 99999;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 576px){
|
|
||||||
|
|
||||||
.example-card {
|
|
||||||
width: 175px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image {
|
|
||||||
width: 175px;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { SubscriptionFileCardComponent } from './subscription-file-card.component';
|
|
||||||
|
|
||||||
describe('SubscriptionFileCardComponent', () => {
|
|
||||||
let component: SubscriptionFileCardComponent;
|
|
||||||
let fixture: ComponentFixture<SubscriptionFileCardComponent>;
|
|
||||||
|
|
||||||
beforeEach(waitForAsync(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ SubscriptionFileCardComponent ]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(SubscriptionFileCardComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
|
||||||
import { Observable, Subject } from 'rxjs';
|
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { PostsService } from 'app/posts.services';
|
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
|
||||||
import { VideoInfoDialogComponent } from 'app/dialogs/video-info-dialog/video-info-dialog.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-subscription-file-card',
|
|
||||||
templateUrl: './subscription-file-card.component.html',
|
|
||||||
styleUrls: ['./subscription-file-card.component.scss']
|
|
||||||
})
|
|
||||||
export class SubscriptionFileCardComponent implements OnInit {
|
|
||||||
image_errored = false;
|
|
||||||
image_loaded = false;
|
|
||||||
|
|
||||||
formattedDuration = null;
|
|
||||||
|
|
||||||
@Input() file;
|
|
||||||
@Input() sub;
|
|
||||||
@Input() use_youtubedl_archive = false;
|
|
||||||
|
|
||||||
@Output() goToFileEmit = new EventEmitter<any>();
|
|
||||||
@Output() reloadSubscription = new EventEmitter<boolean>();
|
|
||||||
|
|
||||||
constructor(private snackBar: MatSnackBar, private postsService: PostsService, private dialog: MatDialog) {}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
if (this.file.duration) {
|
|
||||||
this.formattedDuration = fancyTimeFormat(this.file.duration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onImgError(event) {
|
|
||||||
this.image_errored = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
imageLoaded(loaded) {
|
|
||||||
this.image_loaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
goToFile() {
|
|
||||||
const emit_obj = {
|
|
||||||
uid: this.file.uid,
|
|
||||||
url: this.file.requested_formats ? this.file.requested_formats[0].url : this.file.url
|
|
||||||
}
|
|
||||||
this.goToFileEmit.emit(emit_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
openSubscriptionInfoDialog() {
|
|
||||||
const dialogRef = this.dialog.open(VideoInfoDialogComponent, {
|
|
||||||
data: {
|
|
||||||
file: this.file,
|
|
||||||
},
|
|
||||||
minWidth: '50vw'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteAndRedownload() {
|
|
||||||
this.postsService.deleteSubscriptionFile(this.sub, this.file.id, false, this.file.uid).subscribe(res => {
|
|
||||||
this.reloadSubscription.emit(true);
|
|
||||||
this.openSnackBar(`Successfully deleted file: '${this.file.id}'`, 'Dismiss.');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteForever() {
|
|
||||||
this.postsService.deleteSubscriptionFile(this.sub, this.file.id, true, this.file.uid).subscribe(res => {
|
|
||||||
this.reloadSubscription.emit(true);
|
|
||||||
this.openSnackBar(`Successfully deleted file: '${this.file.id}'`, 'Dismiss.');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public openSnackBar(message: string, action: string) {
|
|
||||||
this.snackBar.open(message, action, {
|
|
||||||
duration: 2000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function fancyTimeFormat(time) {
|
|
||||||
// Hours, minutes and seconds
|
|
||||||
const hrs = ~~(time / 3600);
|
|
||||||
const mins = ~~((time % 3600) / 60);
|
|
||||||
const secs = ~~time % 60;
|
|
||||||
|
|
||||||
// Output like "1:01" or "4:03:59" or "123:03:59"
|
|
||||||
let ret = '';
|
|
||||||
|
|
||||||
if (hrs > 0) {
|
|
||||||
ret += '' + hrs + ':' + (mins < 10 ? '0' : '');
|
|
||||||
}
|
|
||||||
|
|
||||||
ret += '' + mins + ':' + (secs < 10 ? '0' : '');
|
|
||||||
ret += '' + secs;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
@@ -10,38 +10,7 @@
|
|||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<div *ngIf="subscription">
|
<div *ngIf="subscription">
|
||||||
<div class="flex-grid">
|
<app-recent-videos [sub_id]="subscription.id" [usePaginator]="false"></app-recent-videos>
|
||||||
<div class="filter-select-parent">
|
|
||||||
<div style="display: inline-block;">
|
|
||||||
<mat-select style="width: 110px;" [(ngModel)]="this.filterProperty" (selectionChange)="filterOptionChanged($event.value)">
|
|
||||||
<mat-option *ngFor="let filterOption of filterProperties | keyvalue" [value]="filterOption.value">
|
|
||||||
{{filterOption['value']['label']}}
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</div>
|
|
||||||
<div style="display: inline-block;">
|
|
||||||
<button (click)="toggleModeChange()" mat-icon-button><mat-icon>{{descendingMode ? 'arrow_downward' : 'arrow_upward'}}</mat-icon></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<h4 i18n="Subscription videos title" style="text-align: center; margin-bottom: 20px;">Videos</h4>
|
|
||||||
</div>
|
|
||||||
<div style="top: -12px;" class="col">
|
|
||||||
<mat-form-field [ngClass]="searchIsFocused ? 'search-bar-focused' : 'search-bar-unfocused'" class="search-bar" color="accent">
|
|
||||||
<input (focus)="searchIsFocused = true" (blur)="searchIsFocused = false" class="search-input" type="text" placeholder="Search" i18n-placeholder="Subscription videos search placeholder" [(ngModel)]="search_text" (ngModelChange)="onSearchInputChanged($event)" matInput>
|
|
||||||
<mat-icon matSuffix>search</mat-icon>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div *ngFor="let file of filtered_files" class="col-6 col-lg-4 mb-2 mt-2 sub-file-col">
|
|
||||||
<app-subscription-file-card (reloadSubscription)="getSubscription()" (goToFileEmit)="goToFile($event)" [file]="file" [sub]="subscription" [use_youtubedl_archive]="use_youtubedl_archive"></app-subscription-file-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<button class="edit-button" color="primary" (click)="editSubscription()" [disabled]="downloading" mat-fab><mat-icon class="save-icon">edit</mat-icon></button>
|
<button class="edit-button" color="primary" (click)="editSubscription()" [disabled]="downloading" mat-fab><mat-icon class="save-icon">edit</mat-icon></button>
|
||||||
<button class="watch-button" color="primary" (click)="watchSubscription()" mat-fab><mat-icon class="save-icon">video_library</mat-icon></button>
|
<button class="watch-button" color="primary" (click)="watchSubscription()" mat-fab><mat-icon class="save-icon">video_library</mat-icon></button>
|
||||||
|
|||||||
@@ -100,22 +100,12 @@ export class SubscriptionComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getConfig() {
|
getConfig(): void {
|
||||||
this.use_youtubedl_archive = this.postsService.config['Downloader']['use_youtubedl_archive'];
|
this.use_youtubedl_archive = this.postsService.config['Downloader']['use_youtubedl_archive'];
|
||||||
}
|
}
|
||||||
|
|
||||||
goToFile(emit_obj) {
|
|
||||||
const uid = emit_obj['uid'];
|
|
||||||
const url = emit_obj['url'];
|
|
||||||
localStorage.setItem('player_navigator', this.router.url);
|
|
||||||
if (this.subscription.streamingOnly) {
|
|
||||||
this.router.navigate(['/player', {uid: uid, url: url}]);
|
|
||||||
} else {
|
|
||||||
this.router.navigate(['/player', {uid: uid}]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onSearchInputChanged(newvalue) {
|
onSearchInputChanged(newvalue: string): void {
|
||||||
if (newvalue.length > 0) {
|
if (newvalue.length > 0) {
|
||||||
this.search_mode = true;
|
this.search_mode = true;
|
||||||
this.filterFiles(newvalue);
|
this.filterFiles(newvalue);
|
||||||
@@ -129,7 +119,7 @@ export class SubscriptionComponent implements OnInit, OnDestroy {
|
|||||||
this.filtered_files = this.files.filter(option => option.id.toLowerCase().includes(filterValue));
|
this.filtered_files = this.files.filter(option => option.id.toLowerCase().includes(filterValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
filterByProperty(prop) {
|
filterByProperty(prop: string): void {
|
||||||
if (this.descendingMode) {
|
if (this.descendingMode) {
|
||||||
this.filtered_files = this.filtered_files.sort((a, b) => (a[prop] > b[prop] ? -1 : 1));
|
this.filtered_files = this.filtered_files.sort((a, b) => (a[prop] > b[prop] ? -1 : 1));
|
||||||
} else {
|
} else {
|
||||||
@@ -142,17 +132,12 @@ export class SubscriptionComponent implements OnInit, OnDestroy {
|
|||||||
localStorage.setItem('filter_property', value['key']);
|
localStorage.setItem('filter_property', value['key']);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleModeChange() {
|
toggleModeChange(): void {
|
||||||
this.descendingMode = !this.descendingMode;
|
this.descendingMode = !this.descendingMode;
|
||||||
this.filterByProperty(this.filterProperty['property']);
|
this.filterByProperty(this.filterProperty['property']);
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadContent() {
|
downloadContent(): void {
|
||||||
const fileNames = [];
|
|
||||||
for (let i = 0; i < this.files.length; i++) {
|
|
||||||
fileNames.push(this.files[i].path);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.downloading = true;
|
this.downloading = true;
|
||||||
this.postsService.downloadSubFromServer(this.subscription.id).subscribe(res => {
|
this.postsService.downloadSubFromServer(this.subscription.id).subscribe(res => {
|
||||||
this.downloading = false;
|
this.downloading = false;
|
||||||
@@ -164,7 +149,7 @@ export class SubscriptionComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
editSubscription() {
|
editSubscription(): void {
|
||||||
this.dialog.open(EditSubscriptionDialogComponent, {
|
this.dialog.open(EditSubscriptionDialogComponent, {
|
||||||
data: {
|
data: {
|
||||||
sub: this.postsService.getSubscriptionByID(this.subscription.id)
|
sub: this.postsService.getSubscriptionByID(this.subscription.id)
|
||||||
@@ -172,7 +157,7 @@ export class SubscriptionComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
watchSubscription() {
|
watchSubscription(): void {
|
||||||
this.router.navigate(['/player', {sub_id: this.subscription.id}])
|
this.router.navigate(['/player', {sub_id: this.subscription.id}])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -592,10 +592,6 @@
|
|||||||
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context>
|
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context>
|
||||||
<context context-type="linenumber">24</context>
|
<context context-type="linenumber">24</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/subscription/subscription/subscription.component.html</context>
|
|
||||||
<context context-type="linenumber">33</context>
|
|
||||||
</context-group>
|
|
||||||
<note priority="1" from="description">search field description</note>
|
<note priority="1" from="description">search field description</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="746f64ddd9001ac456327cd9a3d5152203a4b93c" datatype="html">
|
<trans-unit id="746f64ddd9001ac456327cd9a3d5152203a4b93c" datatype="html">
|
||||||
@@ -682,21 +678,21 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">Edit role</note>
|
<note priority="1" from="description">Edit role</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="d02888c485d3aeab6de628508f4a00312a722894" datatype="html">
|
<trans-unit id="52e0fa8ada52c3f29774a4508582fd98250b9f93" datatype="html">
|
||||||
<source>My videos</source>
|
<source>My files</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context>
|
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context>
|
||||||
<context context-type="linenumber">20</context>
|
<context context-type="linenumber">20</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">My videos title</note>
|
<note priority="1" from="description">My files title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="73423607944a694ce6f9e55cfee329681bb4d9f9" datatype="html">
|
<trans-unit id="6827066f436adfc56a142d5816a8be6113d73b01" datatype="html">
|
||||||
<source>No videos found.</source>
|
<source>No files found.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context>
|
<context context-type="sourcefile">src/app/components/recent-videos/recent-videos.component.html</context>
|
||||||
<context context-type="linenumber">38</context>
|
<context context-type="linenumber">38</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">No videos found</note>
|
<note priority="1" from="description">No files found</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="b4e61d531b8db72449f043f122119da964f4fc54" datatype="html">
|
<trans-unit id="b4e61d531b8db72449f043f122119da964f4fc54" datatype="html">
|
||||||
<source>File type</source>
|
<source>File type</source>
|
||||||
@@ -1135,10 +1131,6 @@
|
|||||||
<context context-type="sourcefile">src/app/create-playlist/create-playlist.component.html</context>
|
<context context-type="sourcefile">src/app/create-playlist/create-playlist.component.html</context>
|
||||||
<context context-type="linenumber">20</context>
|
<context context-type="linenumber">20</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/subscription/subscription/subscription.component.html</context>
|
|
||||||
<context context-type="linenumber">29</context>
|
|
||||||
</context-group>
|
|
||||||
<note priority="1" from="description">Videos title</note>
|
<note priority="1" from="description">Videos title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="cec82c0a545f37420d55a9b6c45c20546e82f94e" datatype="html">
|
<trans-unit id="cec82c0a545f37420d55a9b6c45c20546e82f94e" datatype="html">
|
||||||
|
|||||||
Reference in New Issue
Block a user