diff --git a/backend/app.js b/backend/app.js index dea5e47..2a4ac56 100644 --- a/backend/app.js +++ b/backend/app.js @@ -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; diff --git a/backend/subscriptions.js b/backend/subscriptions.js index c9d3cc6..d185365 100644 --- a/backend/subscriptions.js +++ b/backend/subscriptions.js @@ -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 { + 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 } diff --git a/src/app/app.component.html b/src/app/app.component.html index 0eb8d77..057f745 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -30,8 +30,8 @@ - Home - Subscriptions + Home + Subscriptions diff --git a/src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.html b/src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.html index b0ccf22..fb967e2 100644 --- a/src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.html +++ b/src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.html @@ -1,7 +1,22 @@

{{sub.name}}

- Type: {{(sub.isPlaylist ? 'Playlist' : 'Channel')}} +
+ Type: + {{(sub.isPlaylist ? 'Playlist' : 'Channel')}} +
+
+ URL: + {{sub.url}} +
+
+ ID: + {{sub.id}} +
+
+ Archive: + {{sub.archive}} +
diff --git a/src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.scss b/src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.scss index e69de29..9feea64 100644 --- a/src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.scss +++ b/src/app/dialogs/subscription-info-dialog/subscription-info-dialog.component.scss @@ -0,0 +1,7 @@ +.info-item { + margin-bottom: 12px; +} + +.info-item-value { + font-size: 13px; +} \ No newline at end of file diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index baf9ab2..e769916 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -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}); } diff --git a/src/app/subscription/subscription-file-card/subscription-file-card.component.html b/src/app/subscription/subscription-file-card/subscription-file-card.component.html index da0a6ca..246ea91 100644 --- a/src/app/subscription/subscription-file-card/subscription-file-card.component.html +++ b/src/app/subscription/subscription-file-card/subscription-file-card.component.html @@ -1,9 +1,11 @@
+
+ Length: {{formattedDuration}} +
- - - + +
diff --git a/src/app/subscription/subscription-file-card/subscription-file-card.component.scss b/src/app/subscription/subscription-file-card/subscription-file-card.component.scss index a0e5f36..65cd869 100644 --- a/src/app/subscription/subscription-file-card/subscription-file-card.component.scss +++ b/src/app/subscription/subscription-file-card/subscription-file-card.component.scss @@ -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; } - } \ No newline at end of file + } diff --git a/src/app/subscription/subscription-file-card/subscription-file-card.component.ts b/src/app/subscription/subscription-file-card/subscription-file-card.component.ts index d17baa2..7a00bc5 100644 --- a/src/app/subscription/subscription-file-card/subscription-file-card.component.ts +++ b/src/app/subscription/subscription-file-card/subscription-file-card.component.ts @@ -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(); + @Output() reloadSubscription = new EventEmitter(); - 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; +} \ No newline at end of file diff --git a/src/app/subscription/subscription/subscription.component.html b/src/app/subscription/subscription/subscription.component.html index 9fa386b..8b6c635 100644 --- a/src/app/subscription/subscription/subscription.component.html +++ b/src/app/subscription/subscription/subscription.component.html @@ -13,7 +13,7 @@
- +
diff --git a/src/app/subscriptions/subscriptions.component.html b/src/app/subscriptions/subscriptions.component.html index 85ddb6b..80572fd 100644 --- a/src/app/subscriptions/subscriptions.component.html +++ b/src/app/subscriptions/subscriptions.component.html @@ -27,7 +27,7 @@

You have no channel subscriptions.

-

Playlists

+

Playlists