Subversion Repositories yt_downloader

Rev

Rev 5 | Rev 7 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 5 Rev 6
Line 1... Line 1...
1
#!/usr/bin/php
1
#!/usr/bin/php
2
<?php
2
<?php
3
 
3
 
4
// ViaThinkSoft YouTube Downloader Util 2.1.1
4
// ViaThinkSoft YouTube Downloader Util 2.2
5
// Revision: 2019-08-05
5
// Revision: 2020-07-25
6
// Author: Daniel Marschall <www.daniel-marschall.de>
6
// Author: Daniel Marschall <www.daniel-marschall.de>
7
// Licensed under the terms of the Apache 2.0 license
7
// Licensed under the terms of the Apache 2.0 license
8
//
8
//
9
// Syntax:
-
 
10
// ./ytdwn [-t|--type v:[ext]|a:[ext]] (default v:)
-
 
11
//         [-o|--outputDir <dir>]      (default current working directory)
-
 
12
//         [-a|--alreadyDownloaded <file>]
-
 
13
//         [-f|--failList <file> <treshold>]  (This file logs failures)
-
 
14
//         [-F|--failTreshold <num>]   (Don't download if failure (-f) treshold is reached. Default: 3)
-
 
15
//         [-V|--version]              (shows version)
-
 
16
//         [-v|--verbose]              (displays verbose information to STDOUT)
-
 
17
//         [-h|--help]                 (shows help)
-
 
18
//         [-N|--no-mp3-tagtransfer]   (disables transfer of video ID to MP3 ID tag)
-
 
19
//                                     (This feature requires the package "id3v2")
-
 
20
//         [-T|--default-template <t>] (Sets default filename template.)
-
 
21
//                                     (Default: '%(title)s-%(id)s.%(ext)s')
-
 
22
//         [-X|--extra-args <args>]    (Additional arguments passed through)
-
 
23
//                                     (youtube-dl. Default "-ic")
-
 
24
//         [-A|--api-key <file|key>]   (specifies the API key, or a file containing the API key)
-
 
25
//                                     (Default: ~/.yt_api_key)
-
 
26
//         [-C|--resultcache <file>]   (allows video results to be cached in this file)
-
 
27
//                                     (only for playlists or channels)
-
 
28
//         [-O|--create-outputdir]     (allows creation of the output directories, recursively)
-
 
29
//         [--]
-
 
30
//         <resource> [<resource> ...]
-
 
31
//
-
 
32
// For all paths (outputDir, alreadyDownloaded, apikey, failList and resultcache), you can use the
-
 
33
// term '[listname]' which will be replaced by the basename of the current list file (without file extension).
-
 
34
// For example you can do following:
-
 
35
//         ./ytdwn -o 'downloads/[listname]' -- list:*.list
-
 
36
// If no list file is processed, it will be replaced with nothing.
-
 
37
//
-
 
38
// The "alreadyDownloaded" argument contains a file which will be managed by ytdwn.
-
 
39
// It will contain all video IDs which have been downloaded. This allows you to
-
 
40
// move away the already downloaded files, and ytdwn will not download them again.
-
 
41
//
-
 
42
// Examples for type:
-
 
43
//         v:         best video quality
-
 
44
//         a:         best audio only
-
 
45
//         a:mp3      audio only, mp3
-
 
46
//         Valid audio formats according to "man youtube-dl":
-
 
47
//         "best", "aac", "flac", "mp3", "m4a", "opus", "vorbis", or "wav"; "best" by default
-
 
48
//
-
 
49
// A <resource> can be one of the following:
-
 
50
//         vid:<video ID>
-
 
51
//         vurl:<youtube video URL>
-
 
52
//         pid:<playlist ID>
-
 
53
//         purl:<playlist URL>
-
 
54
//         cid:<channel id>
-
 
55
//         cname:<channel name>
-
 
56
//         curl:<channel or username URL>
-
 
