Subversion Repositories yt_downloader

Rev

Rev 15 | Rev 17 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 15 Rev 16
1
#!/usr/bin/php
1
#!/usr/bin/php
2
<?php
2
<?php
3
 
3
 
4
// ViaThinkSoft YouTube Downloader Util 2.3
4
// ViaThinkSoft YouTube Downloader Util 2.3.1
5
// Revision: 2022-02-07
5
// Revision: 2022-02-07
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
// For syntax and other documentation, please read the file README.
9
// For syntax and other documentation, please read the file README.
10
 
10
 
11
// ------------------------------------------------------------------------------------------------
11
// ------------------------------------------------------------------------------------------------
12
 
12
 
13
error_reporting(E_ALL | E_NOTICE | E_STRICT | E_DEPRECATED);
13
error_reporting(E_ALL | E_NOTICE | E_STRICT | E_DEPRECATED);
14
 
14
 
15
define('AUTO_API_KEY', '~/.yt_api_key');
15
define('AUTO_API_KEY', '~/.yt_api_key');
16
define('AUTO_COOKIE_FILE', '~/.yt_cookies');
16
define('AUTO_COOKIE_FILE', '~/.yt_cookies');
17
define('DOWNLOAD_SIMULATION_MODE', false);
17
define('DOWNLOAD_SIMULATION_MODE', false);
18
define('DEFAULT_SEARCH_ORDER', 'relevance');
18
define('DEFAULT_SEARCH_ORDER', 'relevance');
19
define('DEFAULT_SEARCH_MAXRESULTS', 10);
19
define('DEFAULT_SEARCH_MAXRESULTS', 10);
20
 
20
 
21
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
22
 
22
 
23
require_once __DIR__ . '/youtube_functions.inc.phps';
23
require_once __DIR__ . '/youtube_functions.inc.phps';
24
require_once __DIR__ . '/checksum_functions.inc.phps';
24
require_once __DIR__ . '/checksum_functions.inc.phps';
25
 
25
 
26
// Check if we are running in command line
26
// Check if we are running in command line
27
 
27
 
28
if (PHP_SAPI !== 'cli') {
28
if (PHP_SAPI !== 'cli') {
29
	fwrite(STDERR, "Error: Can only run in CLI mode\n");
29
	fwrite(STDERR, "Error: Can only run in CLI mode\n");
30
	exit(2);
30
	exit(2);
31
}
31
}
32
 
32
 
33
// Global vars
33
// Global vars
34
 
34
 
35
$listFilenameStack = array();
35
$listFilenameStack = array();
36
 
36
 
37
// Default values
37
// Default values
38
 
38
 
39
$allow_creation_outputdir = false;
39
$allow_creation_outputdir = false;
40
$type = 'v:';
40
$type = 'v:';
41
$outputDir = '';
41
$outputDir = '';
42
$alreadyDownloaded = '';
42
$alreadyDownloaded = '';
43
$checksumMode = 'none';
43
$checksumMode = 'none';
44
$failList = '';
44
$failList = '';
45
$failTreshold = 3;
45
$failTreshold = 3;
46
$rest_args = array();
46
$rest_args = array();
47
$verbose = false;
47
$verbose = false;
48
$mp3id_transfer = true;
48
$mp3id_transfer = true;
49
$apikey = '';
49
$apikey = '';
50
$resultcache = '';
50
$resultcache = '';
51
$extra_args =
51
$extra_args =
52
//            '-k ' . // The additional "-k" option in the above makes youtube-dl keep downloaded videos.
52
//            '-k ' . // The additional "-k" option in the above makes youtube-dl keep downloaded videos.
53
              '-i ' . // continue upon download errors
53
              '-i ' . // continue upon download errors
54
              '-c ';  // resume partially downloaded video files
54
              '-c ';  // resume partially downloaded video files
55
$default_template = '%(title)s-%(id)s.%(ext)s';
55
$default_template = '%(title)s-%(id)s.%(ext)s';
56
$cookie_file = AUTO_COOKIE_FILE;
56
$cookie_file = AUTO_COOKIE_FILE;
-
 
57
$downloader = 'yt-dlp';
57
 
58
 
58
// Parse arguments
59
// Parse arguments
59
// 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
60
// 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
60
 
61
 
61
$init_extra_args = false;
62
$init_extra_args = false;
62
$argv_bak = $_SERVER['argv'];
63
$argv_bak = $_SERVER['argv'];
63
array_shift($argv_bak);
64
array_shift($argv_bak);
64
while (count($argv_bak) > 0) {
65
while (count($argv_bak) > 0) {
65
	$arg = array_shift($argv_bak);
66
	$arg = array_shift($argv_bak);
66
	$arg2 = $arg . ' ' . (isset($argv_bak[0]) ? $argv_bak[0] : '');
67
	$arg2 = $arg . ' ' . (isset($argv_bak[0]) ? $argv_bak[0] : '');
67
	$m = null;
68
	$m = null;
68
	if (preg_match('@^(/t|\-t|\-\-type)(\s+|=)(.*)$@s', $arg2, $m)) {
69
	if (preg_match('@^(/t|\-t|\-\-type)(\s+|=)(.*)$@s', $arg2, $m)) {
69
		array_shift($argv_bak);
70
		array_shift($argv_bak);
70
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
71
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
71
		$type = $m[3];
72
		$type = $m[3];
72
	} else if (preg_match('@^(/o|\-o|\-\-outputDir)(\s+|=)(.*)$@s', $arg2, $m)) {
73
	} else if (preg_match('@^(/o|\-o|\-\-outputDir)(\s+|=)(.*)$@s', $arg2, $m)) {
73
		array_shift($argv_bak);
74
		array_shift($argv_bak);
74
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
75
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
75
		$outputDir = $m[3];
76
		$outputDir = $m[3];
76
	} else if (preg_match('@^(/a|\-a|\-\-alreadyDownloaded)(\s+|=)(.*)$@s', $arg2, $m)) {
77
	} else if (preg_match('@^(/a|\-a|\-\-alreadyDownloaded)(\s+|=)(.*)$@s', $arg2, $m)) {
77
		array_shift($argv_bak);
78
		array_shift($argv_bak);
78
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
79
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
79
		$alreadyDownloaded = $m[3];
80
		$alreadyDownloaded = $m[3];
80
	} else if (preg_match('@^(/f|\-f|\-\-failList)(\s+|=)(.*)$@s', $arg2, $m)) {
81
	} else if (preg_match('@^(/f|\-f|\-\-failList)(\s+|=)(.*)$@s', $arg2, $m)) {
81
		array_shift($argv_bak);
82
		array_shift($argv_bak);
82
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
83
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
83
		$failList = $m[3];
84
		$failList = $m[3];
84
	} else if (preg_match('@^(/F|\-F|\-\-failTreshold)(\s+|=)(.*)$@s', $arg2, $m)) {
85
	} else if (preg_match('@^(/F|\-F|\-\-failTreshold)(\s+|=)(.*)$@s', $arg2, $m)) {
85
		array_shift($argv_bak);
86
		array_shift($argv_bak);
86
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
87
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
87
		$failTreshold = $m[3];
88
		$failTreshold = $m[3];
88
	} else if (preg_match('@^(/C|\-C|\-\-resultcache)(\s+|=)(.*)$@s', $arg2, $m)) {
89
	} else if (preg_match('@^(/C|\-C|\-\-resultcache)(\s+|=)(.*)$@s', $arg2, $m)) {
89
		array_shift($argv_bak);
90
		array_shift($argv_bak);
90
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
91
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
91
		$resultcache = $m[3];
92
		$resultcache = $m[3];
92
	} else if (preg_match('@^(/H|\-H|\-\-checksumMode)(\s+|=)(.*)$@s', $arg2, $m)) {
93
	} else if (preg_match('@^(/H|\-H|\-\-checksumMode)(\s+|=)(.*)$@s', $arg2, $m)) {
93
		array_shift($argv_bak);
94
		array_shift($argv_bak);
94
		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]);
95
		$checksumMode = $m[3];
96
		$checksumMode = $m[3];
96
		if ((strtolower($checksumMode) != 'none')
97
		if ((strtolower($checksumMode) != 'none')
97
			&& (strtolower($checksumMode) != 'sfv')
98
			&& (strtolower($checksumMode) != 'sfv')
98
			&& (strtolower($checksumMode) != 'md5')
99
			&& (strtolower($checksumMode) != 'md5')
99
			&& (strtolower($checksumMode) != 'sfv,md5')
100
			&& (strtolower($checksumMode) != 'sfv,md5')
100
			&& (strtolower($checksumMode) != 'md5,sfv')) syntax_error("Checksum mode needs to be either 'None', 'MD5', 'SFV', or 'MD5,SFV'.");
101
			&& (strtolower($checksumMode) != 'md5,sfv')) syntax_error("Checksum mode needs to be either 'None', 'MD5', 'SFV', or 'MD5,SFV'.");
101
	} else if (preg_match('@^(/T|\-T|\-\-default-template)(\s+|=)(.*)$@s', $arg2, $m)) {
102
	} else if (preg_match('@^(/T|\-T|\-\-default-template)(\s+|=)(.*)$@s', $arg2, $m)) {
102
		array_shift($argv_bak);
103
		array_shift($argv_bak);
103
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
104
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
104
		$default_template = $m[3];
105
		$default_template = $m[3];
105
	} else if (preg_match('@^(/A|\-A|\-\-api-key)(\s+|=)(.*)$@s', $arg2, $m)) {
106
	} else if (preg_match('@^(/A|\-A|\-\-api-key)(\s+|=)(.*)$@s', $arg2, $m)) {
106
		array_shift($argv_bak);
107
		array_shift($argv_bak);
107
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
108
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
108
		$apikey = file_exists($m[3]) ? trim(file_get_contents($m[3])) : $m[3];
109
		$apikey = file_exists($m[3]) ? trim(file_get_contents($m[3])) : $m[3];
109
	} else if (preg_match('@^(\-\-cookies)(\s+|=)(.*)$@s', $arg2, $m)) {
110
	} else if (preg_match('@^(\-\-cookies)(\s+|=)(.*)$@s', $arg2, $m)) {
110
		array_shift($argv_bak);
111
		array_shift($argv_bak);
111
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
112
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
112
		$cookie_file = file_exists($m[3]) ? trim(file_get_contents($m[3])) : $m[3];
113
		$cookie_file = file_exists($m[3]) ? trim(file_get_contents($m[3])) : $m[3];
-
 
114
	} else if (preg_match('@^(\-\-downloader)(\s+|=)(.*)$@s', $arg2, $m)) {
-
 
115
		array_shift($argv_bak);
-
 
116
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
-
 
117
		$downloader = $m[3];
113
	} else if (preg_match('@^(/X|\-X|\-\-extra-args)(\s+|=)(.*)$@s', $arg2, $m)) {
118
	} else if (preg_match('@^(/X|\-X|\-\-extra-args)(\s+|=)(.*)$@s', $arg2, $m)) {
114
		array_shift($argv_bak);
119
		array_shift($argv_bak);
115
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
120
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
116
		if ($init_extra_args) {
121
		if ($init_extra_args) {
117
			$extra_args .= ' ' . $m[3]; // user has multiple "-X" arguments. append.
122
			$extra_args .= ' ' . $m[3]; // user has multiple "-X" arguments. append.
118
		} else {
123
		} else {
119
			$extra_args = $m[3]; // overwrite defaults
124
			$extra_args = $m[3]; // overwrite defaults
120
			$init_extra_args = true;
125
			$init_extra_args = true;
121
		}
126
		}
122
	} else if (preg_match('@^(/\?|/h|\-\?|\-h|\-\-help)$@s', $arg, $m)) {
127
	} else if (preg_match('@^(/\?|/h|\-\?|\-h|\-\-help)$@s', $arg, $m)) {
123
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
128
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
124
		help();
129
		help();
125
	} else if (preg_match('@^(/V|\-V|\-\-version)$@s', $arg, $m)) {
130
	} else if (preg_match('@^(/V|\-V|\-\-version)$@s', $arg, $m)) {
126
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
131
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
127
		version();
132
		version();
128
	} else if (preg_match('@^(/v|\-v|\-\-verbose)$@s', $arg, $m)) {
133
	} else if (preg_match('@^(/v|\-v|\-\-verbose)$@s', $arg, $m)) {
129
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
134
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
130
		$verbose = true;
135
		$verbose = true;
131
	} else if (preg_match('@^(/N|\-N|\-\-no-mp3-tagtransfer)$@s', $arg, $m)) {
136
	} else if (preg_match('@^(/N|\-N|\-\-no-mp3-tagtransfer)$@s', $arg, $m)) {
132
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
137
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
133
		$mp3id_transfer = false;
138
		$mp3id_transfer = false;
134
	} else if (preg_match('@^(/O|\-O|\-\-create-outputdir)$@s', $arg, $m)) {
139
	} else if (preg_match('@^(/O|\-O|\-\-create-outputdir)$@s', $arg, $m)) {
135
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
140
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
136
		$allow_creation_outputdir = true;
141
		$allow_creation_outputdir = true;
137
	} else if ($arg == '--') {
142
	} else if ($arg == '--') {
138
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
143
		if (count($rest_args) > 0) syntax_error("Invalid argument: ".$rest_args[0]);
139
		$rest_args = $argv_bak;
144
		$rest_args = $argv_bak;
140
		break;
145
		break;
141
	} else {
146
	} else {
142
		$rest_args[] = $arg;
147
		$rest_args[] = $arg;
143
	}
148
	}
