pretty code

顯示具有 PHP 標籤的文章。 顯示所有文章
顯示具有 PHP 標籤的文章。 顯示所有文章

2018年3月13日 星期二

Use PHP to download Youtube video

最近很喜歡聽 "方宥心" 唱歌
加了好幾首 "最美的歌" 到喜歡的影片
於是就想試看看如何用程式來下載影片

網路上其實有很多線上服務可以做到
不過 RD 就是要捲起袖子自己來才有 fu

網路上查了一下
大概是利用 get_video_info 這個 api 配合影片 id
先取得影片 url ,接著就可以 download

舊的作法會再取得 'sig' 並串到 url 裡面(&singature=XXX)
但根據我 2018/03/14 的測試
url 裡面其實就有 'signature'
如果沒有,用 get_video_info 也會沒有
此時這個影片可能也不用下載了
因為一定會失敗
(但是線上服務是可以的)

不過這個 api 並不是官方 release 的
故以後還能不能用並不知道
再來也不是每個影片都能下載

步驟大概如下
1. 手動在 Youtube 喜歡的影片頁面,下載該頁面原始檔
2. 程式 parse 原始檔,找到影片的 id 及 title
3. 使用 get_video_info 取得 mp4 的下載 url
4. 使用 curl 下載檔案

github 連結


<?php

function downloadVideo($id, $title, $url, $video)
{
    $fp = fopen($video, "w");
    if (!$fp) {
        echo "$id $title can't create video file \n";
        return;
    }

    $ch = curl_init();

    if ($ch === false) {
        echo "$id $title curl_init error \n";
        fclose($fp);
        return;
    }

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST , false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER , false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 600);
    curl_setopt($ch, CURLOPT_HEADER , false);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION , true);
    curl_setopt($ch, CURLOPT_AUTOREFERER , true);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36');
    curl_setopt($ch, CURLOPT_FILE, $fp);
    $con = curl_exec($ch);
    curl_close($ch);

    if ($con === false) {
        echo "$id $title download video error \n";
        fclose($fp);
        return;
    }

    fclose($fp);
}

function processVideo($id, $title, $video)
{
    $api = sprintf("http://www.youtube.com/get_video_info?video_id=%s", $id);

    $res = file_get_contents($api);

    if ($res === '') {
        echo "$id $title res is null \n";
        return;
    }

    $params = [];
    parse_str($res, $params);

    if (!isset($params['url_encoded_fmt_stream_map'])) {
        echo "$id $title url_encoded_fmt_stream_map error \n";
        return;
    }

    $s = $params['url_encoded_fmt_stream_map'];
    $streams = explode(',', $s);

    $realURL = '';
    for ($i = 0; $i < sizeof($streams); $i++) {
        $params = [];
        parse_str($streams[$i], $params);

        if (!isset($params['url']) || !isset($params['type']) || !isset($params['quality'])) {
            echo "$id $title params error \n";
            return;
        }

        $url = urldecode($params['url']);
        $type = urldecode($params['type']);
        $quality = urldecode($params['quality']);

        if (strpos($type, 'video/mp4') == 0) {
            $realURL = $url;
            if ($quality === 'medium') {
                break;
            }
        }
    }

    if ($realURL === '') {
        echo "$id $title has no mp4 type \n";
        return;
    }

    //echo "$title, $realURL\n";

    downloadVideo($id, $title, $realURL, $video);
}

// "youtube.txt" is the file that you use browser to save the page of your favorite videos.
$html = file_get_contents('youtube.txt');

$pattern = '/window\[\"ytInitialData\"\] = ({.*});/';

if (!preg_match($pattern, $html, $matchs)) {
    echo "can't find favorites \n";
    return;
}

$s = $matchs[1];

$json = json_decode($s, true);

$songs = $json['contents']['twoColumnBrowseResultsRenderer']['tabs'][0]
              ['tabRenderer']['content']['sectionListRenderer']['contents'][0]
              ['itemSectionRenderer']['contents'][0]
              ['playlistVideoListRenderer']['contents'];

for ($i = 0; $i < sizeof($songs); $i++) {
    $title = $songs[$i]['playlistVideoRenderer']['title']['simpleText'];
    $title = mb_convert_encoding($title, "big5", "utf-8");
    $title = str_replace([' ', '?'], ['', ''], $title);

    $id = $songs[$i]['playlistVideoRenderer']['videoId'];

    //echo "$id $title \n";

    $video = sprintf("%s\\%s.mp4", __DIR__, $title);

    processVideo($id, $title, $video);

    sleep(10);
}

2018年3月2日 星期五

MongoDB PHP example

最近需要測試一下 MongoDB
雖然我很喜歡 Go 語言
但還是直覺認為用 PHP 會比較方便
 
不過網路上的文章大部份都是舊的 PHP Driver 寫法(class MongoClient)
故花點時間把它整理一下

1. 新的用法有分 Low, High Level,High Level 的 code 比較精簡,但兩個都要裝
2. High Level - https://github.com/mongodb/mongo-php-library
3. Low Level - https://github.com/mongodb/mongo-php-driver
4. 兩者都是官方 release 的(官方也有提供其他語言的 Library)
5. 我使用的版本 PHP 7.0.8, MongoDB 3.6

官方說明

High Level - Query

require 'vendor/autoload.php';

$connectStr = 'mongodb://user:password@localhost:27017/databaseName';

try {
    $client = new MongoDB\Client($connectStr);

    $collection = $client->databaseName->collectionName;
    
    $records = $collection->find([]);

    foreach ($records as $item) {
        echo "_id = $item->_id \n";
    }

} catch (MongoDB\Driver\Exception\ConnectionTimeoutException $e) {
    echo $e->getMessage() . "\n";
}

High Level - Insert

require 'vendor/autoload.php';

$connectStr = 'mongodb://user:password@localhost:27017/databaseName';

try {
    $client = new MongoDB\Client($connectStr);

    $collection = $client->databaseName->collectionName;
    $collection->insertOne(['b' => 'CCC']);

} catch (MongoDB\Driver\Exception\ConnectionTimeoutException $e) {
    echo $e->getMessage() . "\n";
}


2014年6月5日 星期四

CodeIgniter trace code 1

最近想嘗試了解 CI
想知道如何實現 MVC 的架構
故花 10 分鐘簡單的 trace 一下

程式入口點是 index.php -> core/CodeIgniter.php
裡面有 1 個 $OUT 變數會去 load CI_Output 的 class
最後會呼叫 __display 或是 _display_cache 的方法(視有無 catch 而訂)
至於在 __display 裡面會 echo $output
$output 便是網頁呈現的內容