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 | } |