144
}
149
}
145
unset($arg);
150
unset($arg);
146
unset($argv_bak);
151
unset($argv_bak);
147
unset($init_extra_args);
152
unset($init_extra_args);
148
 
153
 
-
 
154
define('DOWNLOAD_YT_FORK', $downloader);
-
 
155
 
149
// Validity checks
156
// Validity checks
150
 
157
 
151
if ((substr($type,0,2) != 'a:') && (substr($type,0,2) != 'v:')) syntax_error("Type must be either 'v:' or 'a:'. '$type' is not valid.");
158
if ((substr($type,0,2) != 'a:') && (substr($type,0,2) != 'v:')) syntax_error("Type must be either 'v:' or 'a:'. '$type' is not valid.");
152
 
159
 
153
if (count($rest_args) == 0) syntax_error("Please enter at least one desired video for downloading");
160
if (count($rest_args) == 0) syntax_error("Please enter at least one desired video for downloading");
154
 
161
 
155
if ($failTreshold <= 0) syntax_error("Fail treshold has invalid value. Must be >0.");
162
if ($failTreshold <= 0) syntax_error("Fail treshold has invalid value. Must be >0.");
156
 
163
 
157
$cookie_file = expand_tilde($cookie_file);
164
$cookie_file = expand_tilde($cookie_file);
158
if (!file_exists($cookie_file)) $cookie_file = '';
165
if (!file_exists($cookie_file)) $cookie_file = '';
159
 
166
 
160
// Try to download/update youtube-dl into local directory
167
// Try to download/update youtube-dl/yt-dlp into local directory
161
 
-
 
162
$newest_version_md5 = get_latest_ytdl_md5sum();
-
 
163
if (!$newest_version_md5) {
168
download_latest_downloader();
164
	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");
-
 
165
} else {
-
 
166
	if (!file_exists(__DIR__.'/youtube-dl') || ($newest_version_md5 != md5_file(__DIR__.'/youtube-dl'))) {
-
 
167
		// Try to download/update the file in our directory. It should be the newest available, since YT often breaks downloader
-
 
168
		if (file_exists(__DIR__.'/youtube-dl')) {
-
 
169
			echo "Trying to update 'youtube-dl' in local directory...\n";
-
 
170
		} else {
-
 
171
			echo "Trying to download 'youtube-dl' into local directory...\n";
-
 
172
		}
-
 
173
 
-
 
174
		@chmod(__DIR__.'/youtube-dl', 0777); // otherwise we might not be able to write to it
-
 
175
 
169
 
176
		if (!($binary = file_get_contents('https://yt-dl.org/latest/youtube-dl'))) {
-
 
177
			fwrite(STDERR, "Failed to download 'youtube-dl' into local directory (file_get_contents).\n");
-
 
178
		} else if (!@file_put_contents(__DIR__.'/youtube-dl', $binary)) {
-
 
179
			fwrite(STDERR, "Failed to download 'youtube-dl' into local directory (file_put_contents).\n");
-
 
180
		} else {
-
 
181
			if (!@chmod(__DIR__.'/youtube-dl', 0544)) {
-
 
182
				fwrite(STDERR, "Failed to download 'youtube-dl' into local directory (chmod 544).\n");
-
 
183
				@unlink(__DIR__.'/youtube-dl'); // try to delete, otherwise we might try to execute a non-executable file
-
 
184
			}
-
 
185
		}
-
 
186
	}
-
 
187
}
-
 
188
 
-
 
189
if (command_exists(__DIR__.'/youtube-dl')) {
170
if (command_exists(__DIR__.'/'.DOWNLOAD_YT_FORK)) {
190
	echo "Will use 'youtube-dl' from local directory\n";
171
	echo "Will use '".DOWNLOAD_YT_FORK."' from local directory\n";
191
	define('YTDL_EXE', __DIR__.'/youtube-dl');
172
	define('YTDL_EXE', __DIR__.'/'.DOWNLOAD_YT_FORK);
192
} else {
173
} else {
193
	// Download failed. Is at least a package installed?
174
	// Download failed. Is at least a package installed?
194
	if (command_exists('youtube-dl')) {
175
	if (command_exists(DOWNLOAD_YT_FORK)) {
195
		echo "Will use 'youtube-dl' from Linux package\n";
176
		echo "Will use '".DOWNLOAD_YT_FORK."' from Linux package\n";
196
		define('YTDL_EXE', 'youtube-dl');
177
		define('YTDL_EXE', DOWNLOAD_YT_FORK);
197
	} else {
178
	} else {
198
		fwrite(STDERR, "This script requires the tool/package 'youtube-dl'. Please install it first.\n");
179
		fwrite(STDERR, "This script requires the tool/package '".DOWNLOAD_YT_FORK."'. Please install it first.\n");
199
		exit(1);
180
		exit(1);
200
	}
181
	}
201
}
182
}
202
 
183
 
203
// Now process the videos
184
// Now process the videos
204
 
185
 
205
yt_set_apikey_callback('_getApikey');
186
yt_set_apikey_callback('_getApikey');
206
 
187
 
207
foreach ($rest_args as $resource) {
188
foreach ($rest_args as $resource) {
208
	if ($verbose) echo "Handle: $resource\n";
189
	if ($verbose) echo "Handle: $resource\n";
209
	if (strpos($resource, ':') === false) {
190
	if (strpos($resource, ':') === false) {
210
		fwrite(STDERR, "Invalid resource '$resource' (you are missing the prefix, e.g. vurl: or vid:). Skipping.\n");
191
		fwrite(STDERR, "Invalid resource '$resource' (you are missing the prefix, e.g. vurl: or vid:). Skipping.\n");
211
	} else {
192
	} else {
212
		list($resourceType, $resourceValue) = explode(':', $resource, 2);
193
		list($resourceType, $resourceValue) = explode(':', $resource, 2);
213
		ytdwn_handle_resource($resourceType, $resourceValue);
194
		ytdwn_handle_resource($resourceType, $resourceValue);
214
	}
195
	}
215
}
196
}
216
 
197
 
217
// ------------------------------------------------------------------------------------------------
198
// ------------------------------------------------------------------------------------------------
218
 
199
 
