Subversion Repositories yt_downloader

Rev

Rev 16 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 daniel-mar 1
<?php
2
 
14 daniel-mar 3
// ViaThinkSoft YouTube Downloader Functions 2.3
19 daniel-mar 4
// Revision: 2022-12-19
2 daniel-mar 5
// Author: Daniel Marschall <www.daniel-marschall.de>
7 daniel-mar 6
// Licensed under the terms of the Apache 2.0 License
2 daniel-mar 7
 
8
// Get API key:   https://console.developers.google.com/apis/credentials
9
// Test API here: https://developers.google.com/apis-explorer/?hl=de#p/youtube/v3/youtube.playlistItems.list
10
 
11
$yt_apikey = null;
12
$yt_apikey_callback = null;
13
 
14
function yt_set_apikey($apikey) {
15
        global $yt_apikey;
16
        $yt_apikey = $apikey;
17
}
18
 
19
function yt_set_apikey_callback($apikey_callback) {
20
        global $yt_apikey_callback;
21
        $yt_apikey_callback = $apikey_callback;
22
}
23
 
24
function yt_get_apikey() {
25
        global $yt_apikey, $yt_apikey_callback;
26
 
27
        if (!empty($yt_apikey_callback)) {
28
                $apikey = call_user_func($yt_apikey_callback);
29
                if (!yt_check_apikey_syntax($apikey)) throw new Exception("Invalid API key '$apikey'");
30
        } else if (!empty($yt_apikey)) {
31
                $apikey = $yt_apikey;
32
                if (!yt_check_apikey_syntax($apikey)) throw new Exception("Invalid API key '$apikey'");
33
        } else {
34
                throw new Exception("This function requires a YouTube API key.\n");
35
        }
36
 
37
        return $apikey;
38
}
39
 
40
function yt_playlist_items($playlist_id, $maxresults=-1) {
41
        $out = array();
42
 
43
        $next_page_token = '';
44
 
45
        do {
16 daniel-mar 46
                $cont = @file_get_contents('https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId='.urlencode($playlist_id).'&maxResults=50'.(($next_page_token!='') ? '&pageToken='.urlencode($next_page_token) : '').'&key='.urlencode(yt_get_apikey()));
47
                if (!$cont) return false; // e.g. if Playlist was deleted
2 daniel-mar 48
 
49
                $obj = json_decode($cont, true);
50
                if (!$obj) return false;
51
 
8 daniel-mar 52
                if (!isset($obj['items'])) return false;
53
 
2 daniel-mar 54
                foreach ($obj['items'] as $item) {
55
                        if ($item['snippet']['resourceId']['kind'] == 'youtube#video') {
56
                                $title    = $item['snippet']['title'];
57
                                $video_id = $item['snippet']['resourceId']['videoId'];
58
                                $out[] = array($video_id, $title);
59
                                if (($maxresults != -1) && ($maxresults == count($out))) return $out;
60
                        }
61
                }
62
 
63
                $next_page_token = isset($obj['nextPageToken']) ? $obj['nextPageToken'] : '';
64
        } while ($next_page_token != '');
65
 
66
        return $out;
67
}
68
 
69
function yt_get_channel_id($username) {
16 daniel-mar 70
        $cont = @file_get_contents('https://www.googleapis.com/youtube/v3/channels?key='.urlencode(yt_get_apikey()).'&forUsername='.urlencode($username).'&part=id');
2 daniel-mar 71
        if (!$cont) return false;
72
 
73
        $obj = json_decode($cont, true);
74
        if (!$obj) return false;
75
 
8 daniel-mar 76
        if (!isset($obj['items'])) return false;
77
 
2 daniel-mar 78
        foreach ($obj['items'] as $item) {
79
                if ($item['kind'] == 'youtube#channel') {
80
                        return $item['id'];
81
                }
82
        }
83
}
84
 
85
function yt_get_channel_id_and_stats($username) {
16 daniel-mar 86
        $cont = @file_get_contents('https://www.googleapis.com/youtube/v3/channels?key='.urlencode(yt_get_apikey()).'&forUsername='.urlencode($username).'&part=id,statistics');
2 daniel-mar 87
        if (!$cont) return false;
88
 
89
        $obj = json_decode($cont, true);
90
        if (!$obj) return false;
91
 
8 daniel-mar 92
        if (!isset($obj['items'])) return false;
93
 
2 daniel-mar 94
        foreach ($obj['items'] as $item) {
95
                if ($item['kind'] == 'youtube#channel') {
96
                        return array($item['id'], $item['statistics']);
97
                }
98
        }
99
}
100
 