57
//         list:<file with resource entries>   (comments can be #)
-
 
58
//         search:<searchterm>
-
 
59
//
-
 
60
// For channels (cid, cname, curl) you can also perform a search to filter the results.
-
 
61
// This can be done like this:
-
 
62
//         cname:[search="Elvis Presley"]channel_1234
-
 
63
// For the search option, following parameters are possible:
9
// For syntax and other documentation, please read the file README.
64
//         search:[order=date][maxresults=50]"Elvis Presley"
-
 
65
//         Acceptable order values are: date, rating, relevance, title, videoCount, viewCount
-
 
66
//         Default values are order=relevance and maxresults=10
-
 
67
//         Use maxresults=-1 to download everything which matches the searchterm.
-
 
68
//
-
 
69
// Requirements:
-
 
70
//         - PHP CLI
-
 
71
//         - Package "youtube-dl" (ytdwn will try to download it automatically, if possible)
-
 
72
//         - A YouTube API key (can be obtained here: https://console.developers.google.com/apis/credentials )
-
 
73
//         - If you want to extract audio, you need additionally: ffmpeg or avconv and ffprobe or avprobe.
-
 
74
//         - Optional: package "id3v2" to allow the YouTube video id to be transferred to the MP3 ID tag
-
 
75
 
10
 
76
// ------------------------------------------------------------------------------------------------
11
// ------------------------------------------------------------------------------------------------
77
 
12
 
78
error_reporting(E_ALL | E_NOTICE | E_STRICT | E_DEPRECATED);
13
error_reporting(E_ALL | E_NOTICE | E_STRICT | E_DEPRECATED);
79
 
14
 
80
define('AUTO_API_KEY', '~/.yt_api_key');
15
define('AUTO_API_KEY', '~/.yt_api_key');
-
 
16
define('AUTO_COOKIE_FILE', '~/.yt_cookies');
81
define('DOWNLOAD_SIMULATION_MODE', false);
17
define('DOWNLOAD_SIMULATION_MODE', false);
82
define('DEFAULT_SEARCH_ORDER', 'relevance');
18
define('DEFAULT_SEARCH_ORDER', 'relevance');
83
define('DEFAULT_SEARCH_MAXRESULTS', 10);
19
define('DEFAULT_SEARCH_MAXRESULTS', 10);
84
 
20
 
85
putenv("LANG=de_DE.UTF-8"); // required if video titles contain non-ASCII symbols
21
putenv("LANG=de_DE.UTF-8"); // required if video titles contain non-ASCII symbols
Line 113... Line 49...
113
$extra_args =
49
$extra_args =
114
//            '-k ' . // The additional "-k" option in the above makes youtube-dl keep downloaded videos.
50
//            '-k ' . // The additional "-k" option in the above makes youtube-dl keep downloaded videos.
115
              '-i ' . // continue upon download errors
51
              '-i ' . // continue upon download errors
116
              '-c ';  // resume partially downloaded video files
52
              '-c ';  // resume partially downloaded video files
117
$default_template = '%(title)s-%(id)s.%(ext)s';
53
$default_template = '%(title)s-%(id)s.%(ext)s';
-
 
54
$cookie_file = AUTO_COOKIE_FILE;
118
 
55
 
119
// Parse arguments
56
// Parse arguments
120
// We do not use getopt() at the moment, because the important functionality "optind" is only available in PHP 7.1, which is not yet distributed with most of the stable Linux distros
57
// We do not use getopt() at the moment, because the important functionality "optind" is only available in PHP 7.1, which is not yet distributed with most of the stable Linux distros
121
 
58
 
122
$init_extra_args = false;
59
$init_extra_args = false;
Line 155... Line 92...
155
		$default_template = $m[3];
92
		$default_template = $m[3];