219
function ytdwn_handle_resource($resourceType, $resourceValue) {
200
function ytdwn_handle_resource($resourceType, $resourceValue) {
220
	if ($resourceType == 'vid') {
201
	if ($resourceType == 'vid') {
221
		$video_id = parse_quoting($resourceValue);
202
		$video_id = parse_quoting($resourceValue);
222
		ytdwn_video_id($video_id);
203
		ytdwn_video_id($video_id);
223
	} else if ($resourceType == 'vurl') {
204
	} else if ($resourceType == 'vurl') {
224
		$video_url = parse_quoting($resourceValue);
205
		$video_url = parse_quoting($resourceValue);
225
		$video_id  = getVideoIDFromURL($video_url);
206
		$video_id  = getVideoIDFromURL($video_url);
226
		if (!$video_id) {
207
		if (!$video_id) {
227
			fwrite(STDERR, "$video_url is not a valid YouTube video URL. Skipping.\n");
208
			fwrite(STDERR, "$video_url is not a valid YouTube video URL. Skipping.\n");
228
		} else {
209
		} else {
229
			ytdwn_video_id($video_id);
210
			ytdwn_video_id($video_id);
230
		}
211
		}
231
	} else if ($resourceType == 'pid') {
212
	} else if ($resourceType == 'pid') {
232
		$playlist_id = parse_quoting($resourceValue);
213
		$playlist_id = parse_quoting($resourceValue);
233
		ytdwn_playlist_id($playlist_id);
214
		ytdwn_playlist_id($playlist_id);
234
	} else if ($resourceType == 'purl') {
215
	} else if ($resourceType == 'purl') {
235
		$playlist_url = parse_quoting($resourceValue);
216
		$playlist_url = parse_quoting($resourceValue);
236
		$playlist_id  = getPlaylistIDFromURL($playlist_url);
217
		$playlist_id  = getPlaylistIDFromURL($playlist_url);
237
		if (!$playlist_id) {
218
		if (!$playlist_id) {
238
			fwrite(STDERR, "$playlist_url is not a valid YouTube playlist URL. Skipping\n");
219
			fwrite(STDERR, "$playlist_url is not a valid YouTube playlist URL. Skipping\n");
239
		} else {
220
		} else {
240
			ytdwn_playlist_id($playlist_id);
221
			ytdwn_playlist_id($playlist_id);
241
		}
222
		}
242
	} else if ($resourceType == 'cid') {
223
	} else if ($resourceType == 'cid') {
243
		$channel_id = parse_quoting($resourceValue);
224
		$channel_id = parse_quoting($resourceValue);
244
 
225
 
245
		$m = null;
226
		$m = null;
246
		if (preg_match('@\[search=(.+)\]@ismU', $channel_id, $m)) {
227
		if (preg_match('@\[search=(.+)\]@ismU', $channel_id, $m)) {
247
			$search = $m[1];
228
			$search = $m[1];
248
			$channel_id = preg_replace('@\[search=(.+)\]@ismU', '', $channel_id);
229
			$channel_id = preg_replace('@\[search=(.+)\]@ismU', '', $channel_id);
249
		} else {
230
		} else {
250
			$search = ''; // default
231
			$search = ''; // default
251
		}
232
		}
252
		$search = parse_quoting($search);
233
		$search = parse_quoting($search);
253
 
234
 
254
		ytdwn_channel_id($channel_id, $search);
235
		ytdwn_channel_id($channel_id, $search);
255
	} else if ($resourceType == 'cname') {
236
	} else if ($resourceType == 'cname') {
256
		$channel_name = parse_quoting($resourceValue);
237
		$channel_name = parse_quoting($resourceValue);
257
 
238
 
258
		$m = null;
239
		$m = null;
259
		if (preg_match('@\[search=(.+)\]@ismU', $channel_name, $m)) {
240
		if (preg_match('@\[search=(.+)\]@ismU', $channel_name, $m)) {
260
			$search = $m[1];
241
			$search = $m[1];
261
			$channel_name = preg_replace('@\[search=(.+)\]@ismU', '', $channel_name);
242
			$channel_name = preg_replace('@\[search=(.+)\]@ismU', '', $channel_name);
262
		} else {
243
		} else {
263
			$search = ''; // default
244
			$search = ''; // default
264
		}
245
		}
265
		$search = parse_quoting($search);
246
		$search = parse_quoting($search);
266
 
247
 
267
		$channel_name = parse_quoting($channel_name);
248
		$channel_name = parse_quoting($channel_name);
268
		$channel_id = yt_get_channel_id($channel_name);
249
		$channel_id = yt_get_channel_id($channel_name);
269
		if (!$channel_id) {
250
		if (!$channel_id) {
270
			fwrite(STDERR, "URL $channel_name is a valid YouTube username. Will now try to interprete it as channel ID instead....\n");
251
			fwrite(STDERR, "URL $channel_name is a valid YouTube username. Will now try to interprete it as channel ID instead....\n");
271
		}
252
		}
272
		ytdwn_channel_id($channel_id, $search);
253
		ytdwn_channel_id($channel_id, $search);
273
	} else if ($resourceType == 'curl') {
254
	} else if ($resourceType == 'curl') {
274
		$channel_url = parse_quoting($resourceValue);
255
		$channel_url = parse_quoting($resourceValue);
275
 
256
 
276
		$m = null;
257
		$m = null;
277
		if (preg_match('@\[search=(.+)\]@ismU', $channel_url, $m)) {
258
		if (preg_match('@\[search=(.+)\]@ismU', $channel_url, $m)) {
278
			$search = $m[1];
259
			$search = $m[1];
279
			$channel_url = preg_replace('@\[search=(.+)\]@ismU', '', $channel_url);
260
			$channel_url = preg_replace('@\[search=(.+)\]@ismU', '', $channel_url);
280
		} else {
261
		} else {
281
			$search = ''; // default
262
			$search = ''; // default
282
		}
263
		}
283
		$search = parse_quoting($search);
264
		$search = parse_quoting($search);
284
 
265
 
285
		$channel_url = parse_quoting($channel_url);
266
		$channel_url = parse_quoting($channel_url);
286
		$channel_id = curl_to_cid($channel_url);
267
		$channel_id = curl_to_cid($channel_url);
287
		if (!$channel_id) {
268
		if (!$channel_id) {
288
			fwrite(STDERR, "URL $channel_url is a valid YouTube channel or username URL. Skipping\n");
269
			fwrite(STDERR, "URL $channel_url is a valid YouTube channel or username URL. Skipping\n");
289
		} else {
270
		} else {
290
			ytdwn_channel_id($channel_id, $search);
271
			ytdwn_channel_id($channel_id, $search);
291
		}
272
		}
292
	} else if ($resourceType == 'search') {
273
	} else if ($resourceType == 'search') {
293
		$searchterm = parse_quoting($resourceValue);
274
		$searchterm = parse_quoting($resourceValue);
294
 
275
 
295
		$order = '';
276
		$order = '';
296
		$m = null;
277
		$m = null;
297
		if (preg_match('@\[order=(.+)\]@ismU', $searchterm, $m)) {
278
		if (preg_match('@\[order=(.+)\]@ismU', $searchterm, $m)) {
298
			$order = $m[1];
279
			$order = $m[1];
299
			$searchterm = preg_replace('@\[order=(.+)\]@ismU', '', $searchterm);
280
			$searchterm = preg_replace('@\[order=(.+)\]@ismU', '', $searchterm);
300
		} else {
281
		} else {
301
			$order = DEFAULT_SEARCH_ORDER; // default
282
			$order = DEFAULT_SEARCH_ORDER; // default
302
		}
283
		}
303
		$order = parse_quoting($order);
284
		$order = parse_quoting($order);
304
 
285
 
305
		$maxresults = '';
286
		$maxresults = '';
306
		if (preg_match('@\[maxresults=(.+)\]@ismU', $searchterm, $m)) {
287
		if (preg_match('@\[maxresults=(.+)\]@ismU', $searchterm, $m)) {
307
			$maxresults = $m[1];
288
			$maxresults = $m[1];
308
			$searchterm = preg_replace('@\[maxresults=(.+)\]@ismU', '', $searchterm);
289
			$searchterm = preg_replace('@\[maxresults=(.+)\]@ismU', '', $searchterm);
309
		} else {
290
		} else {
310
			$maxresults = DEFAULT_SEARCH_MAXRESULTS; // default
291
			$maxresults = DEFAULT_SEARCH_MAXRESULTS; // default
311
		}
292
		}
312
		$maxresults = parse_quoting($maxresults);
293
		$maxresults = parse_quoting($maxresults);
313
 
294
 
314
		$searchterm = parse_quoting($searchterm);
295
		$searchterm = parse_quoting($searchterm);
315
 
296
 
316
		ytdwn_search($searchterm, $order, $maxresults);
297
		ytdwn_search($searchterm, $order, $maxresults);
317
	} else if ($resourceType == 'list') {
298
	} else if ($resourceType == 'list') {
318
		$list_files = glob(parse_quoting($resourceValue)); // in case the user entered a wildcard, e.g. *.list
299
		$list_files = glob(parse_quoting($resourceValue)); // in case the user entered a wildcard, e.g. *.list
319
		foreach ($list_files as $list_file) {
300
		foreach ($list_files as $list_file) {
320
			if (!file_exists($list_file)) {
301
			if (!file_exists($list_file)) {
321
				fwrite(STDERR, "List file $list_file does not exist. Skipping\n");
302
				fwrite(STDERR, "List file $list_file does not exist. Skipping\n");
322
			} else {
303
			} else {
323
				ytdwn_list_file($list_file);
304
				ytdwn_list_file($list_file);
324
			}
305
			}
325
		}
306
		}
326
	} else {
307
	} else {
327
		fwrite(STDERR, "Resource type '$resourceType' is not valid. Skipping $resourceType:$resourceValue.\n");
308
		fwrite(STDERR, "Resource type '$resourceType' is not valid. Skipping $resourceType:$resourceValue.\n");
328
	}
309
	}
329
}
310
}
330
 
311
 
331
function ytdwn_list_file($list_file) {
312
function ytdwn_list_file($list_file) {
332
	global $listFilenameStack, $verbose;
313
	global $listFilenameStack, $verbose;
333
 
314
 
334
	if ($verbose) echo "Processing list file '$list_file' ...\n";
315
	if ($verbose) echo "Processing list file '$list_file' ...\n";
335
 
316
 
336
	$listFilenameStack[] = $list_file;
317
	$listFilenameStack[] = $list_file;
337
	$lines = file($list_file);
318
	$lines = file($list_file);
338
	foreach ($lines as $line) {
319
	foreach ($lines as $line) {
339
		$line = trim($line);
320
		$line = trim($line);
340
		if ($line == '') continue;
321
		if ($line == '') continue;
341
		if ($line[0] == '#') continue;
322
		if ($line[0] == '#') continue;
342
		if (strpos($line, ':') === false) {
323
		if (strpos($line, ':') === false) {
343
			fwrite(STDERR, "Invalid resource '$line' (you are missing the prefix, e.g. vurl: or vid:). Skipping.\n");
324
			fwrite(STDERR, "Invalid resource '$line' (you are missing the prefix, e.g. vurl: or vid:). Skipping.\n");
344
		} else {
325
		} else {
345
			list($resourceType, $resourceValue) = explode(':',$line,2);
326
			list($resourceType, $resourceValue) = explode(':',$line,2);
346
			ytdwn_handle_resource($resourceType, $resourceValue);
327
			ytdwn_handle_resource($resourceType, $resourceValue);
347
		}
328
		}
348
	}
329
	}
349
	array_pop($listFilenameStack);
330
	array_pop($listFilenameStack);
350
}
331
}
351
 
