Subversion Repositories yt_downloader

Rev

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

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