123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- <?php
- if ( file_exists( __DIR__ . '/config.inc.php' ) ) {
- require( __DIR__ . '/config.inc.php' );
- } else {
- require( __DIR__ . '/sample-config.inc.php' );
- }
- require( dirname( __DIR__ ) . '/vendor/autoload.php' );
- /**
- * Gets installation homepage URL.
- *
- * @return str
- */
- function get_base_url() {
- global $config;
- if ( ! isset( $config['base_url'] ) ) {
- $base_url = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'];
- $base_url .= dirname( $_SERVER['SCRIPT_NAME'] );
- return $base_url;
- } else {
- return $config['base_url'];
- }
- }
- /**
- * Returns a changed YT URL to work on local install.
- *
- * @param str $id Video/channel ID.
- * @param str $type Type of ID provided or URL to be generated. (video|channel)
- * @param array $extra_data Extra information to be included in the URL.
- * @return str
- */
- function get_local_url( $id, $type = 'video', $extra_data = null ) {
- if ( $type == 'video' ) {
- return get_base_url() . '/watch?v=' . $id;
- } elseif ( $type == 'channel' ) {
- return get_base_url() . '/channel/' . $id;
- }
- }
- /**
- * Replaces all occurrences of YT urls from a string.
- *
- * @param str $input_string String to process.
- * @return str
- */
- function replace_urls_with_local_urls( $input_string ) {
- $base_url = get_base_url();
- $patterns = array(
- // https://www.youtube.com/watch?v=C0DPdy98e4c&feature=feedrec_grec_index
- '/https*:\/\/(www.)*youtube.com\/watch(.*)v=([a-zA-Z0-9]*)/i',
- // http://youtu.be/C0DPdy98e4c
- '/https*:\/\/youtu.be\/([a-zA-Z0-9]*)/i',
- // https://www.youtube.com/embed/C0DPdy98e4c?rel=0
- '/https*:\/\/(www.)*youtube.com\/embed\/([a-zA-Z0-9]*)/i',
- // https://www.youtube.com/user/SomeUser_Name#p/a/u/1/QdK8U-VIH_o
- '/https*:\/\/(www.)*youtube.com\/user\/([a-zA-Z0-9_-]*)/i',
- // https://youtube.com/channel/UCHDm-DKoMyJxKVgwGmuTaQA
- '/https*:\/\/(www.)*youtube.com\/channel\/([a-zA-Z0-9_-]*)/i',
- );
- $replacements = array(
- "$base_url/watch?v=$3",
- "$base_url/watch?v=$1",
- "$base_url/watch?v=$2",
- "$base_url/user/$2",
- "$base_url/channel/$2",
- );
- return preg_replace( $patterns, $replacements, $input_string );
- }
- /**
- * Convert urls in the string into clickable links.
- *
- * @source https://stackoverflow.com/a/34732417
- *
- * @param str $input_string Input string to convert.
- * @return str
- */
- function urlify_string( $input_string ) {
- return preg_replace( '/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]*(\/\S*)?/', '<a href="$0">$0</a>', $input_string );
- }
- /**
- * Returns a YT thumbnail image from a given video id
- *
- * @source https://stackoverflow.com/a/20542029
- *
- * @param str $video_id Video id to get the thumbnail for.
- * @param str $size Size of the thumbnail image to get url for. (small|medium|high)
- * @return str
- */
- function get_video_thumbnail_url( $video_id, $size = 'medium' ) {
- if ( $size == 'small') {
- return "https://i.ytimg.com/vi/{$video_id}/default.jpg";
- } elseif ( $size == 'medium') {
- return "https://i.ytimg.com/vi/{$video_id}/mqdefault.jpg";
- } elseif ( $size == 'high') {
- return "https://i.ytimg.com/vi/{$video_id}/hqdefault.jpg";
- }
- }
- /**
- * Returns proxy-enabled url for proxy (esp. for frontend).
- */
- // TODO: Implement proxy code and use throughout project.
- function proxify_url( $url ) {
- return get_base_url() . '/proxyasset.php?url=' . urlencode( $url );
- }
- /**
- * Gets the content of an URL.
- *
- * @param str $url The URL to get contents of.
- * @return str
- */
- function get_url_contents( $url ) {
- global $config;
- $ch = curl_init();
- $curl_options = array(
- CURLOPT_URL => $url,
- CURLOPT_FOLLOWLOCATION => 1,
- CURLOPT_RETURNTRANSFER => 1,
- );
- if ( $config['proxy_enabled'] ) {
- $curl_options[CURLOPT_PROXY] = "{$config['proxy_ip']}:{$config['proxy_port']}";
- if ( isset( $config['proxy_username'] ) ) {
- $curl_options[CURLOPT_PROXYUSERPWD] = "{$config['proxy_username']}:{$config['proxy_password']}";
- }
- switch( $config['proxy_type'] ) {
- case 'socks4':
- $curl_proxy_type = CURLPROXY_SOCKS4;
- break;
- case 'socks5':
- $curl_proxy_type = CURLPROXY_SOCKS5;
- break;
- case 'socks4a':
- $curl_proxy_type = CURLPROXY_SOCKS4A;
- break;
- default:
- $curl_proxy_type = CURLPROXY_HTTP;
- }
- $curl_options[CURLOPT_PROXYTYPE] = CURLPROXY_SOCKS5;
- }
- curl_setopt_array( $ch, $curl_options );
- $curl_output = curl_exec( $ch );
- curl_close( $ch );
- return $curl_output;
- }
- /**
- * Extracts json information from YT HTML.
- *
- * @param str $html Input HTML.
- * @return str|bool
- */
- function get_initial_data( $html ) {
- if ( preg_match( '/window\["ytInitialData"\]\s*=\s*(?<info>.*?);+\n/i', $html, $matches ) ) {
- $json = $matches[1];
- $json = json_decode( $json );
- return $json;
- } else {
- return false;
- }
- }
- /**
- * Extracts items from extracted json (e.g. from get_initial_data()).
- *
- * @param str $json JSON string to be processed.
- * @return array
- */
- function extract_items( $json ) {
- // Temporary storage variable for json data in array
- $items_json = array();
- // Return array
- $items = array();
- // Search data
- if ( isset( $json->contents->twoColumnSearchResultsRenderer ) ) {
- $items_json = @$json->contents->twoColumnSearchResultsRenderer->primaryContents->sectionListRenderer->contents[0]->itemSectionRenderer->contents;
- // Go through contents inside sub-arrays (for trending data etc.)
- // Path: @$json->contents->twoColumnBrowseResultsRenderer->tabs[0]->tabRenderer->content->sectionListRenderer->contents[0]->itemSectionRenderer->contents[0]->shelfRenderer->content->expandedShelfContentsRenderer->items;
- } elseif ( isset( $json->contents->twoColumnBrowseResultsRenderer ) ) {
- foreach ( @$json->contents->twoColumnBrowseResultsRenderer->tabs[0]->tabRenderer->content->sectionListRenderer->contents as $key => $sub_items ) {
- $nested_items = @$sub_items->itemSectionRenderer->contents[0]->shelfRenderer->content->expandedShelfContentsRenderer->items;
- if ( ! empty($nested_items) ) {
- $items_json = array_merge( $items_json, $nested_items );
- }
- }
- // Channel playlist (and also playlist??)
- } elseif ( isset( $json->contents->twoColumnWatchNextResults ) ) {
- //$items_json = @$json->contents->twoColumnWatchNextResults->results->results->contents; // not used
- $items_json = @$json->contents->twoColumnWatchNextResults->playlist->playlist->contents;
- }
- if ( ! empty( $items_json ) ) {
- foreach ( $items_json as $index => $item ) {
- $first_key = @array_keys( get_object_vars($item) )[0];
- if ( isset( $item->videoRenderer ) ) {
- $items[] = array(
- 'item_type' => 'video',
- 'video_id' => $item->videoRenderer->videoId,
- 'video_url' => get_local_url( $item->videoRenderer->videoId, 'video' ),
- 'thumbnail_url' => $item->videoRenderer->thumbnail->thumbnails[1]->url,
- 'title' => $item->videoRenderer->title->runs[0]->text,
- 'title_a11y' => $item->videoRenderer->title->accessibility->accessibilityData->label,
- 'description' => $item->videoRenderer->descriptionSnippet->runs[0]->text . $item->videoRenderer->descriptionSnippet->runs[2]->text,
- 'length' => $item->videoRenderer->lengthText->simpleText,
- 'author_name' => $item->videoRenderer->ownerText->runs[0]->text,
- 'author_url' => get_base_url() . $item->videoRenderer->ownerText->runs[0]->navigationEndpoint->browseEndpoint->canonicalBaseUrl,
- 'channel_id' => get_base_url() . $item->videoRenderer->ownerText->runs[0]->navigationEndpoint->browseEndpoint->browseId,
- 'views_text' => $item->videoRenderer->shortViewCountText->simpleText,
- );
- } elseif ( isset( $item->playlistPanelVideoRenderer ) ) {
- $video_id = @$item->{$first_key}->videoId;
- $items[] = array(
- 'item_type' => 'video',
- 'video_id' => $video_id,
- 'video_url' => get_local_url( $video_id, 'video' ),
- 'thumbnail_url' => $item->{$first_key}->thumbnail->thumbnails[1]->url,
- 'title' => $item->{$first_key}->title->simpleText,
- 'title_a11y' => $item->{$first_key}->title->accessibility->accessibilityData->label,
- 'length' => $item->{$first_key}->lengthText->simpleText,
- 'key_test' => $first_key,
- );
- } elseif ( isset( $item->channelRenderer ) ) {
- $items[] = array(
- 'item_type' => 'channel',
- 'channel_name' => $item->channelRenderer->title->simpleText,
- 'channel_id' => $item->channelRenderer->channelId,
- 'channel_url' => get_local_url( $item->channelRenderer->channelId, 'channel' ),
- 'channel_thumbnail_url' => $item->channelRenderer->thumbnail->thumbnails[1]->url,
- 'subscriber_count' => $item->channelRenderer->subscriberCountText->simpleText,
- 'auto_generated' => ( isset( $item->channelRenderer->videoCountText ) ) ? false : true,
- 'video_count' => $item->channelRenderer->videoCountText->runs[0]->text,
- 'description' => $item->channelRenderer->descriptionSnippet,
- );
- // TODO: Process more types such as playlists etc. (https://github.com/iv-org/invidious/blob/db83ede73ca38cd4c661e96b8f9823db99d9f5c2/src/invidious/helpers/helpers.cr#L250)
- }
- }
- } else {
- throw new Exception( 'Contents node not found on extracted JSON' );
- }
- return $items;
- }
- /**
- * Friendly display of date and time (e.g. 1 month ago).
- *
- * @source https://stackoverflow.com/a/38897003
- *
- * @param int $time_ago The time passed as input.
- * @return str
- */
- function time_ago( $time_ago ) {
- $time_ago = strtotime( $time_ago ) ? strtotime( $time_ago ) : $time_ago;
- $time = time() - $time_ago;
- switch( $time ):
- // seconds
- case $time <= 60;
- return 'less than a minute ago';
- // minutes
- case $time >= 60 && $time < 3600;
- return (round($time/60) == 1) ? 'a minute' : round($time/60) . ' minutes ago';
- // hours
- case $time >= 3600 && $time < 86400;
- return (round($time/3600) == 1) ? 'an hour ago' : round($time/3600) . ' hours ago';
- // days
- case $time >= 86400 && $time < 604800;
- return (round($time/86400) == 1) ? 'a day ago' : round($time/86400) . ' days ago';
- // weeks
- case $time >= 604800 && $time < 2600640;
- return (round($time/604800) == 1) ? 'a week ago' : round($time/604800) . ' weeks ago';
- // months
- case $time >= 2600640 && $time < 31207680;
- return (round($time/2600640) == 1) ? 'a month ago' : round($time/2600640) . ' months ago';
- // years
- case $time >= 31207680;
- return (round($time/31207680) == 1) ? 'a year ago' : round($time/31207680) . ' years ago' ;
- endswitch;
- }
|