mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-07 20:10:03 +03:00
Added duration of video in subscription file card along with implementations of deleting subscribed videos. Subscribed videos now get reloaded after deletion
sidenav now closes when navigating Updated subscription info to include more info
This commit is contained in:
@@ -160,7 +160,11 @@ async function loadConfig() {
|
||||
|
||||
// get subscriptions
|
||||
if (allowSubscriptions) {
|
||||
// runs initially, then runs every ${subscriptionCheckInterval} seconds
|
||||
watchSubscriptions();
|
||||
setInterval(() => {
|
||||
watchSubscriptions();
|
||||
}, subscriptionsCheckInterval * 1000);
|
||||
}
|
||||
|
||||
// start the server here
|
||||
@@ -190,9 +194,7 @@ function watchSubscriptions() {
|
||||
let sub = subscriptions[i];
|
||||
console.log('watching ' + sub.name + ' with delay interval of ' + delay_interval);
|
||||
setTimeout(() => {
|
||||
setInterval(() => {
|
||||
subscriptions_api.getVideosForSub(sub);
|
||||
}, subscriptionsCheckInterval * 1000);
|
||||
subscriptions_api.getVideosForSub(sub);
|
||||
}, current_delay);
|
||||
current_delay += delay_interval;
|
||||
if (current_delay >= subscriptionsCheckInterval * 1000) current_delay = 0;
|
||||
@@ -415,10 +417,11 @@ function deleteAudioFile(name) {
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteVideoFile(name) {
|
||||
async function deleteVideoFile(name, customPath = null) {
|
||||
return new Promise(resolve => {
|
||||
var jsonPath = path.join(videoFolderPath,name+'.info.json');
|
||||
var videoFilePath = path.join(videoFolderPath,name+'.mp4');
|
||||
let filePath = customPath ? customPath : videoFolderPath;
|
||||
var jsonPath = path.join(filePath,name+'.info.json');
|
||||
var videoFilePath = path.join(filePath,name+'.mp4');
|
||||
jsonPath = path.join(__dirname, jsonPath);
|
||||
videoFilePath = path.join(__dirname, videoFilePath);
|
||||
|
||||
@@ -910,6 +913,23 @@ app.post('/api/unsubscribe', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/deleteSubscriptionFile', async (req, res) => {
|
||||
let deleteForever = req.body.deleteForever;
|
||||
let file = req.body.file;
|
||||
let sub = req.body.sub;
|
||||
|
||||
let success = await subscriptions_api.deleteSubscriptionFile(sub, file, deleteForever);
|
||||
|
||||
if (success) {
|
||||
res.send({
|
||||
success: success
|
||||
});
|
||||
} else {
|
||||
res.sendStatus(500);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
app.post('/api/getSubscription', async (req, res) => {
|
||||
let subID = req.body.id;
|
||||
|
||||
|
||||
@@ -64,6 +64,51 @@ async function unsubscribe(sub, deleteMode) {
|
||||
|
||||
}
|
||||
|
||||
async function deleteSubscriptionFile(sub, file, deleteForever) {
|
||||
const basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
||||
const useArchive = config_api.getConfigItem('ytdl_subscriptions_use_youtubedl_archive');
|
||||
const appendedBasePath = getAppendedBasePath(sub, basePath);
|
||||
const name = file;
|
||||
let retrievedID = null;
|
||||
return new Promise(resolve => {
|
||||
let filePath = appendedBasePath;
|
||||
var jsonPath = path.join(filePath,name+'.info.json');
|
||||
var videoFilePath = path.join(filePath,name+'.mp4');
|
||||
jsonPath = path.join(__dirname, jsonPath);
|
||||
videoFilePath = path.join(__dirname, videoFilePath);
|
||||
|
||||
jsonExists = fs.existsSync(jsonPath);
|
||||
videoFileExists = fs.existsSync(videoFilePath);
|
||||
|
||||
if (jsonExists) {
|
||||
retrievedID = JSON.parse(fs.readFileSync(jsonPath, 'utf8'))['id'];
|
||||
fs.unlinkSync(jsonPath);
|
||||
}
|
||||
|
||||
if (videoFileExists) {
|
||||
fs.unlink(videoFilePath, function(err) {
|
||||
if (fs.existsSync(jsonPath) || fs.existsSync(videoFilePath)) {
|
||||
resolve(false);
|
||||
} else {
|
||||
// check if the user wants the video to be redownloaded (deleteForever === false)
|
||||
if (!deleteForever && useArchive && sub.archive && retrievedID) {
|
||||
const archive_path = path.join(sub.archive, 'archive.txt')
|
||||
// if archive exists, remove line with video ID
|
||||
if (fs.existsSync(archive_path)) {
|
||||
removeIDFromArchive(archive_path, retrievedID);
|
||||
}
|
||||
}
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// TODO: tell user that the file didn't exist
|
||||
resolve(true);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
async function getVideosForSub(sub) {
|
||||
return new Promise(resolve => {
|
||||
const basePath = config_api.getConfigItem('ytdl_subscriptions_base_path');
|
||||
@@ -199,10 +244,38 @@ const deleteFolderRecursive = function(folder_to_delete) {
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getSubscription : getSubscription,
|
||||
getAllSubscriptions: getAllSubscriptions,
|
||||
subscribe : subscribe,
|
||||
unsubscribe : unsubscribe,
|
||||
getVideosForSub : getVideosForSub
|
||||
function removeIDFromArchive(archive_path, id) {
|
||||
fs.readFile(archive_path, {encoding: 'utf-8'}, function(err, data) {
|
||||
if (err) throw error;
|
||||
|
||||
let dataArray = data.split('\n'); // convert file data in an array
|
||||
const searchKeyword = id; // we are looking for a line, contains, key word id in the file
|
||||
let lastIndex = -1; // let say, we have not found the keyword
|
||||
|
||||
for (let index=0; index<dataArray.length; index++) {
|
||||
if (dataArray[index].includes(searchKeyword)) { // check if a line contains the id keyword
|
||||
lastIndex = index; // found a line includes a id keyword
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dataArray.splice(lastIndex, 1); // remove the keyword id from the data Array
|
||||
|
||||
// UPDATE FILE WITH NEW DATA
|
||||
const updatedData = dataArray.join('\n');
|
||||
fs.writeFile(archive_path, updatedData, (err) => {
|
||||
if (err) throw err;
|
||||
// console.log ('Successfully updated the file data');
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getSubscription : getSubscription,
|
||||
getAllSubscriptions : getAllSubscriptions,
|
||||
subscribe : subscribe,
|
||||
unsubscribe : unsubscribe,
|
||||
deleteSubscriptionFile : deleteSubscriptionFile,
|
||||
getVideosForSub : getVideosForSub
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
<mat-sidenav-container style="height: 100%">
|
||||
<mat-sidenav #sidenav>
|
||||
<mat-nav-list>
|
||||
<a mat-list-item routerLink='/home'>Home</a>
|
||||
<a mat-list-item routerLink='/subscriptions'>Subscriptions</a>
|
||||
<a mat-list-item (click)="sidenav.close()" routerLink='/home'>Home</a>
|
||||
<a mat-list-item (click)="sidenav.close()" routerLink='/subscriptions'>Subscriptions</a>
|
||||
</mat-nav-list>
|
||||
</mat-sidenav>
|
||||
<mat-sidenav-content [style.background]="postsService.theme ? postsService.theme.background_color : null">
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
<h4 mat-dialog-title>{{sub.name}}</h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<strong>Type:</strong> {{(sub.isPlaylist ? 'Playlist' : 'Channel')}}
|
||||
<div class="info-item">
|
||||
<strong>Type: </strong>
|
||||
<span class="info-item-value">{{(sub.isPlaylist ? 'Playlist' : 'Channel')}}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>URL: </strong>
|
||||
<span class="info-item-value">{{sub.url}}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<strong>ID: </strong>
|
||||
<span class="info-item-value">{{sub.id}}</span>
|
||||
</div>
|
||||
<div class="info-item" *ngIf="sub.archive">
|
||||
<strong>Archive: </strong>
|
||||
<span class="info-item-value">{{sub.archive}}</span>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.info-item {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.info-item-value {
|
||||
font-size: 13px;
|
||||
}
|
||||
@@ -149,6 +149,10 @@ export class PostsService {
|
||||
return this.http.post(this.path + 'unsubscribe', {sub: sub, deleteMode: deleteMode})
|
||||
}
|
||||
|
||||
deleteSubscriptionFile(sub, file, deleteForever) {
|
||||
return this.http.post(this.path + 'deleteSubscriptionFile', {sub: sub, file: file, deleteForever: deleteForever})
|
||||
}
|
||||
|
||||
getSubscription(id) {
|
||||
return this.http.post(this.path + 'getSubscription', {id: id});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<div style="position: relative; width: fit-content;">
|
||||
<div class="duration-time">
|
||||
Length: {{formattedDuration}}
|
||||
</div>
|
||||
<button [matMenuTriggerFor]="action_menu" class="menuButton" mat-icon-button><mat-icon>more_vert</mat-icon></button>
|
||||
<mat-menu #action_menu="matMenu">
|
||||
<button mat-menu-item><mat-icon>info</mat-icon>Info</button>
|
||||
<button mat-menu-item><mat-icon>restore</mat-icon>Delete and redownload</button>
|
||||
<button mat-menu-item><mat-icon>delete_forever</mat-icon>Delete forever</button>
|
||||
<button (click)="deleteAndRedownload()" mat-menu-item><mat-icon>restore</mat-icon>Delete and redownload</button>
|
||||
<button (click)="deleteForever()" mat-menu-item><mat-icon>delete_forever</mat-icon>Delete forever</button>
|
||||
</mat-menu>
|
||||
<mat-card (click)="goToFile(file.name)" matRipple class="example-card mat-elevation-z6">
|
||||
<div style="padding:5px">
|
||||
|
||||
@@ -55,6 +55,13 @@
|
||||
bottom: 5px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.duration-time {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
@media (max-width: 576px){
|
||||
|
||||
@@ -66,4 +73,4 @@
|
||||
width: 175px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { Router } from '@angular/router';
|
||||
import { PostsService } from 'app/posts.services';
|
||||
|
||||
@Component({
|
||||
selector: 'app-subscription-file-card',
|
||||
@@ -15,11 +16,15 @@ export class SubscriptionFileCardComponent implements OnInit {
|
||||
scrollSubject;
|
||||
scrollAndLoad;
|
||||
|
||||
formattedDuration = null;
|
||||
|
||||
@Input() file;
|
||||
@Input() sub;
|
||||
|
||||
@Output() goToFileEmit = new EventEmitter<any>();
|
||||
@Output() reloadSubscription = new EventEmitter<boolean>();
|
||||
|
||||
constructor(private snackBar: MatSnackBar) {
|
||||
constructor(private snackBar: MatSnackBar, private postsService: PostsService) {
|
||||
this.scrollSubject = new Subject();
|
||||
this.scrollAndLoad = Observable.merge(
|
||||
Observable.fromEvent(window, 'scroll'),
|
||||
@@ -28,7 +33,9 @@ export class SubscriptionFileCardComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
if (this.file.duration) {
|
||||
this.formattedDuration = fancyTimeFormat(this.file.duration);
|
||||
}
|
||||
}
|
||||
|
||||
onImgError(event) {
|
||||
@@ -47,6 +54,20 @@ export class SubscriptionFileCardComponent implements OnInit {
|
||||
this.goToFileEmit.emit(this.file.title);
|
||||
}
|
||||
|
||||
deleteAndRedownload() {
|
||||
this.postsService.deleteSubscriptionFile(this.sub, this.file.id, false).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).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,
|
||||
@@ -54,3 +75,22 @@ export class SubscriptionFileCardComponent implements OnInit {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div *ngFor="let file of files" class="col mb-4 sub-file-col">
|
||||
<app-subscription-file-card (goToFileEmit)="goToFile($event)" [file]="file"></app-subscription-file-card>
|
||||
<app-subscription-file-card (reloadSubscription)="getSubscription()" (goToFileEmit)="goToFile($event)" [file]="file" [sub]="subscription"></app-subscription-file-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<p>You have no channel subscriptions.</p>
|
||||
</div>
|
||||
|
||||
<h4 style="text-align: center;">Playlists</h4>
|
||||
<h4 style="text-align: center; margin-top: 10px;">Playlists</h4>
|
||||
<mat-nav-list class="sub-nav-list">
|
||||
<mat-list-item *ngFor="let sub of playlist_subscriptions">
|
||||
<a class="a-list-item" matLine (click)="goToSubscription(sub)" href="javascript:void(0)">
|
||||
|
||||
Reference in New Issue
Block a user