332
 
352
function ytdwn_channel_id($channel_id, $search='') {
333
function ytdwn_channel_id($channel_id, $search='') {
353
	global $type;
334
	global $type;
354
	global $verbose;
335
	global $verbose;
355
 
336
 
356
	if ($verbose) echo "Processing channel ID '$channel_id' ...\n";
337
	if ($verbose) echo "Processing channel ID '$channel_id' ...\n";
357
 
338
 
358
	// List the videos of the channel
339
	// List the videos of the channel
359
 
340
 
360
	$cont = !empty(_getResultcache()) && file_exists(_getResultcache()) ? file_get_contents(_getResultcache()) : '';
341
	$use_cache = !empty(_getResultcache()) && file_exists(_getResultcache());
-
 
342
	$cont = $use_cache ? file_get_contents(_getResultcache()) : '';
361
	$out = json_decode($cont, true);
343
	$out = json_decode($cont, true);
362
	if ($out == NULL) $out = array();
344
	if ($out == NULL) $out = array();
363
 
345
 
364
	if (!empty(_getResultcache())) {
346
	if ($use_cache) {
365
		$stats = yt_get_channel_stats($channel_id);
347
		$stats = yt_get_channel_stats($channel_id);
366
		if ($stats === false) {
348
		if ($stats === false) {
367
			fwrite(STDERR, "Cannot get stats for channel with ID '$channel_id'\n");
349
			fwrite(STDERR, "Cannot get stats for channel with ID '$channel_id'\n");
368
			return;
350
			return;
369
		}
351
		}
370
		$videocount = $stats['videoCount'];
352
		$videocount = $stats['videoCount'];
371
 
353
 
372
		$key = (!empty($search)) ? 'cid:'.$channel_id.'/'.$search : 'cid:'.$channel_id;
354
		$key = (!empty($search)) ? 'cid:'.$channel_id.'/'.$search : 'cid:'.$channel_id;
373
 
355
 
374
		if (!isset($out[$key])) $out[$key] = array();
356
		if (!isset($out[$key])) $out[$key] = array();
375
		$videocount_old = isset($out[$key]['count']) ? $out[$key]['count'] : -1;
357
		$videocount_old = isset($out[$key]['count']) ? $out[$key]['count'] : -1;
376
	} else {
358
	} else {
377
		$videocount = -1;
359
		$videocount = -1;
378
		$videocount_old = -2;
360
		$videocount_old = -2;
379
		$key = '';
361
		$key = '';
380
	}
362
	}
381
 
363
 
382
	if ($videocount_old != $videocount) { // Attention: This might not work if videos are deleted and added (= the count stays the same)
364
	if ($videocount_old != $videocount) { // Attention: This might not work if videos are deleted and added (= the count stays the same)
383
		if ($verbose && !empty(_getResultcache())) echo "Video count changed from $videocount_old to $videocount\n";
365
		if ($verbose && $use_cache) echo "Video count changed from $videocount_old to $videocount\n";
384
		$out[$key]['count'] = $videocount;
366
		$out[$key]['count'] = $videocount;
385
		if (!empty($search)) {
367
		if (!empty($search)) {
386
			$out[$key]['results'] = yt_channel_items($channel_id, $search);
368
			$out[$key]['results'] = yt_channel_items($channel_id, $search);
387
		} else {
369
		} else {
388
			$out[$key]['results'] = yt_channel_items($channel_id);
370
			$out[$key]['results'] = yt_channel_items($channel_id);
389
		}
371
		}
-
 
372
		if (!$out[$key]['results']) {
-
 
373
			fwrite(STDERR, "Cannot get result for channel with ID '$channel_id'\n");
-
 
374
			return;
-
 
375
		}
390
	} else {
376
	} else {
391
		if ($verbose) echo "Video count for channel is still $videocount, keep ".count($out[$key]['results'])." results.\n";
377
		if ($verbose) echo "Video count for channel is still $videocount, keep ".count($out[$key]['results'])." results.\n";
392
	}
378
	}
393
 
379
 
394
	// Save the cache
380
	// Save the cache
395
 
381
 
396
	try {
382
	try {
397
		if (!empty(_getResultcache())) file_put_contents(_getResultcache(), json_encode($out));
383
		if ($use_cache) file_put_contents(_getResultcache(), json_encode($out));
398
	} catch(Exception $e) {
384
	} catch(Exception $e) {
399
		fwrite(STDERR, "Cannot write result cache\n");
385
		fwrite(STDERR, "Cannot write result cache\n");
400
	}
386
	}
401
 
387
 
402
	// Now download
388
	// Now download
403
 
389
 
404
	if (!$out[$key]['results']) {
390
	if (!$out[$key]['results']) {
405
		fwrite(STDERR, "Cannot get result for channel with ID '$channel_id'\n");
391
		fwrite(STDERR, "Cannot get result for channel with ID '$channel_id'\n");
406
		return;
392
		return;
407
	}
393
	}
408
	foreach ($out[$key]['results'] as list($id, $title)) {
394
	foreach ($out[$key]['results'] as list($id, $title)) {
409
		if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n";
395
		if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n";
410
		ytdwn_video_id($id);
396
		ytdwn_video_id($id);
411
	}
397
	}
412
}
398
}
413
 
399
 
414
function ytdwn_playlist_id($playlist_id) {
400
function ytdwn_playlist_id($playlist_id) {
415
	global $type;
401
	global $type;
416
	global $verbose;
402
	global $verbose;
417
 
403
 
418
	if ($verbose) echo "Processing playlist ID '$playlist_id' ...\n";
404
	if ($verbose) echo "Processing playlist ID '$playlist_id' ...\n";
419
 
405
 
420
	// List the videos of the playlist
406
	// List the videos of the playlist
421
 
407
 
422
	$cont = !empty(_getResultcache()) && file_exists(_getResultcache()) ? file_get_contents(_getResultcache()) : '';
408
	$use_cache = !empty(_getResultcache()) && file_exists(_getResultcache());
-
 
409
	$cont = $use_cache ? file_get_contents(_getResultcache()) : '';
423
	$out = json_decode($cont, true);
410
	$out = json_decode($cont, true);
424
	if ($out == NULL) $out = array();
411
	if ($out == NULL) $out = array();
425
 
412
 
426
	if (!empty(_getResultcache())) {
413
	if ($use_cache) {
427
		$stats = yt_get_playlist_stats($playlist_id);
414
		$stats = yt_get_playlist_stats($playlist_id);
428
		if ($stats === false) {
415
		if ($stats === false) {
429
			fwrite(STDERR, "Cannot get stats for playlist with ID '$playlist_id'\n");
416
			fwrite(STDERR, "Cannot get stats for playlist with ID '$playlist_id'\n");
430
			return;
417
			return;
431
		}
418
		}
432
		$videocount = $stats['itemCount'];
419
		$videocount = $stats['itemCount'];
433
 
420
 
434
		$key = 'pid:'.$playlist_id;
421
		$key = 'pid:'.$playlist_id;
435
 
422
 
436
		if (!isset($out[$key])) $out[$key] = array();
423
		if (!isset($out[$key])) $out[$key] = array();
437
		$videocount_old = isset($out[$key]['count']) ? $out[$key]['count'] : -1;
424
		$videocount_old = isset($out[$key]['count']) ? $out[$key]['count'] : -1;
438
	} else {
425
	} else {
439
		$videocount = -1;
426
		$videocount = -1;
440
		$videocount_old = -2;
427
		$videocount_old = -2;
441
		$key = '';
428
		$key = '';
442
	}
429
	}
443
 
430
 
444
	if ($videocount_old != $videocount) { // Attention: This might not work if videos are deleted and added (= the count stays the same)
431
	if ($videocount_old != $videocount) { // Attention: This might not work if videos are deleted and added (= the count stays the same)
445
		if ($verbose && !empty(_getResultcache())) echo "Video count changed from $videocount_old to $videocount\n";
432
		if ($verbose && $use_cache) echo "Video count changed from $videocount_old to $videocount\n";
446
		$out[$key]['count'] = $videocount;
433
		$out[$key]['count'] = $videocount;
447
		$out[$key]['results'] = yt_playlist_items($playlist_id);
434
		$out[$key]['results'] = yt_playlist_items($playlist_id);
-
 
435
		if (!$out[$key]['results']) {
-
 
436
			fwrite(STDERR, "Cannot get result for playlist with ID '$playlist_id'\n");
-
 
437
			return;
-
 
438
		}
448
	} else {
439
	} else {
449
		if ($verbose) echo "Video count for playlist is still $videocount, keep ".count($out[$key]['results'])." results.\n";
440
		if ($verbose) echo "Video count for playlist is still $videocount, keep ".count($out[$key]['results'])." results.\n";
450
	}
441
	}
451
 
442
 
452
	// Save the cache
443
	// Save the cache
453
 
444
 
454
	try {
445
	try {
455
		if (!empty(_getResultcache())) file_put_contents(_getResultcache(), json_encode($out));
446
		if ($use_cache) file_put_contents(_getResultcache(), json_encode($out));
456
	} catch(Exception $e) {
447
	} catch(Exception $e) {
457
		fwrite(STDERR, "Cannot write result cache\n");
448
		fwrite(STDERR, "Cannot write result cache\n");
458
	}
449
	}
459
 
450
 
460
	// Now download
451
	// Now download
461
 
452
 
462
	if (!$out[$key]['results']) {
453
	if (!$out[$key]['results']) {
463
		fwrite(STDERR, "Cannot get result for playlist with ID '$playlist_id'\n");
454
		fwrite(STDERR, "Cannot get result for playlist with ID '$playlist_id'\n");
464
		return;
455
		return;
465
	}
456
	}
466
	foreach ($out[$key]['results'] as list($id, $title)) {
457
	foreach ($out[$key]['results'] as list($id, $title)) {
467
		if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n";
458
		if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n";
468
		ytdwn_video_id($id);
459
		ytdwn_video_id($id);
469
	}
460
	}
470
}
461
}
471
 