156
	} else if (preg_match('@^(/A|\-A|\-\-api-key)(\s+|=)(.*)$@s', $arg2, $m)) {
93
	} else if (preg_match('@^(/A|\-A|\-\-api-key)(\s+|=)(.*)$@s', $arg2, $m)) {
157
		array_shift($argv_bak);
94
		array_shift($argv_bak);
158
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
95
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
159
		$apikey = file_exists($m[3]) ? trim(file_get_contents($m[3])) : $m[3];
96
		$apikey = file_exists($m[3]) ? trim(file_get_contents($m[3])) : $m[3];
-
 
97
	} else if (preg_match('@^(\-\-cookies)(\s+|=)(.*)$@s', $arg2, $m)) {
-
 
98
		array_shift($argv_bak);
-
 
99
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
-
 
100
		$cookie_file = file_exists($m[3]) ? trim(file_get_contents($m[3])) : $m[3];
160
	} else if (preg_match('@^(/X|\-X|\-\-extra-args)(\s+|=)(.*)$@s', $arg2, $m)) {
101
	} else if (preg_match('@^(/X|\-X|\-\-extra-args)(\s+|=)(.*)$@s', $arg2, $m)) {
161
		array_shift($argv_bak);
102
		array_shift($argv_bak);
162
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
103
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
163
		if ($init_extra_args) {
104
		if ($init_extra_args) {
164
			$extra_args .= ' ' . $m[3]; // user has multiple "-X" arguments. append.
105
			$extra_args .= ' ' . $m[3]; // user has multiple "-X" arguments. append.
Line 199... Line 140...
199
 
140
 
200
if (count($rest_args) == 0) syntax_error("Please enter at least one desired video for downloading");
141
if (count($rest_args) == 0) syntax_error("Please enter at least one desired video for downloading");
201
 
142
 
202
if ($failTreshold <= 0) syntax_error("Fail treshold has invalid value. Must be >0.");
143
if ($failTreshold <= 0) syntax_error("Fail treshold has invalid value. Must be >0.");
203
 
144
 
-
 
145
$cookie_file = expand_tilde($cookie_file);
-
 
146
if (!file_exists($cookie_file)) $cookie_file = '';
-
 
147
 
204
// Try to download/update youtube-dl into local directory
148
// Try to download/update youtube-dl into local directory
205
 
149
 
206
$newest_version_md5 = get_latest_ytdl_md5sum();
150
$newest_version_md5 = get_latest_ytdl_md5sum();
207
if (!$newest_version_md5) {
151
if (!$newest_version_md5) {
208
	fwrite(STDERR, "Failed to get MD5 sum of latest version of 'youtube-dl' from GitHub. Will not try to download/update 'youtube-dl' into local directory.\n");
152
	fwrite(STDERR, "Failed to get MD5 sum of latest version of 'youtube-dl' from GitHub. Will not try to download/update 'youtube-dl' into local directory.\n");
Line 431... Line 375...
431
		fwrite(STDERR, "Cannot write result cache\n");
375
		fwrite(STDERR, "Cannot write result cache\n");
432
	}
376
	}
433
 
377
 
434
	// Now download
378
	// Now download
435
 
379
 
-
 
380
	if (!$out[$key]['results']) {
-
 
381
		fwrite(STDERR, "Cannot get result for channel with ID '$channel_id'\n");
-
 
382
		return;
-
 
383
	}
436
	foreach ($out[$key]['results'] as list($id, $title)) {
384
	foreach ($out[$key]['results'] as list($id, $title)) {
437
		if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n";
385
		if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n";
438
		ytdwn_video_id($id);
386
		ytdwn_video_id($id);
439
	}
387
	}
440
}
388
}
Line 481... Line 429...
481
		fwrite(STDERR, "Cannot write result cache\n");
429
		fwrite(STDERR, "Cannot write result cache\n");
482
	}
430
	}
483
 
431
 
484
	// Now download
432
	// Now download
485
 
433
 
-
 
434
	if (!$out[$key]['results']) {
-
 
435
		fwrite(STDERR, "Cannot get result for playlist with ID '$playlist_id'\n");
-
 
436
		return;
-
 
437
	}
