mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-19 19:21:02 +03:00
Merge branch 'master' of https://github.com/Tzahi12345/YoutubeDL-Material into api-generator
This commit is contained in:
@@ -1,19 +1,21 @@
|
||||
import { Component, OnInit, HostListener, EventEmitter, OnDestroy, AfterViewInit } from '@angular/core';
|
||||
import { VgAPI } from 'ngx-videogular';
|
||||
import { Component, OnInit, HostListener, OnDestroy, AfterViewInit, ViewChild, ChangeDetectorRef } from '@angular/core';
|
||||
import { VgApiService } from '@videogular/ngx-videogular/core';
|
||||
import { PostsService } from 'app/posts.services';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { InputDialogComponent } from 'app/input-dialog/input-dialog.component';
|
||||
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||
import { ShareMediaDialogComponent } from '../dialogs/share-media-dialog/share-media-dialog.component';
|
||||
import { FileType } from '../../api-types';
|
||||
import { TwitchChatComponent } from 'app/components/twitch-chat/twitch-chat.component';
|
||||
import { VideoInfoDialogComponent } from 'app/dialogs/video-info-dialog/video-info-dialog.component';
|
||||
|
||||
export interface IMedia {
|
||||
title: string;
|
||||
src: string;
|
||||
type: string;
|
||||
label: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@@ -31,19 +33,20 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
currentIndex = 0;
|
||||
currentItem: IMedia = null;
|
||||
api: VgAPI;
|
||||
api: VgApiService;
|
||||
api_ready = false;
|
||||
|
||||
// params
|
||||
fileNames: string[];
|
||||
uids: string[];
|
||||
type: FileType;
|
||||
id = null; // used for playlists (not subscription)
|
||||
playlist_id = null; // used for playlists (not subscription)
|
||||
uid = null; // used for non-subscription files (audio, video, playlist)
|
||||
subscriptionName = null;
|
||||
subscription = null;
|
||||
sub_id = null;
|
||||
subPlaylist = null;
|
||||
uuid = null; // used for sharing in multi-user mode, uuid is the user that downloaded the video
|
||||
timestamp = null;
|
||||
|
||||
is_shared = false;
|
||||
auto = null;
|
||||
|
||||
db_playlist = null;
|
||||
db_file = null;
|
||||
@@ -53,8 +56,6 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
videoFolderPath = null;
|
||||
subscriptionFolderPath = null;
|
||||
|
||||
sharingEnabled = null;
|
||||
|
||||
// url-mode params
|
||||
url = null;
|
||||
name = null;
|
||||
@@ -66,6 +67,8 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
save_volume_timer = null;
|
||||
original_volume = null;
|
||||
|
||||
@ViewChild('twitchchat') twitchChat: TwitchChatComponent;
|
||||
|
||||
@HostListener('window:resize', ['$event'])
|
||||
onResize(event) {
|
||||
this.innerWidth = window.innerWidth;
|
||||
@@ -74,15 +77,14 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
ngOnInit(): void {
|
||||
this.innerWidth = window.innerWidth;
|
||||
|
||||
this.type = this.route.snapshot.paramMap.get('type') as FileType;
|
||||
this.id = this.route.snapshot.paramMap.get('id');
|
||||
this.playlist_id = this.route.snapshot.paramMap.get('playlist_id');
|
||||
this.uid = this.route.snapshot.paramMap.get('uid');
|
||||
this.subscriptionName = this.route.snapshot.paramMap.get('subscriptionName');
|
||||
this.subPlaylist = this.route.snapshot.paramMap.get('subPlaylist');
|
||||
this.sub_id = this.route.snapshot.paramMap.get('sub_id');
|
||||
this.url = this.route.snapshot.paramMap.get('url');
|
||||
this.name = this.route.snapshot.paramMap.get('name');
|
||||
this.uuid = this.route.snapshot.paramMap.get('uuid');
|
||||
this.timestamp = this.route.snapshot.paramMap.get('timestamp');
|
||||
this.auto = this.route.snapshot.paramMap.get('auto');
|
||||
|
||||
// loading config
|
||||
if (this.postsService.initialized) {
|
||||
@@ -97,6 +99,7 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.cdr.detectChanges();
|
||||
this.postsService.sidenav.close();
|
||||
}
|
||||
|
||||
@@ -106,7 +109,7 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
constructor(public postsService: PostsService, private route: ActivatedRoute, private dialog: MatDialog, private router: Router,
|
||||
public snackBar: MatSnackBar) {
|
||||
public snackBar: MatSnackBar, private cdr: ChangeDetectorRef) {
|
||||
|
||||
}
|
||||
|
||||
@@ -115,17 +118,14 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.audioFolderPath = this.postsService.config['Downloader']['path-audio'];
|
||||
this.videoFolderPath = this.postsService.config['Downloader']['path-video'];
|
||||
this.subscriptionFolderPath = this.postsService.config['Subscriptions']['subscriptions_base_path'];
|
||||
this.fileNames = this.route.snapshot.paramMap.get('fileNames') ? this.route.snapshot.paramMap.get('fileNames').split('|nvr|') : null;
|
||||
|
||||
if (!this.fileNames && !this.type) {
|
||||
this.is_shared = true;
|
||||
}
|
||||
|
||||
if (this.uid && !this.id) {
|
||||
this.getFile();
|
||||
} else if (this.id) {
|
||||
if (this.sub_id) {
|
||||
this.getSubscription();
|
||||
} else if (this.playlist_id) {
|
||||
this.getPlaylistFiles();
|
||||
}
|
||||
} else if (this.uid) {
|
||||
this.getFile();
|
||||
}
|
||||
|
||||
if (this.url) {
|
||||
// if a url is given, just stream the URL
|
||||
@@ -134,49 +134,52 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
title: this.name,
|
||||
label: this.name,
|
||||
src: this.url,
|
||||
type: 'video/mp4'
|
||||
type: 'video/mp4',
|
||||
url: this.url
|
||||
}
|
||||
this.playlist.push(imedia);
|
||||
this.currentItem = this.playlist[0];
|
||||
this.currentIndex = 0;
|
||||
this.show_player = true;
|
||||
} else if (this.subscriptionName || this.fileNames) {
|
||||
this.show_player = true;
|
||||
this.parseFileNames();
|
||||
}
|
||||
}
|
||||
|
||||
getFile() {
|
||||
const already_has_filenames = !!this.fileNames;
|
||||
this.postsService.getFile(this.uid, null, this.uuid).subscribe(res => {
|
||||
this.db_file = res['file'];
|
||||
if (!this.db_file) {
|
||||
this.openSnackBar('Failed to get file information from the server.', 'Dismiss');
|
||||
return;
|
||||
}
|
||||
this.sharingEnabled = this.db_file.sharingEnabled;
|
||||
if (!this.fileNames) {
|
||||
// means it's a shared video
|
||||
if (!this.id) {
|
||||
// regular video/audio file (not playlist)
|
||||
this.fileNames = [this.db_file['id']];
|
||||
this.type = (this.db_file['isAudio'] ? 'audio' : 'video') as FileType;
|
||||
if (!already_has_filenames) { this.parseFileNames(); }
|
||||
}
|
||||
}
|
||||
if (this.db_file['sharingEnabled'] || !this.uuid) {
|
||||
this.show_player = true;
|
||||
} else if (!already_has_filenames) {
|
||||
this.openSnackBar('Error: Sharing has been disabled for this video!', 'Dismiss');
|
||||
}
|
||||
this.postsService.incrementViewCount(this.db_file['uid'], null, this.uuid).subscribe(res => {}, err => {
|
||||
console.error('Failed to increment view count');
|
||||
console.error(err);
|
||||
});
|
||||
// regular video/audio file (not playlist)
|
||||
this.uids = [this.db_file['uid']];
|
||||
this.type = this.db_file['isAudio'] ? 'audio' : 'video' as FileType;
|
||||
this.parseFileNames();
|
||||
});
|
||||
}
|
||||
|
||||
getSubscription() {
|
||||
this.postsService.getSubscription(this.sub_id).subscribe(res => {
|
||||
const subscription = res['subscription'];
|
||||
this.subscription = subscription;
|
||||
this.type === this.subscription.type;
|
||||
this.uids = this.subscription.videos.map(video => video['uid']);
|
||||
this.parseFileNames();
|
||||
}, err => {
|
||||
this.openSnackBar(`Failed to find subscription ${this.sub_id}`, 'Dismiss');
|
||||
});
|
||||
}
|
||||
|
||||
getPlaylistFiles() {
|
||||
this.postsService.getPlaylist(this.id, null, this.uuid).subscribe(res => {
|
||||
this.postsService.getPlaylist(this.playlist_id, this.uuid, true).subscribe(res => {
|
||||
if (res['playlist']) {
|
||||
this.db_playlist = res['playlist'];
|
||||
this.fileNames = this.db_playlist['fileNames'];
|
||||
this.db_playlist['file_objs'] = res['file_objs'];
|
||||
this.uids = this.db_playlist.uids;
|
||||
this.type = res['type'];
|
||||
this.show_player = true;
|
||||
this.parseFileNames();
|
||||
@@ -188,65 +191,58 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
parseFileNames() {
|
||||
let fileType = null;
|
||||
if (this.type === 'audio') {
|
||||
fileType = 'audio/mp3';
|
||||
} else if (this.type === 'video') {
|
||||
fileType = 'video/mp4';
|
||||
} else {
|
||||
// error
|
||||
console.error('Must have valid file type! Use \'audio\', \'video\', or \'subscription\'.');
|
||||
}
|
||||
parseFileNames() {
|
||||
this.playlist = [];
|
||||
for (let i = 0; i < this.fileNames.length; i++) {
|
||||
const fileName = this.fileNames[i];
|
||||
let baseLocation = null;
|
||||
let fullLocation = null;
|
||||
if (!this.subscriptionName) {
|
||||
baseLocation = this.type + '/';
|
||||
fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName);
|
||||
for (let i = 0; i < this.uids.length; i++) {
|
||||
let file_obj = null;
|
||||
if (this.playlist_id) {
|
||||
file_obj = this.db_playlist['file_objs'][i];
|
||||
} else if (this.sub_id) {
|
||||
file_obj = this.subscription['videos'][i];
|
||||
} else {
|
||||
// default to video but include subscription name param
|
||||
baseLocation = this.type === 'audio' ? 'audio/' : 'video/';
|
||||
fullLocation = this.baseStreamPath + baseLocation + encodeURIComponent(fileName) + '?subName=' + this.subscriptionName +
|
||||
'&subPlaylist=' + this.subPlaylist;
|
||||
file_obj = this.db_file;
|
||||
}
|
||||
|
||||
// adds user token if in multi-user-mode
|
||||
const uuid_str = this.uuid ? `&uuid=${this.uuid}` : '';
|
||||
const uid_str = (this.id || !this.db_file) ? '' : `&uid=${this.db_file.uid}`;
|
||||
const type_str = (this.id || !this.db_file || !this.db_file.type) ? '' : `&type=${this.db_file.type}`
|
||||
const id_str = this.id ? `&id=${this.id}` : '';
|
||||
const mime_type = file_obj.isAudio ? 'audio/mp3' : 'video/mp4'
|
||||
|
||||
let baseLocation = 'stream/';
|
||||
let fullLocation = this.baseStreamPath + baseLocation + `?test=test&uid=${file_obj['uid']}`;
|
||||
|
||||
if (this.postsService.isLoggedIn) {
|
||||
fullLocation += (this.subscriptionName ? '&' : '?') + `jwt=${this.postsService.token}`;
|
||||
if (this.is_shared) { fullLocation += `${uuid_str}${uid_str}${type_str}${id_str}`; }
|
||||
} else if (this.is_shared) {
|
||||
fullLocation += (this.subscriptionName ? '&' : '?') + `test=test${uuid_str}${uid_str}${type_str}${id_str}`;
|
||||
fullLocation += `&jwt=${this.postsService.token}`;
|
||||
}
|
||||
// if it has a slash (meaning it's in a directory), only get the file name for the label
|
||||
let label = null;
|
||||
const decodedName = decodeURIComponent(fileName);
|
||||
const hasSlash = decodedName.includes('/') || decodedName.includes('\\');
|
||||
if (hasSlash) {
|
||||
label = decodedName.replace(/^.*[\\\/]/, '');
|
||||
} else {
|
||||
label = decodedName;
|
||||
|
||||
if (this.uuid) {
|
||||
fullLocation += `&uuid=${this.uuid}`;
|
||||
}
|
||||
|
||||
if (this.sub_id) {
|
||||
fullLocation += `&sub_id=${this.sub_id}`;
|
||||
} else if (this.playlist_id) {
|
||||
fullLocation += `&playlist_id=${this.playlist_id}`;
|
||||
}
|
||||
|
||||
const mediaObject: IMedia = {
|
||||
title: fileName,
|
||||
title: file_obj['title'],
|
||||
src: fullLocation,
|
||||
type: fileType,
|
||||
label: label
|
||||
type: mime_type,
|
||||
label: file_obj['title'],
|
||||
url: file_obj['url']
|
||||
}
|
||||
this.playlist.push(mediaObject);
|
||||
}
|
||||
if (this.db_playlist && this.db_playlist['randomize_order']) {
|
||||
this.shuffleArray(this.playlist);
|
||||
}
|
||||
this.currentItem = this.playlist[this.currentIndex];
|
||||
this.original_playlist = JSON.stringify(this.playlist);
|
||||
this.show_player = true;
|
||||
}
|
||||
|
||||
onPlayerReady(api: VgAPI) {
|
||||
onPlayerReady(api: VgApiService) {
|
||||
this.api = api;
|
||||
this.api_ready = true;
|
||||
this.cdr.detectChanges();
|
||||
|
||||
// checks if volume has been previously set. if so, use that as default
|
||||
if (localStorage.getItem('player_volume')) {
|
||||
@@ -291,13 +287,6 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.currentItem = item;
|
||||
}
|
||||
|
||||
getFileInfos() {
|
||||
const fileNames = this.getFileNames();
|
||||
this.postsService.getFileInfo(fileNames, this.type, false).subscribe(res => {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
getFileNames() {
|
||||
const fileNames = [];
|
||||
for (let i = 0; i < this.playlist.length; i++) {
|
||||
@@ -311,17 +300,9 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
downloadContent() {
|
||||
const fileNames = [];
|
||||
for (let i = 0; i < this.playlist.length; i++) {
|
||||
fileNames.push(this.playlist[i].title);
|
||||
}
|
||||
|
||||
const zipName = fileNames[0].split(' ')[0] + fileNames[1].split(' ')[0];
|
||||
const zipName = this.db_playlist.name;
|
||||
this.downloading = true;
|
||||
this.postsService.downloadFileFromServer(
|
||||
fileNames, this.type,
|
||||
{outputName: zipName, uuid: !this.uuid ? this.postsService.user.uid : this.uuid, id: this.id}
|
||||
).subscribe(res => {
|
||||
this.postsService.downloadPlaylistFromServer(this.playlist_id, this.uuid).subscribe(res => {
|
||||
this.downloading = false;
|
||||
const blob: Blob = res;
|
||||
saveAs(blob, zipName + '.zip');
|
||||
@@ -332,13 +313,10 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
downloadFile() {
|
||||
const ext = (this.type === 'audio') ? '.mp3' : '.mp4';
|
||||
const filename = this.playlist[0].title;
|
||||
const ext = (this.playlist[0].type === 'audio/mp3') ? '.mp3' : '.mp4';
|
||||
this.downloading = true;
|
||||
this.postsService.downloadFileFromServer(
|
||||
filename, this.type,
|
||||
{subscriptionName: this.subscriptionName, subPlaylist: this.subPlaylist, uid: this.is_shared ? this.db_file['uid'] : null, uuid: this.uuid}
|
||||
).subscribe(res => {
|
||||
this.postsService.downloadFileFromServer(this.uid, this.uuid, this.sub_id).subscribe(res => {
|
||||
this.downloading = false;
|
||||
const blob: Blob = res;
|
||||
saveAs(blob, filename + ext);
|
||||
@@ -348,50 +326,10 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
namePlaylistDialog() {
|
||||
const done = new EventEmitter<any>();
|
||||
const dialogRef = this.dialog.open(InputDialogComponent, {
|
||||
width: '300px',
|
||||
data: {
|
||||
inputTitle: 'Name the playlist',
|
||||
inputPlaceholder: 'Name',
|
||||
submitText: 'Favorite',
|
||||
doneEmitter: done
|
||||
}
|
||||
});
|
||||
|
||||
done.subscribe(name => {
|
||||
|
||||
// Eventually do additional checks on name
|
||||
if (name) {
|
||||
const fileNames = this.getFileNames();
|
||||
this.postsService.createPlaylist(name, fileNames, this.type as FileType, null).subscribe(res => {
|
||||
if (res['success']) {
|
||||
dialogRef.close();
|
||||
const new_playlist = res['new_playlist'];
|
||||
this.db_playlist = new_playlist;
|
||||
this.openSnackBar('Playlist \'' + name + '\' successfully created!', '')
|
||||
this.playlistPostCreationHandler(new_playlist.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
createPlaylist(name) {
|
||||
this.postsService.createPlaylist(name, this.fileNames, this.type, null).subscribe(res => {
|
||||
if (res['success']) {
|
||||
console.log('Success!');
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
playlistPostCreationHandler(playlistID) {
|
||||
// changes the route without moving from the current view or
|
||||
// triggering a navigation event
|
||||
this.id = playlistID;
|
||||
this.playlist_id = playlistID;
|
||||
this.router.navigateByUrl(this.router.url + ';id=' + playlistID);
|
||||
}
|
||||
|
||||
@@ -403,29 +341,12 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
return JSON.stringify(this.playlist) !== this.original_playlist;
|
||||
}
|
||||
|
||||
updatePlaylist() {
|
||||
const fileNames = this.getFileNames();
|
||||
this.playlist_updating = true;
|
||||
this.postsService.updatePlaylistFiles(this.id, fileNames, this.type as FileType).subscribe(res => {
|
||||
this.playlist_updating = false;
|
||||
if (res['success']) {
|
||||
const fileNamesEncoded = fileNames.join('|nvr|');
|
||||
this.router.navigate(['/player', {fileNames: fileNamesEncoded, type: this.type, id: this.id}]);
|
||||
this.openSnackBar('Successfully updated playlist.', '');
|
||||
this.original_playlist = JSON.stringify(this.playlist);
|
||||
} else {
|
||||
this.openSnackBar('ERROR: Failed to update playlist.', '');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
openShareDialog() {
|
||||
const dialogRef = this.dialog.open(ShareMediaDialogComponent, {
|
||||
data: {
|
||||
uid: this.id ? this.id : this.uid,
|
||||
type: this.type,
|
||||
sharing_enabled: this.id ? this.db_playlist.sharingEnabled : this.db_file.sharingEnabled,
|
||||
is_playlist: !!this.id,
|
||||
uid: this.playlist_id ? this.playlist_id : this.uid,
|
||||
sharing_enabled: this.playlist_id ? this.db_playlist.sharingEnabled : this.db_file.sharingEnabled,
|
||||
is_playlist: !!this.playlist_id,
|
||||
uuid: this.postsService.isLoggedIn ? this.postsService.user.uid : null,
|
||||
current_timestamp: this.api.time.current
|
||||
},
|
||||
@@ -433,13 +354,45 @@ export class PlayerComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(res => {
|
||||
if (!this.id) {
|
||||
if (!this.playlist_id) {
|
||||
this.getFile();
|
||||
} else {
|
||||
this.getPlaylistFiles();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
openFileInfoDialog() {
|
||||
this.dialog.open(VideoInfoDialogComponent, {
|
||||
data: {
|
||||
file: this.db_file,
|
||||
},
|
||||
minWidth: '50vw'
|
||||
})
|
||||
}
|
||||
|
||||
setPlaybackTimestamp(time) {
|
||||
this.api.seekTime(time);
|
||||
}
|
||||
|
||||
togglePlayback(to_play) {
|
||||
if (to_play) {
|
||||
this.api.play();
|
||||
} else {
|
||||
this.api.pause();
|
||||
}
|
||||
}
|
||||
|
||||
setPlaybackRate(speed) {
|
||||
this.api.playbackRate = speed;
|
||||
}
|
||||
|
||||
shuffleArray(array) {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[array[i], array[j]] = [array[j], array[i]];
|
||||
}
|
||||
}
|
||||
|
||||
// snackbar helper
|
||||
public openSnackBar(message: string, action: string) {
|
||||
|
||||
Reference in New Issue
Block a user