462
 
472
function ytdwn_search($search, $order='', $maxresults=-1) {
463
function ytdwn_search($search, $order='', $maxresults=-1) {
473
	global $type;
464
	global $type;
474
	global $verbose;
465
	global $verbose;
475
 
466
 
476
	if ($verbose) echo "Searching for '$search' ...\n";
467
	if ($verbose) echo "Searching for '$search' ...\n";
477
 
468
 
478
	// Perform the search and list the videos
469
	// Perform the search and list the videos
479
 
470
 
480
	$results = yt_search_items($search, $order, $maxresults);
471
	$results = yt_search_items($search, $order, $maxresults);
481
 
472
 
482
	// Now download
473
	// Now download
483
 
474
 
484
	if (!$results) {
475
	if (!$results) {
485
		fwrite(STDERR, "Cannot get data for search '$search'\n");
476
		fwrite(STDERR, "Cannot get data for search '$search'\n");
486
		return;
477
		return;
487
	}
478
	}
488
	foreach ($results as list($id, $title)) {
479
	foreach ($results as list($id, $title)) {
489
		if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n";
480
		if ($verbose) echo "Downloading '$title' as ".hf_type($type)." ...\n";
490
		ytdwn_video_id($id);
481
		ytdwn_video_id($id);
491
	}
482
	}
492
}
483
}
493
 
484
 
494
function template_to_wildcard($template, $video_id) {
485
function template_to_wildcard($template, $video_id) {
495
	$x = $template;
486
	$x = $template;
496
	$x = str_replace('%(id)s', $video_id, $x);
487
	$x = str_replace('%(id)s', $video_id, $x);
497
	$x = preg_replace('@%\\(.+\\)s@ismU', '*', $x);
488
	$x = preg_replace('@%\\(.+\\)s@ismU', '*', $x);
498
	$x = preg_replace('@\\*+@', '*', $x);
489
	$x = preg_replace('@\\*+@', '*', $x);
499
	return $x;
490
	return $x;
500
}
491
}
501
 
492
 
502
function ytdwn_get_downloaded_filename($outputTemplate, $video_id) {
493
function ytdwn_get_downloaded_filename($outputTemplate, $video_id) {
503
	if (strpos($outputTemplate, '%(id)s') === false) {
494
	if (strpos($outputTemplate, '%(id)s') === false) {
504
		// TODO: There needs to be a better way to find out the written file name !!!
495
		// TODO: There needs to be a better way to find out the written file name !!!
505
		return false;
496
		return false;
506
	} else {
497
	} else {
507
		$wildcard = template_to_wildcard($outputTemplate, $video_id);
498
		$wildcard = template_to_wildcard($outputTemplate, $video_id);
508
		$test = glob($wildcard);
499
		$test = glob($wildcard);
509
		if (count($test) == 0) return false;
500
		if (count($test) == 0) return false;
510
		return $test[0];
501
		return $test[0];
511
	}
502
	}
512
}
503
}
513
 
504
 
514
function ytdwn_video_id($video_id) {
505
function ytdwn_video_id($video_id) {
515
	global $type;
506
	global $type;
516
	global $verbose;
507
	global $verbose;
517
	global $mp3id_transfer;
508
	global $mp3id_transfer;
518
	global $extra_args;
509
	global $extra_args;
519
	global $default_template;
510
	global $default_template;
520
	global $failTreshold;
511
	global $failTreshold;
521
	global $cookie_file;
512
	global $cookie_file;
522
	global $checksumMode;
513
	global $checksumMode;
523
 
514
 
524
	if (DOWNLOAD_SIMULATION_MODE) {
515
	if (DOWNLOAD_SIMULATION_MODE) {
525
		echo "SIMULATE download of video id $video_id as ".hf_type($type)." to "._getOutputDir()."\n";
516
		echo "SIMULATE download of video id $video_id as ".hf_type($type)." to "._getOutputDir()."\n";
526
		return;
517
		return;
527
	}
518
	}
528
 
519
 
529
	if (!empty(_getAlreadyDownloaded()) && in_alreadydownloaded_file($type, $video_id)) {
520
	if (!empty(_getAlreadyDownloaded()) && in_alreadydownloaded_file($type, $video_id)) {
530
		if ($verbose) echo "Video $video_id has already been downloaded. Skip.\n";
521
		if ($verbose) echo "Video $video_id has already been downloaded. Skip.\n";
531
		return true;
522
		return true;
532
	}
523
	}
533
 
524
 
534
	if (!empty(_getFailList()) && (ytdwn_fail_counter($type, $video_id) >= $failTreshold)) {
525
	if (!empty(_getFailList()) && (ytdwn_fail_counter($type, $video_id) >= $failTreshold)) {
535
		if ($verbose) echo "Video $video_id has failed too often. Skip.\n";
526
		if ($verbose) echo "Video $video_id has failed too often. Skip.\n";
536
		return true;
527
		return true;
537
	}
528
	}
538
 
529
 
539
	$out = '';
530
	$out = '';
540
	$code = -1;
531
	$code = -1;
541
 
532
 
542
	$outputTemplate = rtrim(_getOutputDir(), '/').'/'.$default_template;
533
	$outputTemplate = rtrim(_getOutputDir(), '/').'/'.$default_template;
543
 
534
 
544
	if (substr($type,0,2) == 'v:') {
535
	if (substr($type,0,2) == 'v:') {
545
		$format = substr($type,2);
536
		$format = substr($type,2);
546
		if (!empty($format)) {
537
		if (!empty($format)) {
547
			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);
538
			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);
548
		} else {
539
		} else {
549
			exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)), $out, $code);
540
			exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)), $out, $code);
550
		}
541
		}
551
 
542
 
552
		$written_file = $code == 0 ? ytdwn_get_downloaded_filename($outputTemplate, $video_id) : false;
543
		$written_file = $code == 0 ? ytdwn_get_downloaded_filename($outputTemplate, $video_id) : false;
553
 
544
 
554
	} else if (substr($type,0,2) == 'a:') {
545
	} else if (substr($type,0,2) == 'a:') {
555
		$format = substr($type,2);
546
		$format = substr($type,2);
556
		if (!empty($format)) {
547
		if (!empty($format)) {
557
			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);
548
			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);
558
		} else {
549
		} else {
559
			exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)).' --extract-audio', $out, $code);
550
			exec(YTDL_EXE.' -o '.escapeshellarg($outputTemplate).' '.$extra_args.(empty($cookie_file) ? '' : ' --cookies '.$cookie_file).' '.escapeshellarg(vid_to_vurl($video_id)).' --extract-audio', $out, $code);
560
		}
551
		}
561
 
552
 
562
		$written_file = $code == 0 ? ytdwn_get_downloaded_filename($outputTemplate, $video_id) : false;
553
		$written_file = $code == 0 ? ytdwn_get_downloaded_filename($outputTemplate, $video_id) : false;
563
 
554
 
564
		if (($code == 0) && ($mp3id_transfer) && (strtolower($format) == 'mp3')) {
555
		if (($code == 0) && ($mp3id_transfer) && (strtolower($format) == 'mp3')) {
565
			if ($written_file === false) {
556
			if ($written_file === false) {
566
				fwrite(STDERR, "Cannot include YouTube ID to MP3, because the default template does not contain '%(id)s', or the downloaded file could not be determined for another reason.\n");
557
				fwrite(STDERR, "Cannot include YouTube ID to MP3, because the default template does not contain '%(id)s', or the downloaded file could not be determined for another reason.\n");
567
			} else {
558
			} else {
568
				mp3_transfer_vid_to_id($written_file, $video_id);
559
				mp3_transfer_vid_to_id($written_file, $video_id);
569
			}
560
			}
570
		}
561
		}
571
	} else {
562
	} else {
572
		assert(false);
563
		assert(false);
573
		return false;
564
		return false;
574
	}
565
	}
575
 
566
 
576
	if ($code == 0) {
567
	if ($code == 0) {
577
		if ($verbose) {
568
		if ($verbose) {
578
			fwrite(STDOUT, "Successfully downloaded video ID $video_id as ".hf_type($type)."\n");
569
			fwrite(STDOUT, "Successfully downloaded video ID $video_id as ".hf_type($type)."\n");
579
			if ($written_file !== false) fwrite(STDOUT, "Output file name: $written_file\n");
570
			if ($written_file !== false) fwrite(STDOUT, "Output file name: $written_file\n");
580
		}
571
		}
581
		if (!empty(_getAlreadyDownloaded())) {
572
		if (!empty(_getAlreadyDownloaded())) {
582
			try {
573
			try {
583
				addto_alreadydownloaded_file($type, $video_id);
574
				addto_alreadydownloaded_file($type, $video_id);
584
			} catch(Exception $e) {
575
			} catch(Exception $e) {
585
				fwrite(STDERR, "Cannot add to 'already downloaded' file\n");
576
				fwrite(STDERR, "Cannot add to 'already downloaded' file\n");
586
			}
577
			}
587
		}
578
		}
588
 
579
 
589
		// Now do the checksums
580
		// Now do the checksums
590
		foreach (explode(',',$checksumMode) as $mode) {
581
		foreach (explode(',',$checksumMode) as $mode) {
591
			if (strtolower($mode) === 'none') continue;
582
			if (strtolower($mode) === 'none') continue;
592
			if ($written_file === false) {
583
			if ($written_file === false) {
593
				fwrite(STDERR, "Cannot add to the '$mode' checksum file, because the default template does not contain '%(id)s', or the downloaded file could not be determined for another reason.\n");
584
				fwrite(STDERR, "Cannot add to the '$mode' checksum file, because the default template does not contain '%(id)s', or the downloaded file could not be determined for another reason.\n");
594
			} else if (!cs_add_automatically($written_file, $mode)) {
585
			} else if (!cs_add_automatically($written_file, $mode)) {
595
				fwrite(STDERR, "Could not write to '$mode' checksum file!\n");
586
				fwrite(STDERR, "Could not write to '$mode' checksum file!\n");
596
			}
587
			}
597
		}
588
		}
598
	} else {
589
	} else {
599
		fwrite(STDERR, "Error downloading $video_id! (Code $code)\n");
590
		fwrite(STDERR, "Error downloading $video_id! (Code $code)\n");
600
		if (!empty(_getFailList())) {
591
		if (!empty(_getFailList())) {
601
			try {
592
			try {
602
				ytdwn_register_fail($type, $video_id, $code);
593
				ytdwn_register_fail($type, $video_id, $code);
603
			} catch(Exception $e) {
594
			} catch(Exception $e) {
604
				fwrite(STDERR, "Cannot register fail\n");
595
				fwrite(STDERR, "Cannot register fail\n");
605
			}
596
			}
606
		}
597
		}
607
		return false;
598
		return false;
608
	}
599
	}
