diff --git a/README.md b/README.md index f999524e..b415777d 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,6 @@ Please be aware that this app is only tested on windows, linux and macOS. If you ## Planned features - Select individual audio and video codecs (advanced mode) - List all audio qualities -- Support for downloading livestreams Feel free to [request a new feature](https://github.com/StefanLobbenmeier/youtube-dl-gui/issues). diff --git a/main.js b/main.js index e78c4b41..6883c2ab 100644 --- a/main.js +++ b/main.js @@ -151,6 +151,9 @@ function startCriticalHandlers(env) { case "stop": queryManager.stopDownload(args.identifier); break; + case "remove": + queryManager.removeDownload(args.identifier); + break; case "open": queryManager.openVideo(args); break; diff --git a/modules/QueryManager.js b/modules/QueryManager.js index 4d454e5c..d36a70c8 100644 --- a/modules/QueryManager.js +++ b/modules/QueryManager.js @@ -46,7 +46,8 @@ class QueryManager { this.removeVideo(metadataVideo); break; case "livestream": - this.environment.errorHandler.raiseError({code: "Not supported", description: "Livestreams are not yet supported."}, metadataVideo.identifier); + this.manageSingle(initialQuery, url, headers); + this.removeVideo(metadataVideo); break; default: //This.environment.errorHandler.raiseUnhandledError("Youtube-dl returned an empty object\n" + JSON.stringify(Utils.detectInfoType(initialQuery), null, 2), metadataVideo.identifier); @@ -357,7 +358,7 @@ class QueryManager { } } - stopDownload(identifier) { + removeDownload(identifier) { let video = this.getVideo(identifier); if (video.query != null) { video.query.cancel(); @@ -365,6 +366,13 @@ class QueryManager { this.removeVideo(video); } + stopDownload(identifier) { + let video = this.getVideo(identifier); + if (video.query != null) { + video.query.cancel(); + } + } + async openVideo(args) { let video = this.getVideo(args.identifier); let file = video.filename; diff --git a/modules/download/DownloadQuery.js b/modules/download/DownloadQuery.js index 5081c7ee..b24f2d8f 100644 --- a/modules/download/DownloadQuery.js +++ b/modules/download/DownloadQuery.js @@ -169,11 +169,26 @@ class DownloadQuery extends Query { let destinationCount = 0; let initialReset = false; let result = null; + const regexliverec = /size=\s*(\d+\w)iB\s*time=(\d+:\d+:\d+.\d+)\s*bitrate=\s*(\d+.\d+\w)bits\/s\s*speed=\d+.\d+x/; try { result = await this.environment.downloadLimiter.schedule(() => this.start(this.url, args, (liveData) => { this.environment.logger.log(this.video.identifier, liveData); this.video.setFilename(liveData); - + if(this.video.is_live) { + if (!initialReset) { + initialReset = true; + this.progressBar.reset(); + return; + } + try{ + const livrec = liveData.match(regexliverec); + if (typeof (`${livrec[1]}`) == "undefined") return; + this.progressBar.updateDownload('livestream', `${livrec[2]}`, `${livrec[3]}bits\/s`, this.video.audioOnly || this.video.downloadingAudio); + } catch(e) { + return; + } + return; + } if (!liveData.includes("[download]")) return; if (liveData.includes("Destination")) destinationCount += 1; diff --git a/modules/types/Query.js b/modules/types/Query.js index a54f1e23..bb4273a6 100644 --- a/modules/types/Query.js +++ b/modules/types/Query.js @@ -1,3 +1,4 @@ +const child_process = require('child_process'); const execa = require('execa'); const UserAgent = require('user-agents'); @@ -12,7 +13,9 @@ class Query { stop() { this.stopped = true; if(this.process != null) { - this.process.cancel(); + if (this.progressBar.video.is_live) + process.kill(this.process.pid, 'SIGINT');///Only way to stop ffmpeg lives + else this.process.kill(); } } @@ -79,7 +82,7 @@ class Query { //Return data while the query is running (live) //Return "done" when the query has finished return await new Promise((resolve) => { - this.process = execa(command, args); + this.process = child_process.spawn(command, args); this.process.stdout.setEncoding('utf8'); this.process.stdout.on('data', (data) => { const lines = data diff --git a/modules/types/Video.js b/modules/types/Video.js index 37caf84f..71763965 100644 --- a/modules/types/Video.js +++ b/modules/types/Video.js @@ -21,6 +21,7 @@ class Video { this.error = false; this.filename = null; this.identifier = Utils.getRandomID(32); + this.is_live = false; } setFilename(liveData) { @@ -120,6 +121,7 @@ class Video { this.formats = Utils.parseAvailableFormats(metadata); this.audioCodecs = Utils.parseAvailableAudioCodecs(metadata); this.selected_format_index = this.selectHighestQuality(); + this.is_live = (metadata.is_live != null && metadata.is_live === true); } selectHighestQuality() { diff --git a/renderer/renderer.js b/renderer/renderer.js index b0652d53..93aa5086 100644 --- a/renderer/renderer.js +++ b/renderer/renderer.js @@ -672,10 +672,11 @@ function removeVideo(card) { if(btn.hasClass("clicked") || $(card).find(".custom-select.download-type").is(":visible") || $(card).find(".btn.btn-dark.folder").is(":visible") || $(card).find(".row.error.d-none").is(":visible") || $(card).find(".url").length) { $(btn).popover('hide'); $(card).remove(); - window.main.invoke("videoAction", {action: "stop", identifier: $(card).prop("id")}); + window.main.invoke("videoAction", {action: "remove", identifier: $(card).prop("id")}); } else { $(btn).popover('show'); $(btn).addClass("clicked"); + window.main.invoke("videoAction", {action: "stop", identifier: $(card).prop("id")}); } }