mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-04-15 21:31:28 +03:00
Added ability to crop files
Fixed bug in downloading playlists
This commit is contained in:
@@ -853,7 +853,7 @@ async function createPlaylistZipFile(fileNames, type, outputName, fullPathProvid
|
|||||||
let zipFolderPath = null;
|
let zipFolderPath = null;
|
||||||
|
|
||||||
if (!fullPathProvided) {
|
if (!fullPathProvided) {
|
||||||
zipFolderPath = path.join(__dirname, (type === 'audio') ? audioFolderPath : videoFolderPath);
|
zipFolderPath = (type === 'audio') ? audioFolderPath : videoFolderPath
|
||||||
if (user_uid) zipFolderPath = path.join(config_api.getConfigItem('ytdl_users_base_path'), user_uid, zipFolderPath);
|
if (user_uid) zipFolderPath = path.join(config_api.getConfigItem('ytdl_users_base_path'), user_uid, zipFolderPath);
|
||||||
} else {
|
} else {
|
||||||
zipFolderPath = path.join(__dirname, config_api.getConfigItem('ytdl_subscriptions_base_path'));
|
zipFolderPath = path.join(__dirname, config_api.getConfigItem('ytdl_subscriptions_base_path'));
|
||||||
@@ -1155,7 +1155,7 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// download file
|
// download file
|
||||||
youtubedl.exec(url, downloadConfig, {}, function(err, output) {
|
youtubedl.exec(url, downloadConfig, {}, async function(err, output) {
|
||||||
if (download_checker) clearInterval(download_checker); // stops the download checker from running as the download finished (or errored)
|
if (download_checker) clearInterval(download_checker); // stops the download checker from running as the download finished (or errored)
|
||||||
|
|
||||||
download['downloading'] = false;
|
download['downloading'] = false;
|
||||||
@@ -1227,8 +1227,12 @@ async function downloadFileByURL_exec(url, type, options, sessionID = null) {
|
|||||||
const file_path = options.noRelativePath ? path.basename(full_file_path) : full_file_path.substring(fileFolderPath.length, full_file_path.length);
|
const file_path = options.noRelativePath ? path.basename(full_file_path) : full_file_path.substring(fileFolderPath.length, full_file_path.length);
|
||||||
const customPath = options.noRelativePath ? path.dirname(full_file_path).split(path.sep).pop() : null;
|
const customPath = options.noRelativePath ? path.dirname(full_file_path).split(path.sep).pop() : null;
|
||||||
|
|
||||||
|
if (options.cropFileSettings) {
|
||||||
|
await cropFile(full_file_path, options.cropFileSettings.cropFileStart, options.cropFileSettings.cropFileEnd, ext);
|
||||||
|
}
|
||||||
|
|
||||||
// registers file in DB
|
// registers file in DB
|
||||||
file_uid = db_api.registerFileDB(file_path, type, multiUserMode, null, customPath, category);
|
file_uid = db_api.registerFileDB(file_path, type, multiUserMode, null, customPath, category, options.cropFileSettings);
|
||||||
|
|
||||||
if (file_name) file_names.push(file_name);
|
if (file_name) file_names.push(file_name);
|
||||||
}
|
}
|
||||||
@@ -1587,6 +1591,8 @@ async function getUrlInfos(urls) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ffmpeg helper functions
|
||||||
|
|
||||||
async function convertFileToMp3(input_file, output_file) {
|
async function convertFileToMp3(input_file, output_file) {
|
||||||
logger.verbose(`Converting ${input_file} to ${output_file}...`);
|
logger.verbose(`Converting ${input_file} to ${output_file}...`);
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
@@ -1604,6 +1610,33 @@ async function convertFileToMp3(input_file, output_file) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function cropFile(file_path, start, end, ext) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const temp_file_path = `${file_path}.cropped${ext}`;
|
||||||
|
let base_ffmpeg_call = ffmpeg(file_path);
|
||||||
|
if (start) {
|
||||||
|
base_ffmpeg_call = base_ffmpeg_call.seekOutput(start);
|
||||||
|
}
|
||||||
|
if (end) {
|
||||||
|
base_ffmpeg_call = base_ffmpeg_call.duration(end - start);
|
||||||
|
}
|
||||||
|
base_ffmpeg_call
|
||||||
|
.on('end', () => {
|
||||||
|
logger.verbose(`Cropping for '${file_path}' complete.`);
|
||||||
|
fs.unlinkSync(file_path);
|
||||||
|
fs.moveSync(temp_file_path, file_path);
|
||||||
|
resolve(true);
|
||||||
|
})
|
||||||
|
.on('error', (err, test, test2) => {
|
||||||
|
logger.error(`Failed to crop ${file_path}.`);
|
||||||
|
logger.error(err);
|
||||||
|
resolve(false);
|
||||||
|
}).save(temp_file_path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// archive helper functions
|
||||||
|
|
||||||
async function writeToBlacklist(type, line) {
|
async function writeToBlacklist(type, line) {
|
||||||
let blacklistPath = path.join(archivePath, (type === 'audio') ? 'blacklist_audio.txt' : 'blacklist_video.txt');
|
let blacklistPath = path.join(archivePath, (type === 'audio') ? 'blacklist_audio.txt' : 'blacklist_video.txt');
|
||||||
// adds newline to the beginning of the line
|
// adds newline to the beginning of the line
|
||||||
@@ -1908,7 +1941,8 @@ app.post('/api/tomp4', optionalJwt, async function(req, res) {
|
|||||||
youtubeUsername: req.body.youtubeUsername,
|
youtubeUsername: req.body.youtubeUsername,
|
||||||
youtubePassword: req.body.youtubePassword,
|
youtubePassword: req.body.youtubePassword,
|
||||||
ui_uid: req.body.ui_uid,
|
ui_uid: req.body.ui_uid,
|
||||||
user: req.isAuthenticated() ? req.user.uid : null
|
user: req.isAuthenticated() ? req.user.uid : null,
|
||||||
|
cropFileSettings: req.body.cropFileSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
const safeDownloadOverride = config_api.getConfigItem('ytdl_safe_download_override') || config_api.globalArgsRequiresSafeDownload();
|
const safeDownloadOverride = config_api.getConfigItem('ytdl_safe_download_override') || config_api.globalArgsRequiresSafeDownload();
|
||||||
@@ -2666,7 +2700,7 @@ app.post('/api/downloadFile', optionalJwt, async (req, res) => {
|
|||||||
for (let i = 0; i < fileNames.length; i++) {
|
for (let i = 0; i < fileNames.length; i++) {
|
||||||
fileNames[i] = decodeURIComponent(fileNames[i]);
|
fileNames[i] = decodeURIComponent(fileNames[i]);
|
||||||
}
|
}
|
||||||
file = await createPlaylistZipFile(fileNames, type, outputName, fullPathProvided, req.body.uuid);
|
file = await createPlaylistZipFile(fileNames, type, outputName, fullPathProvided, req.body.uuid || req.user.uid);
|
||||||
if (!path.isAbsolute(file)) file = path.join(__dirname, file);
|
if (!path.isAbsolute(file)) file = path.join(__dirname, file);
|
||||||
}
|
}
|
||||||
res.sendFile(file, function (err) {
|
res.sendFile(file, function (err) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ function initialize(input_db, input_users_db, input_logger) {
|
|||||||
setLogger(input_logger);
|
setLogger(input_logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerFileDB(file_path, type, multiUserMode = null, sub = null, customPath = null, category = null) {
|
function registerFileDB(file_path, type, multiUserMode = null, sub = null, customPath = null, category = null, cropFileSettings = null) {
|
||||||
let db_path = null;
|
let db_path = null;
|
||||||
const file_id = utils.removeFileExtension(file_path);
|
const file_id = utils.removeFileExtension(file_path);
|
||||||
const file_object = generateFileObject(file_id, type, customPath || multiUserMode && multiUserMode.file_path, sub);
|
const file_object = generateFileObject(file_id, type, customPath || multiUserMode && multiUserMode.file_path, sub);
|
||||||
@@ -32,6 +32,11 @@ function registerFileDB(file_path, type, multiUserMode = null, sub = null, custo
|
|||||||
// if category exists, only include essential info
|
// if category exists, only include essential info
|
||||||
if (category) file_object['category'] = {name: category['name'], uid: category['uid']};
|
if (category) file_object['category'] = {name: category['name'], uid: category['uid']};
|
||||||
|
|
||||||
|
// modify duration
|
||||||
|
if (cropFileSettings) {
|
||||||
|
file_object['duration'] = (cropFileSettings.cropFileEnd || file_object.duration) - cropFileSettings.cropFileStart;
|
||||||
|
}
|
||||||
|
|
||||||
if (!sub) {
|
if (!sub) {
|
||||||
if (multiUserMode) {
|
if (multiUserMode) {
|
||||||
const user_uid = multiUserMode.user;
|
const user_uid = multiUserMode.user;
|
||||||
|
|||||||
@@ -124,6 +124,10 @@ mat-form-field.mat-form-field {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.advanced-input-time {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.edit-button {
|
.edit-button {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
top: -5px;
|
top: -5px;
|
||||||
|
|||||||
@@ -129,7 +129,7 @@
|
|||||||
</mat-hint>
|
</mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!youtubeAuthDisabledOverride" class="col-12 col-sm-6 mt-2">
|
<div *ngIf="!youtubeAuthDisabledOverride" class="col-12 col-sm-6 mt-3">
|
||||||
<mat-checkbox color="accent" [disabled]="current_download" (change)="youtubeAuthEnabledChanged($event)" [(ngModel)]="youtubeAuthEnabled" style="z-index: 999" [ngModelOptions]="{standalone: true}">
|
<mat-checkbox color="accent" [disabled]="current_download" (change)="youtubeAuthEnabledChanged($event)" [(ngModel)]="youtubeAuthEnabled" style="z-index: 999" [ngModelOptions]="{standalone: true}">
|
||||||
<ng-container i18n="Use authentication checkbox">
|
<ng-container i18n="Use authentication checkbox">
|
||||||
Use authentication
|
Use authentication
|
||||||
@@ -139,11 +139,28 @@
|
|||||||
<input [(ngModel)]="youtubeUsername" [ngModelOptions]="{standalone: true}" [disabled]="!youtubeAuthEnabled" matInput placeholder="Username" i18n-placeholder="YT Username placeholder">
|
<input [(ngModel)]="youtubeUsername" [ngModelOptions]="{standalone: true}" [disabled]="!youtubeAuthEnabled" matInput placeholder="Username" i18n-placeholder="YT Username placeholder">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!youtubeAuthDisabledOverride" class="col-12 col-sm-6 mt-2">
|
<div *ngIf="!youtubeAuthDisabledOverride" class="col-12 col-sm-6 mt-3">
|
||||||
<mat-form-field style="margin-top: 31px;" color="accent" class="advanced-input">
|
<mat-form-field style="margin-top: 31px;" color="accent" class="advanced-input">
|
||||||
<input [(ngModel)]="youtubePassword" type="password" [ngModelOptions]="{standalone: true}" [disabled]="!youtubeAuthEnabled" matInput placeholder="Password" i18n-placeholder="YT Password placeholder">
|
<input [(ngModel)]="youtubePassword" type="password" [ngModelOptions]="{standalone: true}" [disabled]="!youtubeAuthEnabled" matInput placeholder="Password" i18n-placeholder="YT Password placeholder">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12 col-sm-6 mt-3">
|
||||||
|
<mat-checkbox color="accent" [disabled]="current_download" [(ngModel)]="cropFile" style="z-index: 999" [ngModelOptions]="{standalone: true}">
|
||||||
|
<ng-container i18n="Crop video checkbox">
|
||||||
|
Crop file
|
||||||
|
</ng-container>
|
||||||
|
</mat-checkbox>
|
||||||
|
<mat-form-field color="accent" class="advanced-input">
|
||||||
|
<input [(ngModel)]="cropFileStart" type="number" [ngModelOptions]="{standalone: true}" [disabled]="!cropFile" matInput placeholder="Crop from (seconds)" i18n-placeholder="Crom from placeholder">
|
||||||
|
<span matSuffix i18n="Crop units">Seconds</span>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-6 mt-3">
|
||||||
|
<mat-form-field style="margin-top: 31px;" color="accent" class="advanced-input">
|
||||||
|
<input [(ngModel)]="cropFileEnd" type="number" [ngModelOptions]="{standalone: true}" [disabled]="!cropFile" matInput placeholder="Crop to (seconds)" i18n-placeholder="Crop to placeholder">
|
||||||
|
<span matSuffix i18n="Crop units">Seconds</span>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ export class MainComponent implements OnInit {
|
|||||||
youtubeAuthEnabled = false;
|
youtubeAuthEnabled = false;
|
||||||
youtubeUsername = null;
|
youtubeUsername = null;
|
||||||
youtubePassword = null;
|
youtubePassword = null;
|
||||||
|
cropFile = false;
|
||||||
|
cropFileStart = null;
|
||||||
|
cropFileEnd = null;
|
||||||
urlError = false;
|
urlError = false;
|
||||||
path = '';
|
path = '';
|
||||||
url = '';
|
url = '';
|
||||||
@@ -521,8 +524,17 @@ export class MainComponent implements OnInit {
|
|||||||
|
|
||||||
const customQualityConfiguration = this.getSelectedVideoFormat();
|
const customQualityConfiguration = this.getSelectedVideoFormat();
|
||||||
|
|
||||||
|
let cropFileSettings = null;
|
||||||
|
|
||||||
|
if (this.cropFile) {
|
||||||
|
cropFileSettings = {
|
||||||
|
cropFileStart: this.cropFileStart,
|
||||||
|
cropFileEnd: this.cropFileEnd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.postsService.makeMP4(this.url, (this.selectedQuality === '' ? null : this.selectedQuality),
|
this.postsService.makeMP4(this.url, (this.selectedQuality === '' ? null : this.selectedQuality),
|
||||||
customQualityConfiguration, customArgs, customOutput, youtubeUsername, youtubePassword, new_download.uid).subscribe(posts => {
|
customQualityConfiguration, customArgs, customOutput, youtubeUsername, youtubePassword, new_download.uid, cropFileSettings).subscribe(posts => {
|
||||||
// update download object
|
// update download object
|
||||||
new_download.downloading = false;
|
new_download.downloading = false;
|
||||||
new_download.percent_complete = 100;
|
new_download.percent_complete = 100;
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ export class PostsService implements CanActivate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tslint:disable-next-line: max-line-length
|
// tslint:disable-next-line: max-line-length
|
||||||
makeMP4(url: string, selectedQuality: string, customQualityConfiguration: string, customArgs: string = null, customOutput: string = null, youtubeUsername: string = null, youtubePassword: string = null, ui_uid = null) {
|
makeMP4(url: string, selectedQuality: string, customQualityConfiguration: string, customArgs: string = null, customOutput: string = null, youtubeUsername: string = null, youtubePassword: string = null, ui_uid = null, cropFileSettings = null) {
|
||||||
return this.http.post(this.path + 'tomp4', {url: url,
|
return this.http.post(this.path + 'tomp4', {url: url,
|
||||||
selectedHeight: selectedQuality,
|
selectedHeight: selectedQuality,
|
||||||
customQualityConfiguration: customQualityConfiguration,
|
customQualityConfiguration: customQualityConfiguration,
|
||||||
@@ -191,7 +191,8 @@ export class PostsService implements CanActivate {
|
|||||||
customOutput: customOutput,
|
customOutput: customOutput,
|
||||||
youtubeUsername: youtubeUsername,
|
youtubeUsername: youtubeUsername,
|
||||||
youtubePassword: youtubePassword,
|
youtubePassword: youtubePassword,
|
||||||
ui_uid: ui_uid}, this.httpOptions);
|
ui_uid: ui_uid,
|
||||||
|
cropFileSettings: cropFileSettings}, this.httpOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
killAllDownloads() {
|
killAllDownloads() {
|
||||||
|
|||||||
Reference in New Issue
Block a user