609
 
600
 
610
	return true;
601
	return true;
611
}
602
}
612
 
603
 
613
function vid_to_vurl($video_id) {
604
function vid_to_vurl($video_id) {
614
	return "https://www.youtube.com/watch?v=$video_id";
605
	return "https://www.youtube.com/watch?v=$video_id";
615
}
606
}
616
 
607
 
617
function EndsWith($Haystack, $Needle){
608
function EndsWith($Haystack, $Needle){
618
	return strrpos($Haystack, $Needle) === strlen($Haystack)-strlen($Needle);
609
	return strrpos($Haystack, $Needle) === strlen($Haystack)-strlen($Needle);
619
}
610
}
620
 
611
 
621
function mp3_transfer_vid_to_id(&$written_file, $video_id) {
612
function mp3_transfer_vid_to_id(&$written_file, $video_id) {
622
	global $verbose;
613
	global $verbose;
623
	global $default_template;
614
	global $default_template;
624
 
615
 
625
	if (!command_exists('id3v2')) {
616
	if (!command_exists('id3v2')) {
626
		fwrite(STDERR, "Tool id3v2 is not installed. Will not transfer the YouTube ID into the MP3 ID Tag. Use paramter '-N' to stop trying the transfer.\n");
617
		fwrite(STDERR, "Tool id3v2 is not installed. Will not transfer the YouTube ID into the MP3 ID Tag. Use paramter '-N' to stop trying the transfer.\n");
627
		return false;
618
		return false;
628
	}
619
	}
629
 
620
 
630
	$orig_ts = filemtime($written_file);
621
	$orig_ts = filemtime($written_file);
631
	$ec = -1;
622
	$ec = -1;
632
	system('id3v2 -c '.escapeshellarg($video_id).' '.escapeshellarg($written_file), $ec);
623
	system('id3v2 -c '.escapeshellarg($video_id).' '.escapeshellarg($written_file), $ec);
633
	touch($written_file, $orig_ts);
624
	touch($written_file, $orig_ts);
634
	if ($ec != 0) {
625
	if ($ec != 0) {
635
		fwrite(STDERR, "Cannot set ID tag for file $written_file\n");
626
		fwrite(STDERR, "Cannot set ID tag for file $written_file\n");
636
		return false;
627
		return false;
637
	}
628
	}
638
 
629
 
639
	$target_filename = $written_file;
630
	$target_filename = $written_file;
640
 
631
 
641
	// Things like '<title>-<id>.mp3' become '<title>.mp3' (our default template)
632
	// Things like '<title>-<id>.mp3' become '<title>.mp3' (our default template)
642
	// But templates like '<title> (<id>).mp3' could become '<title> ().mp3', which is not nice
633
	// But templates like '<title> (<id>).mp3' could become '<title> ().mp3', which is not nice
643
	// So, we try our best to find the most common template types...
634
	// So, we try our best to find the most common template types...
644
	$target_filename = str_replace('-'.$video_id, '', $target_filename);
635
	$target_filename = str_replace('-'.$video_id, '', $target_filename);
645
	$target_filename = str_replace('_'.$video_id, '', $target_filename);
636
	$target_filename = str_replace('_'.$video_id, '', $target_filename);
646
	$target_filename = str_replace(' '.$video_id, '', $target_filename);
637
	$target_filename = str_replace(' '.$video_id, '', $target_filename);
647
	$target_filename = str_replace('('.$video_id.')', '', $target_filename);
638
	$target_filename = str_replace('('.$video_id.')', '', $target_filename);
648
	$target_filename = str_replace('['.$video_id.']', '', $target_filename);
639
	$target_filename = str_replace('['.$video_id.']', '', $target_filename);
649
	$target_filename = str_replace($video_id, '', $target_filename); // must be the last!
640
	$target_filename = str_replace($video_id, '', $target_filename); // must be the last!
650
	if ($target_filename === $written_file) {
641
	if ($target_filename === $written_file) {
651
		fwrite(STDERR, "Could not remove VideoID from filename '$written_file'\n"); // should not happen
642
		fwrite(STDERR, "Could not remove VideoID from filename '$written_file'\n"); // should not happen
652
		return false;
643
		return false;
653
	}
644
	}
654
 
645
 
655
	if (!intelligent_rename($written_file, $target_filename)) {
646
	if (!intelligent_rename($written_file, $target_filename)) {
656
		fwrite(STDERR, "Could not rename '$written_file' to '$target_filename'\n");
647
		fwrite(STDERR, "Could not rename '$written_file' to '$target_filename'\n");
657
		return false;
648
		return false;
658
	}
649
	}
659
 
650
 
660
	$written_file = $target_filename; // was modified by intelligent_rename()
651
	$written_file = $target_filename; // was modified by intelligent_rename()
661
	return true;
652
	return true;
662
}
653
}
663
 
654
 
664
function curl_to_cid($channel_url) {
655
function curl_to_cid($channel_url) {
665
	return yt_get_channel_id_from_url($channel_url);
656
	return yt_get_channel_id_from_url($channel_url);
666
}
657
}
667
 
658
 
668
function in_alreadydownloaded_file($type, $video_id) {
659
function in_alreadydownloaded_file($type, $video_id) {
669
	$lines = file(_getAlreadyDownloaded());
660
	$lines = file(_getAlreadyDownloaded());
670
	foreach ($lines as $line) {
661
	foreach ($lines as $line) {
671
		if (trim($line) == rtrim($type,':').':'.$video_id) {
662
		if (trim($line) == rtrim($type,':').':'.$video_id) {
672
			return true;
663
			return true;
673
		}
664
		}
674
	}
665
	}
675
	return false;
666
	return false;
676
}
667
}
677
 
668
 
678
function addto_alreadydownloaded_file($type, $video_id) {
669
function addto_alreadydownloaded_file($type, $video_id) {
679
	file_put_contents(_getAlreadyDownloaded(), rtrim($type,':').':'.$video_id."\n", FILE_APPEND);
670
	file_put_contents(_getAlreadyDownloaded(), rtrim($type,':').':'.$video_id."\n", FILE_APPEND);
680
}
671
}
681
 
672
 
682
function syntax_error($msg) {
673
function syntax_error($msg) {
683
	fwrite(STDERR, "Syntax error: ".trim($msg)."\n");
674
	fwrite(STDERR, "Syntax error: ".trim($msg)."\n");
684
	fwrite(STDERR, "Please use argument '--help' to show the syntax rules.\n");
675
	fwrite(STDERR, "Please use argument '--help' to show the syntax rules.\n");
685
	exit(2);
676
	exit(2);
686
}
677
}
687
 
678
 
688
function _help() {
679
function _help() {
689
	global $argv;
680
	global $argv;
690
	$out = '';
681
	$out = '';
691
	$own = file_get_contents($argv[0]);
682
	$own = file_get_contents($argv[0]);
692
	$help = explode('// ----', $own, 2)[0];
683
	$help = explode('// ----', $own, 2)[0];
693
	$m = null;
684
	$m = null;
694
	$help = preg_match_all('@^//(.*)$@mU', $help, $m);
685
	$help = preg_match_all('@^//(.*)$@mU', $help, $m);
695
	foreach ($m[1] as $line) {
686
	foreach ($m[1] as $line) {
696
		$out .= substr($line,1)."\n";
687
		$out .= substr($line,1)."\n";
697
	}
688
	}
698
	return $out;
689
	return $out;
699
}
690
}
700
 
691
 
701
function help() {
692
function help() {
702
	echo _help();
693
	echo _help();
703
	exit(0);
694
	exit(0);
704
}
695
}
705
 
696
 
706
function version() {
697
function version() {
707
	echo explode("\n\n", _help(), 2)[0]."\n";
698
	echo explode("\n\n", _help(), 2)[0]."\n";
708
	exit(0);
699
	exit(0);
709
}
700
}
710
 
701
 
711
function command_exists($command) {
702
function command_exists($command) {
712
	// https://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script
703
	// https://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script
713
 
704
 
714
	$ec = -1;
705
	$ec = -1;
715
	system('command -v '.escapeshellarg($command).' > /dev/null', $ec);
706
	system('command -v '.escapeshellarg($command).' > /dev/null', $ec);
716
	return ($ec == 0);
707
	return ($ec == 0);
717
}
708
}
718
 
709
 
719
function hf_type($type) {
710
function hf_type($type) {
720
	if (strpos($type, ':') === false) return $type; // invalid type (missing ':')
711
	if (strpos($type, ':') === false) return $type; // invalid type (missing ':')
721
	list($av, $format) = explode(':', $type);
712
	list($av, $format) = explode(':', $type);
722
 
713
 
723
	if ($av == 'a') $av = 'audio';
714
	if ($av == 'a') $av = 'audio';
724
	else if ($av == 'v') $av = 'video';
715
	else if ($av == 'v') $av = 'video';
725
	else return $type; // invalid type
716
	else return $type; // invalid type
726
 
717
 
727
	return (!empty($format)) ? $format.'-'.$av : $av;
718
	return (!empty($format)) ? $format.'-'.$av : $av;
728
}
719
}
729
 
720
 
730
function expand_tilde($path) {
721
function expand_tilde($path) {
731
	// Source: http://jonathonhill.net/2013-09-03/tilde-expansion-in-php/
722
	// Source: http://jonathonhill.net/2013-09-03/tilde-expansion-in-php/
732
 
723
 
733
	if (function_exists('posix_getuid') && strpos($path, '~') !== false) {
724
	if (function_exists('posix_getuid') && strpos($path, '~') !== false) {
734
		$info = posix_getpwuid(posix_getuid());
725
		$info = posix_getpwuid(posix_getuid());
735
		$path = str_replace('~', $info['dir'], $path);
726
		$path = str_replace('~', $info['dir'], $path);
736
	}
727
	}
737
 
728
 
738
	return $path;
729
	return $path;
739
}
730
}
740
 