486
	foreach ($out[$key]['results'] as list($id, $title)) {
438
	foreach ($out[$key]['results'] as list($id, $title)) {
487
		if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n";
439
		if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n";
488
		ytdwn_video_id($id);
440
		ytdwn_video_id($id);
489
	}
441
	}
490
}
442
}
Line 499... Line 451...
499
 
451
 
500
	$results = yt_search_items($search, $order, $maxresults);
452
	$results = yt_search_items($search, $order, $maxresults);
501
 
453
 
502
	// Now download
454
	// Now download
503
 
455
 
-
 
456
	if (!$results) {
-
 
457
		fwrite(STDERR, "Cannot get data for search '$search'\n");
-
 
458
		return;
-
 
459
	}
504
	foreach ($results as list($id, $title)) {
460
	foreach ($results as list($id, $title)) {
505
		if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n";
461
		if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n";
506
		ytdwn_video_id($id);
462
		ytdwn_video_id($id);
507
	}
463
	}
508
}
464
}
Line 512... Line 468...
512
	global $verbose;
468
	global $verbose;
513
	global $mp3id_transfer;
469
	global $mp3id_transfer;
514
	global $extra_args;
470
	global $extra_args;
515
	global $default_template;
471
	global $default_template;
516
	global $failTreshold;
472
	global $failTreshold;
-
 
473
	global $cookie_file;
517
 
474
 
518
	if (DOWNLOAD_SIMULATION_MODE) {
475
	if (DOWNLOAD_SIMULATION_MODE) {
519
		echo "SIMULATE download of video id $video_id as ".hf_type($type)." to "._getOutputDir()."\n";
476
		echo "SIMULATE download of video id $video_id as ".hf_type($type)." to "._getOutputDir()."\n";
520
		return;
477
		return;
521
	}
478
	}
Line 536... Line 493...
536
	$outputTemplate = rtrim(_getOutputDir(), '/').'/'.$default_template;
493
	$outputTemplate = rtrim(_getOutputDir(), '/').'/'.$default_template;
537
 
494
 
538
	if (substr($type,0,2) == 'v:') {
495
	if (substr($type,0,2) == 'v:') {
539
		$format = substr($type,2);
496
		$format = substr($type,2);
540
		if (!empty($format)) {
497
		if (!empty($format)) {
541
			exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.' '.escapeshellarg(vid_to_vurl($video_id)).' --format '.escapeshellarg($format), $out, $code);
498
			exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)).' --format '.escapeshellarg($format), $out, $code);
542
		} else {
499
		} else {
543
			exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.' '.escapeshellarg(vid_to_vurl($video_id)), $out, $code);
500
			exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)), $out, $code);
544
		}
501
		}
545
	} else if (substr($type,0,2) == 'a:') {
502
	} else if (substr($type,0,2) == 'a:') {
546
		$format = substr($type,2);
503
		$format = substr($type,2);
547
		if (!empty($format)) {
504
		if (!empty($format)) {
548
			exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.' '.escapeshellarg(vid_to_vurl($video_id)).' --extract-audio --audio-format '.escapeshellarg($format), $out, $code);
505
			exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)).' --extract-audio --audio-format '.escapeshellarg($format), $out, $code);
549
		} else {
506
		} else {
550
			exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.' '.escapeshellarg(vid_to_vurl($video_id)).' --extract-audio', $out, $code);
507
			exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)).' --extract-audio', $out, $code);
551
		}
508
		}
552
		if (($mp3id_transfer) && ($format == 'mp3')) mp3_transfer_vid_to_id();
509
		if (($mp3id_transfer) && ($format == 'mp3')) mp3_transfer_vid_to_id();
553
	} else assert(false);
510
	} else assert(false);
554
 
511
 
555
	if ($code == 0) {
512
	if ($code == 0) {