101
function yt_get_channel_stats($channel_id) {
16 daniel-mar 102
        $cont = @file_get_contents('https://www.googleapis.com/youtube/v3/channels?key='.urlencode(yt_get_apikey()).'&id='.urlencode($channel_id).'&part=statistics');
2 daniel-mar 103
        if (!$cont) return false;
104
 
105
        $obj = json_decode($cont, true);
106
        if (!$obj) return false;
107
 
8 daniel-mar 108
        if (!isset($obj['items'])) return false; //totalResults could be 0
109
 
2 daniel-mar 110
        foreach ($obj['items'] as $item) {
111
                if ($item['kind'] == 'youtube#channel') {
112
                        return $item['statistics'];
113
                }
114
        }
115
}
116
 
117
function yt_get_playlist_stats($playlist_id) {
16 daniel-mar 118
        $cont = @file_get_contents('https://www.googleapis.com/youtube/v3/playlists?part=contentDetails&id='.urlencode($playlist_id).'&key='.urlencode(yt_get_apikey()));
2 daniel-mar 119
        if (!$cont) return false;
120
 
121
        $obj = json_decode($cont, true);
122
        if (!$obj) return false;
123
 
8 daniel-mar 124
        if (!isset($obj['items'])) return false;
125
 
2 daniel-mar 126
        foreach ($obj['items'] as $item) {
127
                if ($item['kind'] == 'youtube#playlist') {
16 daniel-mar 128
                        if (!isset($item['contentDetails']) || is_null($item['contentDetails'])) return false; // can happen to deleted playlists
2 daniel-mar 129
                        return $item['contentDetails'];
130
                }
131
        }
132
}
133
 
134
function yt_channel_items($channel_id, $searchterms='', $maxresults=-1) {
135
        $out = array();
136
 
137
        $next_page_token = '';
138
 
139
        do {
16 daniel-mar 140
                $cont = @file_get_contents('https://www.googleapis.com/youtube/v3/search?part=snippet&channelId='.urlencode($channel_id).(($searchterms!='') ? '&q='.urlencode($searchterms) : '').'&maxResults=50'.(($next_page_token!='') ? '&pageToken='.urlencode($next_page_token) : '').'&key='.urlencode(yt_get_apikey()));
2 daniel-mar 141
                if (!$cont) return false;
142
 
143
                $obj = json_decode($cont, true);
144
                if (!$obj) return false;
145
 
8 daniel-mar 146
                if (!isset($obj['items'])) return false;
147
 
2 daniel-mar 148
                foreach ($obj['items'] as $item) {
149
                        if ($item['id']['kind'] == 'youtube#video') {
150
                                $title    = $item['snippet']['title'];
151
                                $video_id = $item['id']['videoId'];
152
                                $out[] = array($video_id, $title);
153
                                if (($maxresults != -1) && ($maxresults == count($out))) return $out;
154
                        }
155
                }
156
 
157
                $next_page_token = isset($obj['nextPageToken']) ? $obj['nextPageToken'] : '';
158
        } while ($next_page_token != '');
159
 
160
        return $out;
161
}
162
 
163
// Acceptable order values are: date, rating, relevance(default), title, videoCount, viewCount
164
function yt_search_items($searchterms, $order='', $maxresults=-1) {
165
        $out = array();
166
 
167
        $next_page_token = '';
168
 
169
        do {
16 daniel-mar 170
                $cont = @file_get_contents('https://www.googleapis.com/youtube/v3/search?part=snippet&q='.urlencode($searchterms).(($order!='') ? '&order='.urlencode($order) : '').'&maxResults=50'.(($next_page_token!='') ? '&pageToken='.urlencode($next_page_token) : '').'&key='.urlencode(yt_get_apikey()));
2 daniel-mar 171
                if (!$cont) return false;
172
 
173
                $obj = json_decode($cont, true);
174
                if (!$obj) return false;
175
 
8 daniel-mar 176
                if (!isset($obj['items'])) return false;
177
 
2 daniel-mar 178
                foreach ($obj['items'] as $item) {
179
                        if ($item['id']['kind'] == 'youtube#video') {
180
                                $title    = $item['snippet']['title'];
181
                                $video_id = $item['id']['videoId'];
182
                                $out[] = array($video_id, $title);
183
                                if (($maxresults != -1) && ($maxresults == count($out))) return $out;
184
                        }
185
                }
186
 
187
                $next_page_token = isset($obj['nextPageToken']) ? $obj['nextPageToken'] : '';
188
        } while ($next_page_token != '');
189
 
190
        return $out;
191
}
192
 