731
 
741
function _getLastListname() {
732
function _getLastListname() {
742
	global $listFilenameStack;
733
	global $listFilenameStack;
743
	$listname = ''; // default
734
	$listname = ''; // default
744
	if (count($listFilenameStack) > 0) {
735
	if (count($listFilenameStack) > 0) {
745
		$listname = $listFilenameStack[count($listFilenameStack)-1];
736
		$listname = $listFilenameStack[count($listFilenameStack)-1];
746
		$listname = pathinfo($listname, PATHINFO_FILENAME); // remove file extension, e.g. ".list"
737
		$listname = pathinfo($listname, PATHINFO_FILENAME); // remove file extension, e.g. ".list"
747
	}
738
	}
748
	return $listname;
739
	return $listname;
749
}
740
}
750
 
741
 
751
function _getApiKey() {
742
function _getApiKey() {
752
	global $apikey;
743
	global $apikey;
753
 
744
 
754
	$out = $apikey;
745
	$out = $apikey;
755
	if (empty($out)) {
746
	if (empty($out)) {
756
		$auto_api_key = AUTO_API_KEY;
747
		$auto_api_key = AUTO_API_KEY;
757
		$auto_api_key = expand_tilde($auto_api_key);
748
		$auto_api_key = expand_tilde($auto_api_key);
758
		$auto_api_key = str_replace('[listname]', _getLastListname(), $auto_api_key);
749
		$auto_api_key = str_replace('[listname]', _getLastListname(), $auto_api_key);
759
 
750
 
760
		if (file_exists($auto_api_key)) {
751
		if (file_exists($auto_api_key)) {
761
			$out = trim(file_get_contents($auto_api_key));
752
			$out = trim(file_get_contents($auto_api_key));
762
		} else {
753
		} else {
763
			syntax_error("Please specify a YouTube API key with argument '-A'.");
754
			syntax_error("Please specify a YouTube API key with argument '-A'.");
764
		}
755
		}
765
	} else {
756
	} else {
766
		$out = str_replace('[listname]', _getLastListname(), $out);
757
		$out = str_replace('[listname]', _getLastListname(), $out);
767
		$out = expand_tilde($out);
758
		$out = expand_tilde($out);
768
 
759
 
769
		if (file_exists($out)) {
760
		if (file_exists($out)) {
770
			$out = trim(file_get_contents($out));
761
			$out = trim(file_get_contents($out));
771
		} else {
762
		} else {
772
			// Assume, $out is a key, not a file
763
			// Assume, $out is a key, not a file
773
		}
764
		}
774
	}
765
	}
775
 
766
 
776
	if (!yt_check_apikey_syntax($out)) syntax_error("'$out' is not a valid API key, not an existing file containing an API key.\n");
767
	if (!yt_check_apikey_syntax($out)) syntax_error("'$out' is not a valid API key, not an existing file containing an API key.\n");
777
 
768
 
778
	return $out;
769
	return $out;
779
}
770
}
780
 
771
 
781
function _getResultCache() {
772
function _getResultCache() {
782
	global $resultcache;
773
	global $resultcache;
783
	if (empty($resultcache)) return '';
774
	if (empty($resultcache)) return '';
784
 
775
 
785
	$out = expand_tilde($resultcache);
776
	$out = expand_tilde($resultcache);
786
 
777
 
787
	$out = str_replace('[listname]', _getLastListname(), $out);
778
	$out = str_replace('[listname]', _getLastListname(), $out);
788
 
779
 
789
	if (!file_exists($out)) {
780
	if (!file_exists($out)) {
790
		@touch($out);
781
		@touch($out);
791
		if (!file_exists($out)) {
782
		if (!file_exists($out)) {
792
			fwrite(STDERR, "File '$out' cannot be created. Disable feature.\n");
783
			fwrite(STDERR, "File '$out' cannot be created. Disable feature.\n");
793
			return '';
784
			return '';
794
		}
785
		}
795
	}
786
	}
796
 
787
 
797
	return $out;
788
	return $out;
798
}
789
}
799
 
790
 
800
function _getAlreadyDownloaded() {
791
function _getAlreadyDownloaded() {
801
	global $alreadyDownloaded;
792
	global $alreadyDownloaded;
802
	if (empty($alreadyDownloaded)) return '';
793
	if (empty($alreadyDownloaded)) return '';
803
 
794
 
804
	$out = expand_tilde($alreadyDownloaded);
795
	$out = expand_tilde($alreadyDownloaded);
805
 
796
 
806
	$out = str_replace('[listname]', _getLastListname(), $out);
797
	$out = str_replace('[listname]', _getLastListname(), $out);
807
 
798
 
808
	if (!file_exists($out)) {
799
	if (!file_exists($out)) {
809
		@touch($out);
800
		@touch($out);
810
		if (!file_exists($out)) {
801
		if (!file_exists($out)) {
811
			fwrite(STDERR, "File '$out' cannot be created. Disable feature.\n");
802
			fwrite(STDERR, "File '$out' cannot be created. Disable feature.\n");
812
			return '';
803
			return '';
813
		}
804
		}
814
	}
805
	}
815
 
806
 
816
	return $out;
807
	return $out;
817
}
808
}
818
 
809
 
819
function _getFailList() {
810
function _getFailList() {
820
	global $failList;
811
	global $failList;
821
	if (empty($failList)) return '';
812
	if (empty($failList)) return '';
822
 
813
 
823
	$out = expand_tilde($failList);
814
	$out = expand_tilde($failList);
824
 
815
 
825
	$out = str_replace('[listname]', _getLastListname(), $out);
816
	$out = str_replace('[listname]', _getLastListname(), $out);
826
 
817
 
827
	if (!file_exists($out)) {
818
	if (!file_exists($out)) {
828
		@touch($out);
819
		@touch($out);
829
		if (!file_exists($out)) {
820
		if (!file_exists($out)) {
830
			fwrite(STDERR, "File '$out' cannot be created. Disable feature.\n");
821
			fwrite(STDERR, "File '$out' cannot be created. Disable feature.\n");
831
			return '';
822
			return '';
832
		}
823
		}
833
	}
824
	}
834
 
825
 
835
	return $out;
826
	return $out;
836
}
827
}
837
 
828
 
838
function _getOutputDir() {
829
function _getOutputDir() {
839
	global $outputDir, $allow_creation_outputdir;
830
	global $outputDir, $allow_creation_outputdir;
840
	if (empty($outputDir)) return '.';
831
	if (empty($outputDir)) return '.';
841
 
832
 
842
	$out = expand_tilde($outputDir);
833
	$out = expand_tilde($outputDir);
843
 
834
 
844
	$out = str_replace('[listname]', _getLastListname(), $out);
835
	$out = str_replace('[listname]', _getLastListname(), $out);
845
 
836
 
846
	if ($allow_creation_outputdir) {
837
	if ($allow_creation_outputdir) {
847
		if (!is_dir($out)) {
838
		if (!is_dir($out)) {
848
			mkdir($out, 0777, true);
839
			mkdir($out, 0777, true);
849
			if (!is_dir($out)) {
840
			if (!is_dir($out)) {
850
				fwrite(STDERR, "Output directory '$out' does not exist.\n");
841
				fwrite(STDERR, "Output directory '$out' does not exist.\n");
851
				exit(1);
842
				exit(1);
852
			}
843
			}
853
		}
844
		}
854
	} else {
845
	} else {
855
		if (!is_dir($out)) {
846
		if (!is_dir($out)) {
856
			fwrite(STDERR, "Output directory '$out' does not exist.\n");
847
			fwrite(STDERR, "Output directory '$out' does not exist.\n");
857
			exit(1);
848
			exit(1);
858
		}
849
		}
859
	}
850
	}
860
 
851
 
861
	return $out;
852
	return $out;
862
}
853
}
863
 
854
 
864
function parse_quoting($str) {
855
function parse_quoting($str) {
865
	if ((substr($str,0,1) == '"') && (substr($str,-1,1) == '"')) {
856
	if ((substr($str,0,1) == '"') && (substr($str,-1,1) == '"')) {
866
		$str = substr($str,1,strlen($str)-2);
857
		$str = substr($str,1,strlen($str)-2);
867
 
858
 
868
		$escape = false;
859
		$escape = false;
869
		$out = '';
860
		$out = '';
870
		for ($i=0; $i<strlen($str); $i++) {
861
		for ($i=0; $i<strlen($str); $i++) {
871
			$char = $str[$i];
862
			$char = $str[$i];
872
 
863
 
873
			if ($char == '\\') {
864
			if ($char == '\\') {
874
				if ($escape) {
865
				if ($escape) {
875
					$out .= $char;
866
					$out .= $char;
876
					$escape = false;
867
					$escape = false;
877
				} else {
868
				} else {
878
					$escape = true;
869
					$escape = true;
879
				}
870
				}
880
			} else {
871
			} else {
881
				$out .= $char;
872
				$out .= $char;
882
			}
873
			}
883
 
874
 
884
		}
875
		}
885
		$str = $out;
876
		$str = $out;
886
 
877
 
887
	}
878
	}
888
 
879
 
889
	return $str;
880
	return $str;
890
}
881
}
891
 
882
 
892
function ytdwn_register_fail($type, $video_id, $code) {
883
function ytdwn_register_fail($type, $video_id, $code) {
893
	// Note: Error code $code ist currently not used
884
	// Note: Error code $code ist currently not used
894
 
885
 
895
	$file = _getFailList();
886
	$file = _getFailList();
896
	$cont = file_get_contents($file);
887
	$cont = file_get_contents($file);
897
	$m = null;
888
	$m = null;
898
	if (preg_match("@Video ID ".preg_quote($video_id,'@')." failed (\d+) time\(s\) with type ".preg_quote($type,'@')."@ismU", $cont, $m)) {
889
	if (preg_match("@Video ID ".preg_quote($video_id,'@')." failed (\d+) time\(s\) with type ".preg_quote($type,'@')."@ismU", $cont, $m)) {
899
		$cont = preg_replace("@Video ID ".preg_quote($video_id,'@')." failed (\d+) time\(s\) with type ".preg_quote($type,'@')."@ismU",
890
		$cont = preg_replace("@Video ID ".preg_quote($video_id,'@')." failed (\d+) time\(s\) with type ".preg_quote($type,'@')."@ismU",
900
		                     "Video ID $video_id failed ".($m[1]+1)." time(s) with type $type", $cont);
891
		                     "Video ID $video_id failed ".($m[1]+1)." time(s) with type $type", $cont);
901
		file_put_contents($file, $cont);
892
		file_put_contents($file, $cont);
902
	} else {
893
	} else {
903
		file_put_contents($file, "Video ID $video_id failed 1 time(s) with type $type\n", FILE_APPEND);
894
		file_put_contents($file, "Video ID $video_id failed 1 time(s) with type $type\n", FILE_APPEND);
904
	}
895
	}
905
}
896
}
906
 
