mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-11 23:30:58 +03:00
Audio downloads now work with progress bar, but it requires file conversion at the end. It ends up being around the same speed as the regular method
This commit is contained in:
@@ -5,6 +5,7 @@ var auth_api = require('./authentication/auth');
|
||||
var winston = require('winston');
|
||||
var path = require('path');
|
||||
var youtubedl = require('youtube-dl');
|
||||
var ffmpeg = require('fluent-ffmpeg');
|
||||
var compression = require('compression');
|
||||
var https = require('https');
|
||||
var express = require("express");
|
||||
@@ -1287,6 +1288,7 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
|
||||
}
|
||||
|
||||
download['complete'] = true;
|
||||
download['fileNames'] = is_playlist ? file_names : [full_file_path]
|
||||
updateDownloads();
|
||||
|
||||
var videopathEncoded = encodeURIComponent(file_names[0]);
|
||||
@@ -1305,7 +1307,11 @@ async function downloadFileByURL_normal(url, type, options, sessionID = null) {
|
||||
return new Promise(async resolve => {
|
||||
var date = Date.now();
|
||||
var file_uid = null;
|
||||
var fileFolderPath = type === 'audio' ? audioFolderPath : videoFolderPath;
|
||||
const is_audio = type === 'audio';
|
||||
const ext = is_audio ? '.mp3' : '.mp4';
|
||||
var fileFolderPath = is_audio ? audioFolderPath : videoFolderPath;
|
||||
|
||||
if (is_audio) options.skip_audio_args = true;
|
||||
|
||||
// prepend with user if needed
|
||||
let multiUserMode = null;
|
||||
@@ -1373,16 +1379,17 @@ async function downloadFileByURL_normal(url, type, options, sessionID = null) {
|
||||
}
|
||||
});
|
||||
|
||||
video.on('end', function() {
|
||||
video.on('end', async function() {
|
||||
let new_date = Date.now();
|
||||
let difference = (new_date - date)/1000;
|
||||
logger.debug(`Video download delay: ${difference} seconds.`);
|
||||
|
||||
download['timestamp_end'] = Date.now();
|
||||
download['fileNames'] = [removeFileExtension(video_info._filename) + ext];
|
||||
download['complete'] = true;
|
||||
updateDownloads();
|
||||
|
||||
// audio-only cleanup
|
||||
if (type === 'audio') {
|
||||
if (is_audio) {
|
||||
// filename fix
|
||||
video_info['_filename'] = removeFileExtension(video_info['_filename']) + '.mp3';
|
||||
|
||||
@@ -1393,6 +1400,12 @@ async function downloadFileByURL_normal(url, type, options, sessionID = null) {
|
||||
}
|
||||
let success = NodeID3.write(tags, video_info._filename);
|
||||
if (!success) logger.error('Failed to apply ID3 tag to audio file ' + video_info._filename);
|
||||
|
||||
const possible_webm_path = removeFileExtension(video_info['_filename']) + '.webm';
|
||||
const possible_mp4_path = removeFileExtension(video_info['_filename']) + '.mp4';
|
||||
// check if audio file is webm
|
||||
if (fs.existsSync(possible_webm_path)) await convertFileToMp3(possible_webm_path, video_info['_filename']);
|
||||
else if (fs.existsSync(possible_mp4_path)) await convertFileToMp3(possible_mp4_path, video_info['_filename']);
|
||||
}
|
||||
|
||||
// registers file in DB
|
||||
@@ -1409,7 +1422,7 @@ async function downloadFileByURL_normal(url, type, options, sessionID = null) {
|
||||
videopathEncoded = encodeURIComponent(removeFileExtension(base_file_name));
|
||||
|
||||
resolve({
|
||||
[(type === 'audio') ? 'audiopathEncoded' : 'videopathEncoded']: videopathEncoded,
|
||||
[is_audio ? 'audiopathEncoded' : 'videopathEncoded']: videopathEncoded,
|
||||
file_names: /*is_playlist ? file_names :*/ null, // playlist support is not ready
|
||||
uid: file_uid
|
||||
});
|
||||
@@ -1451,7 +1464,7 @@ async function generateArgs(url, type, options) {
|
||||
var youtubePassword = options.youtubePassword;
|
||||
|
||||
let downloadConfig = null;
|
||||
let qualityPath = is_audio ? '-f bestaudio' :'-f best[ext=mp4]';
|
||||
let qualityPath = (is_audio && !options.skip_audio_args) ? '-f bestaudio' :'-f best[ext=mp4]';
|
||||
|
||||
if (!is_audio && (url.includes('tiktok') || url.includes('pscp.tv'))) {
|
||||
// tiktok videos fail when using the default format
|
||||
@@ -1470,12 +1483,12 @@ async function generateArgs(url, type, options) {
|
||||
}
|
||||
|
||||
if (customOutput) {
|
||||
downloadConfig = ['-o', path.join(fileFolderPath, customOutput), qualityPath, '--write-info-json', '--print-json'];
|
||||
downloadConfig = ['-o', path.join(fileFolderPath, customOutput) + ".%(ext)s", qualityPath, '--write-info-json', '--print-json'];
|
||||
} else {
|
||||
downloadConfig = ['-o', path.join(fileFolderPath, videopath + (is_audio ? '.%(ext)s' : '.mp4')), qualityPath, '--write-info-json', '--print-json'];
|
||||
}
|
||||
|
||||
if (is_audio) {
|
||||
if (is_audio && !options.skip_audio_args) {
|
||||
downloadConfig.push('-x');
|
||||
downloadConfig.push('--audio-format', 'mp3');
|
||||
}
|
||||
@@ -1519,6 +1532,8 @@ async function generateArgs(url, type, options) {
|
||||
}
|
||||
|
||||
}
|
||||
// downloadConfig.map((arg) => `"${arg}"`);
|
||||
console.log(downloadConfig.toString())
|
||||
resolve(downloadConfig);
|
||||
});
|
||||
}
|
||||
@@ -1550,6 +1565,23 @@ async function getUrlInfos(urls) {
|
||||
});
|
||||
}
|
||||
|
||||
async function convertFileToMp3(input_file, output_file) {
|
||||
logger.verbose(`Converting ${input_file} to ${output_file}...`);
|
||||
return new Promise(resolve => {
|
||||
ffmpeg(input_file).noVideo().toFormat('mp3')
|
||||
.on('end', () => {
|
||||
logger.verbose(`Conversion for '${output_file}' complete.`);
|
||||
fs.unlinkSync(input_file)
|
||||
resolve(true);
|
||||
})
|
||||
.on('error', (err) => {
|
||||
logger.error('Failed to convert audio file to the correct format.');
|
||||
logger.error(err);
|
||||
resolve(false);
|
||||
}).save(output_file);
|
||||
});
|
||||
}
|
||||
|
||||
function writeToBlacklist(type, line) {
|
||||
let blacklistPath = path.join(archivePath, (type === 'audio') ? 'blacklist_audio.txt' : 'blacklist_video.txt');
|
||||
// adds newline to the beginning of the line
|
||||
@@ -1821,7 +1853,7 @@ app.post('/api/tomp3', optionalJwt, async function(req, res) {
|
||||
}
|
||||
|
||||
const is_playlist = url.includes('playlist');
|
||||
if (true || is_playlist)
|
||||
if (is_playlist || options.customQualityConfiguration )
|
||||
result_obj = await downloadFileByURL_exec(url, 'audio', options, req.query.sessionID);
|
||||
else
|
||||
result_obj = await downloadFileByURL_normal(url, 'audio', options, req.query.sessionID);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"config": "^3.2.3",
|
||||
"exe": "^1.0.2",
|
||||
"express": "^4.17.1",
|
||||
"fluent-ffmpeg": "^2.1.2",
|
||||
"fs-extra": "^9.0.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"lowdb": "^1.0.0",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</h4>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div *ngFor="let download of session_downloads.value | keyvalue; let i = index;" class="col-12 my-1">
|
||||
<div *ngFor="let download of session_downloads.value | keyvalue: sort_downloads; let i = index;" class="col-12 my-1">
|
||||
<mat-card *ngIf="download.value" class="mat-elevation-z3">
|
||||
<app-download-item [download]="download.value" [queueNumber]="i+1" (cancelDownload)="clearDownload(session_downloads.key, download.value.uid)"></app-download-item>
|
||||
</mat-card>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, ViewChildren, QueryList, ElementRef } from '@angular/core';
|
||||
import { Component, OnInit, ViewChildren, QueryList, ElementRef, OnDestroy } from '@angular/core';
|
||||
import { PostsService } from 'app/posts.services';
|
||||
import { trigger, transition, animateChild, stagger, query, style, animate } from '@angular/animations';
|
||||
import { Router } from '@angular/router';
|
||||
@@ -32,20 +32,26 @@ import { Router } from '@angular/router';
|
||||
])
|
||||
],
|
||||
})
|
||||
export class DownloadsComponent implements OnInit {
|
||||
export class DownloadsComponent implements OnInit, OnDestroy {
|
||||
|
||||
downloads_check_interval = 500;
|
||||
downloads_check_interval = 1000;
|
||||
downloads = {};
|
||||
interval_id = null;
|
||||
|
||||
keys = Object.keys;
|
||||
|
||||
valid_sessions_length = 0;
|
||||
|
||||
sort_downloads = (a, b) => {
|
||||
const result = a.value.timestamp_start < b.value.timestamp_start;
|
||||
return result;
|
||||
}
|
||||
|
||||
constructor(public postsService: PostsService, private router: Router) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getCurrentDownloads();
|
||||
setInterval(() => {
|
||||
this.interval_id = setInterval(() => {
|
||||
this.getCurrentDownloads();
|
||||
}, this.downloads_check_interval);
|
||||
|
||||
@@ -58,6 +64,10 @@ export class DownloadsComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.interval_id) { clearInterval(this.interval_id) }
|
||||
}
|
||||
|
||||
getCurrentDownloads() {
|
||||
this.postsService.getCurrentDownloads().subscribe(res => {
|
||||
if (res['downloads']) {
|
||||
|
||||
@@ -12,10 +12,30 @@
|
||||
<button style="margin-bottom: 2px;" (click)="cancelTheDownload()" mat-icon-button color="warn"><mat-icon fontSet="material-icons-outlined">cancel</mat-icon></button>
|
||||
</mat-grid-tile>
|
||||
</mat-grid-list>
|
||||
<mat-expansion-panel class="ignore-margin" *ngIf="download.error">
|
||||
<mat-expansion-panel *ngIf="download.timestamp_start" class="ignore-margin">
|
||||
<mat-expansion-panel-header>
|
||||
Error
|
||||
<div>
|
||||
<ng-container i18n="Details">Details</ng-container>
|
||||
</div>
|
||||
<div style="width: 100%">
|
||||
<div style="float: right">
|
||||
<mat-panel-description>{{download.timestamp_start | date:'medium'}}</mat-panel-description>
|
||||
</div>
|
||||
</div>
|
||||
</mat-expansion-panel-header>
|
||||
{{download.error}}
|
||||
<div *ngIf="download.error">
|
||||
<strong>An error has occured:</strong>
|
||||
<br/>
|
||||
{{download.error}}
|
||||
</div>
|
||||
<div *ngIf="download.timestamp_start">
|
||||
<strong>Download start: </strong>{{download.timestamp_start | date:'medium'}}
|
||||
</div>
|
||||
<div *ngIf="download.timestamp_end">
|
||||
<strong>Download end: </strong> {{download.timestamp_end | date:'medium'}}
|
||||
</div>
|
||||
<div *ngIf="download.fileNames">
|
||||
<strong>File path(s): </strong> {{download.fileNames.join(', ')}}
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</div>
|
||||
@@ -16,6 +16,8 @@ export class DownloadItemComponent implements OnInit {
|
||||
complete: false,
|
||||
url: 'http://youtube.com/watch?v=17848rufj',
|
||||
downloading: true,
|
||||
timestamp_start: null,
|
||||
timestamp_end: null,
|
||||
is_playlist: false,
|
||||
error: false
|
||||
};
|
||||
|
||||
@@ -36,6 +36,8 @@ export interface Download {
|
||||
error: boolean | string;
|
||||
fileNames?: string[];
|
||||
complete?: boolean;
|
||||
timestamp_start?: number;
|
||||
timestamp_end?: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
||||
Reference in New Issue
Block a user