193
function getVideoIDFromURL($url) {
194
        // Extract video ID from the URL
195
 
196
        $vid = false;
7 daniel-mar 197
        $m = null;
2 daniel-mar 198
 
199
        # Usual format
8 daniel-mar 200
        if (($vid === false) && (preg_match("@https{0,1}://(www\\.|)youtube\\.com/watch(.*)(/|&|\\?)v=([a-zA-Z0-9_-]{11})@ismU", $url, $m))) {
2 daniel-mar 201
                $vid = $m[4];
202
        }
203
 
204
        # Short format
12 daniel-mar 205
        if (($vid === false) && (preg_match("@https{0,1}://(www\\.|)youtu\\.be/([a-zA-Z0-9_-]{11})@ismU", $url, $m))) {
2 daniel-mar 206
                $vid = $m[2];
207
        }
208
 
7 daniel-mar 209
        # YouTube "Shorts"
210
        if (($vid === false) && (preg_match("@https{0,1}://(www\\.|)youtube\\.com/shorts/([a-zA-Z0-9_-]{11})@ismU", $url, $m))) {
211
                $vid = $m[2];
212
        }
213
 
2 daniel-mar 214
        return $vid;
215
}
216
 
217
function getPlaylistIDFromURL($url) {
218
        $pid = false;
219
 
220
        # Usual format
7 daniel-mar 221
        $m = null;
8 daniel-mar 222
        if (($pid === false) && (preg_match("@https{0,1}://(www\\.|)youtube\\.com/(.*)(/|&|\\?)list=(.+)&@ismU", $url.'&', $m))) {
2 daniel-mar 223
                $pid = $m[4];
224
        }
225
 
226
        return $pid;
227
}
228
 
229
function yt_check_apikey_syntax($apikey) {
230
        return preg_match('@^[a-zA-Z0-9]{39}$@', $apikey);
231
}
232
 
233
function yt_check_video_id($video_id) {
234
        return preg_match('@^[a-zA-Z0-9\-_]{11}$@', $video_id);
235
}
236
 
13 daniel-mar 237
function yt_get_channel_id_from_custom_url($custom_url) {
238
        // TODO: is there any API possibility??? API only accepts username and id ?!
239
 
240
        // https://www.youtube.com/c/SASASMR
241
        // <link rel="canonical" href="https://www.youtube.com/channel/UCp4LfMtDfoa29kTlLnqQ5Mg">
242
        // https://www.youtube.com/impaulsive
243
        // <link rel="canonical" href="https://www.youtube.com/channel/UCGeBogGDZ9W3dsGx-mWQGJA">
19 daniel-mar 244
        // https://www.youtube.com/@KosmonautMusicSpecials
245
        // <link rel="canonical" href="https://www.youtube.com/channel/UCayYOQ0DEIpcTgO9z_b-K1A">
13 daniel-mar 246
 
16 daniel-mar 247
        $cont = @file_get_contents($custom_url);
13 daniel-mar 248
        if ($cont === false) {
249
                throw new Exception("Cannot open $custom_url using file_get_contents.");
250
        }
251
        if (!preg_match('@<link rel="canonical" href="https://www.youtube.com/channel/([^"]+)">@ismU', $cont, $m)) {
252
                return false;
253
        }
254
 
255
        return $m[1];
256
}
257
 
258
function yt_get_channel_id_from_url($channel_url) {
259
        $m = null;
260
        if (preg_match("@https{0,1}://(www\\.|)youtube\\.com/user/(.*)(/|&|\\?)@ismU", $channel_url.'&', $m)) {
261
                // Username (deprecated feature. Not every channel has a username associated with it)
262
                // https://www.youtube.com/user/USERNAME
263
                $username = $m[2];
264
                $channel_id = yt_get_channel_id($username);
265
                return $channel_id;
266
        } else if (preg_match("@https{0,1}://(www\\.|)youtube\\.com/channel/(.*)(/|&|\\?)@ismU", $channel_url.'&', $m)) {
267
                // Real channel ID
268
                // https://www.youtube.com/channel/ID
269
                $channel_id = $m[2];
270
                return $channel_id;
271
        } else if (preg_match("@https{0,1}://(www\\.|)youtube\\.com/(c/){0,1}(.*)(/|&|\\?)@ismU", $channel_url.'&', $m)) {
272
                // Channel custom URL
273
                // https://www.youtube.com/NAME or https://www.youtube.com/c/NAME
274
                return yt_get_channel_id_from_custom_url($channel_url);
275
        } else {
276
                return false;
277
        }
278
}
279
 
2 daniel-mar 280
// Examples:
281
//yt_set_apikey(trim(file_get_contents(__DIR__ . '/.yt_api_key')));
282
//print_r(yt_playlist_items('PL9GbGAd-gY1pyxZJIX5MOdYdRbdweVAID'));
283
//print_r(yt_channel_items('UCjPjcMEAN64opOoNQE9GykA'));
284
//print_r(yt_channel_items('UCjPjcMEAN64opOoNQE9GykA', 'Knight'));
285
//print_r(yt_search_items('Wesley Willis', 'date', 10));