897
 
907
function ytdwn_fail_counter($type, $video_id) {
898
function ytdwn_fail_counter($type, $video_id) {
908
	$file = _getFailList();
899
	$file = _getFailList();
909
	$cont = file_get_contents($file);
900
	$cont = file_get_contents($file);
910
	$m = null;
901
	$m = null;
911
	if (preg_match("@Video ID ".preg_quote($video_id,'@')." failed (\d+) time\(s\) with type ".preg_quote($type,'@')."@ismU", $cont, $m)) {
902
	if (preg_match("@Video ID ".preg_quote($video_id,'@')." failed (\d+) time\(s\) with type ".preg_quote($type,'@')."@ismU", $cont, $m)) {
912
		return $m[1];
903
		return $m[1];
913
	} else {
904
	} else {
914
		return 0;
905
		return 0;
915
	}
906
	}
916
}
907
}
917
 
908
 
918
function intelligent_rename($src, &$dest) {
909
function intelligent_rename($src, &$dest) {
919
	$pos = strrpos($dest, '.');
910
	$pos = strrpos($dest, '.');
920
	$ext = substr($dest, $pos);
911
	$ext = substr($dest, $pos);
921
	$namewoext = substr($dest, 0, $pos);
912
	$namewoext = substr($dest, 0, $pos);
922
	$failcnt = 1;
913
	$failcnt = 1;
923
	$dest_neu = $dest;
914
	$dest_neu = $dest;
924
	while (file_exists($dest_neu)) {
915
	while (file_exists($dest_neu)) {
925
		$failcnt++;
916
		$failcnt++;
926
		$dest_neu = "$namewoext ($failcnt)$ext";
917
		$dest_neu = "$namewoext ($failcnt)$ext";
927
	}
918
	}
928
	$res = rename($src, $dest_neu);
919
	$res = rename($src, $dest_neu);
929
	if ($res) $dest = $dest_neu;
920
	if ($res) $dest = $dest_neu;
930
	return $res;
921
	return $res;
931
}
922
}
932
 
923
 
933
function get_latest_ytdl_md5sum() {
924
function download_latest_downloader() {
-
 
925
	$download_url = false;
-
 
926
 
-
 
927
	if (DOWNLOAD_YT_FORK == 'yt-dlp') {
-
 
928
		$ch = curl_init();
-
 
929
		curl_setopt($ch, CURLOPT_URL, 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/SHA2-256SUMS');
-
 
930
		#curl_setopt($ch, CURLOPT_HEADER, false);
-
 
931
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
-
 
932
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
-
 
933
		$cont = curl_exec($ch);
-
 
934
		$m = null;
-
 
935
		if (preg_match('@^(.+)\s+yt-dlp$@ismU', $cont, $m)) {
-
 
936
			$newest_version_sha2 = $m[1];
-
 
937
		} else {
-
 
938
			$newest_version_sha2 = false;
-
 
939
		}
-
 
940
 
-
 
941
		if (!$newest_version_sha2) {
-
 
942
			fwrite(STDERR, "Failed to get SHA2 sum of latest version of '".DOWNLOAD_YT_FORK."' online. Will not try to download/update '".DOWNLOAD_YT_FORK."' into local directory.\n");
-
 
943
		} else {
-
 
944
			if (!file_exists(__DIR__.'/'.DOWNLOAD_YT_FORK) || ($newest_version_sha2 != hash_file('sha256',__DIR__.'/'.DOWNLOAD_YT_FORK))) {
-
 
945
				$download_url = 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp';
-
 
946
			}
-
 
947
		}
-
 
948
	}
-
 
949
 
-
 
950
	if (DOWNLOAD_YT_FORK == 'youtube-dlc') {
-
 
951
		// TODO: What is the difference between these two? Which one should be chosen?!
-
 
952
		// https://github.com/blackjack4494/yt-dlc (18372 commits, last commit 23 Aug 2021, last release 2020.11.11-3)
-
 
953
		// https://github.com/blackjack4494/youtube-dlc (18888 commits, last commit 26 Jul 2021, last release 2020.10.09)
-
 
954
 
-
 
955
		// TODO: yt-dlc The sha256 does not fit to the binary! Therefore, it is always downloaded!
-
 
956
		// TODO: youtube-dlc has no hashes online...
-
 
957
		/*
-
 
958
		$ch = curl_init();
-
 
959
		curl_setopt($ch, CURLOPT_URL, 'https://github.com/blackjack4494/yt-dlc/releases/latest/download/SHA2-256SUMS');
-
 
960
		#curl_setopt($ch, CURLOPT_HEADER, false);
-
 
961
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
-
 
962
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
-
 
963
		$cont = curl_exec($ch);
-
 
964
		$m = null;
-
 
965
		if (preg_match('@^youtube\-dlc:(.+)$@ismU', $cont, $m)) {
-
 
966
			$newest_version_sha2 = $m[1];
-
 
967
		} else {
-
 
968
			$newest_version_sha2 = false;
-
 
969
		}
-
 
970
 
-
 
971
		if (!$newest_version_sha2) {
-
 
972
			fwrite(STDERR, "Failed to get SHA2 sum of latest version of '".DOWNLOAD_YT_FORK."' online. Will not try to download/update '".DOWNLOAD_YT_FORK."' into local directory.\n");
-
 
973
		} else {
-
 
974
			if (!file_exists(__DIR__.'/'.DOWNLOAD_YT_FORK) || ($newest_version_sha2 != hash_file('sha256',__DIR__.'/'.DOWNLOAD_YT_FORK))) {
-
 
975
				$download_url = 'https://github.com/blackjack4494/yt-dlc/releases/latest/download/youtube-dlc';
-
 
976
			}
-
 
977
		}
-
 
978
		*/
-
 
979
 
-
 
980
		if (!file_exists(__DIR__.'/'.DOWNLOAD_YT_FORK) || (time()-filemtime(__DIR__.'/'.DOWNLOAD_YT_FORK) > 24*60*60)) {
-
 
981
			$download_url = 'https://github.com/blackjack4494/yt-dlc/releases/latest/download/youtube-dlc';
-
 
982
		}
-
 
983
	}
-
 
984
 
-
 
985
	if (DOWNLOAD_YT_FORK == 'youtube-dl') {
934
	$ch = curl_init();
986
		$ch = curl_init();
935
	curl_setopt($ch, CURLOPT_URL, 'https://yt-dl.org/downloads/latest/MD5SUMS');
987
		curl_setopt($ch, CURLOPT_URL, 'https://yt-dl.org/downloads/latest/MD5SUMS');
936
	#curl_setopt($ch, CURLOPT_HEADER, false);
988
		#curl_setopt($ch, CURLOPT_HEADER, false);
937
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
989
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
938
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
990
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
939
	$cont = curl_exec($ch);
991
		$cont = curl_exec($ch);
940
	$m = null;
992
		$m = null;
941
	if (preg_match('@^(.+)  youtube\-dl$@ismU', $cont, $m)) {
993
		if (preg_match('@^(.+)  youtube\-dl$@ismU', $cont, $m)) {
942
		return $m[1];
994
			$newest_version_md5 = $m[1];
943
	} else {
995
		} else {
-
 
996
			$newest_version_md5 = false;
-
 
997
		}
-
 
998
 
-
 
999
		if (!$newest_version_md5) {
-
 
1000
			fwrite(STDERR, "Failed to get MD5 sum of latest version of '".DOWNLOAD_YT_FORK."' online. Will not try to download/update '".DOWNLOAD_YT_FORK."' into local directory.\n");
944
		return false;
1001
		} else {
-
 
1002
			if (!file_exists(__DIR__.'/'.DOWNLOAD_YT_FORK) || ($newest_version_md5 != md5_file(__DIR__.'/'.DOWNLOAD_YT_FORK))) {
-
 
1003
				$download_url = 'https://yt-dl.org/latest/youtube-dl';
-
 
1004
			}
-
 
1005
		}
-
 
1006
	}
-
 
1007
 
-
 
1008
	if ($download_url !== false) {
-
 
1009
		// Try to download/update the file in our directory. It should be the newest available, since YT often breaks downloader
-
 
1010
		if (file_exists(__DIR__.'/'.DOWNLOAD_YT_FORK)) {
-
 
1011
			echo "Trying to update '".DOWNLOAD_YT_FORK."' in local directory...\n";
-
 
1012
		} else {
-
 
1013
			echo "Trying to download '".DOWNLOAD_YT_FORK."' into local directory...\n";
-
 
1014
		}
-
 
1015
 
-
 
1016
		@chmod(__DIR__.'/'.DOWNLOAD_YT_FORK, 0777); // otherwise we might not be able to write to it
-
 
1017
 
-
 
1018
		if (!($binary = file_get_contents($download_url))) {
-
 
1019
			fwrite(STDERR, "Failed to download '".DOWNLOAD_YT_FORK."' into local directory (file_get_contents).\n");
-
 
1020
		} else if (!@file_put_contents(__DIR__.'/'.DOWNLOAD_YT_FORK, $binary)) {
-
 
1021
			fwrite(STDERR, "Failed to download '".DOWNLOAD_YT_FORK."' into local directory (file_put_contents).\n");
-
 
1022
		} else {
-
 
1023
			if (!@chmod(__DIR__.'/'.DOWNLOAD_YT_FORK, 0544)) {
-
 
1024
				fwrite(STDERR, "Failed to download '".DOWNLOAD_YT_FORK."' into local directory (chmod 544).\n");
-
 
1025
				@unlink(__DIR__.'/'.DOWNLOAD_YT_FORK); // try to delete, otherwise we might try to execute a non-executable file
-
 
1026
			}
-
 
1027
		}
945
	}
1028
	}
946
}
1029
}