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
pull/29/head
Isaac Grynsztein 5 years ago
parent 4172b0b355
commit 881a103051

@ -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) {
}
};
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,
getVideosForSub : getVideosForSub
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)">

Loading…
Cancel
Save