zoukankan      html  css  js  c++  java
  • hls 直播视频下载器

    -----------------------------------------------------------------------------------
    #include <curl/curl.h>
    #include <string>
    #include <iostream>
    #include <unistd.h>
    #include <stdio.h>
    #include <conio.h>
    #include <regex>
    #include <iterator>
    #include <io.h>
    #include <fstream>
    #include <getopt.h>
    #include <list>
    
    using namespace std;
    
    static char m3u8[1024];
    static char g_useragent[1024];
    static char g_proxy[100];
    static char g_maxlen[50];
    static double g_maxduration = 0.0;
    //static size_t totalsize = 0;
    static unsigned long long int totalsize = 0;
    static char strtotalsize[100];
    
    static string baseurl;
    static int isDEBUG = 0;
    
    static list<pthread_t>listpthreads;
    
    void putmsg(const char * msg)
    {
        if (!msg) return;
        FILE * m3u8h = fopen(m3u8, "ab+");
        if (m3u8h) {
            fwrite(msg, 1, strlen(msg), m3u8h);
            fwrite("
    ", 1, 2, m3u8h);
            fclose(m3u8h);
        }
    }
    
    typedef struct targ {
        char url[1024];
        char fn[1024];
    } targ;
    
    size_t writeFunction(void *ptr, size_t size, size_t nmemb, string* data) {
        data->append((char*) ptr, size * nmemb);
        return size * nmemb;
    }
    size_t writeFunction2(void *ptr, size_t size, size_t nmemb, string* data) {
        data->append((char*) ptr, size * nmemb);
        return size * nmemb;
    }
    static void deleteNode(pthread_t tid)
    {
        list<pthread_t>::iterator it;
        for (it = listpthreads.begin(); it != listpthreads.end(); it++)
        {
            if (tid == *it)
            {
                listpthreads.erase(it);
            }
        }
    }
    
    static void waitThreads()
    {
        list<pthread_t>::iterator it;
        cout << "Waiting for the end of download threads" << endl;
        for (it = listpthreads.begin(); it != listpthreads.end(); it++)
        {
            //cout << "      thread ID " << *it << endl;
            pthread_join(*it, NULL);
        }
    }
    
    static void * downts(void *arg)
    {
        targ *ta= (targ *)arg;
        string fullurl = ta->url;;
        string fn = ta->fn;
        
        //cout << fn << endl;
        auto curl = curl_easy_init();
        if (curl) {
            curl_easy_setopt(curl, CURLOPT_URL, fullurl.c_str());
            curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
            //curl_easy_setopt(curl, CURLOPT_USERPWD, "user:pass");
            curl_easy_setopt(curl, CURLOPT_USERAGENT, g_useragent);
            curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
            curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
            curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 10000L);
            if(strlen(g_proxy)>0)
                curl_easy_setopt(curl, CURLOPT_PROXY, g_proxy);
            string response_string;
            string header_string;
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction2);
            curl_easy_setopt(curl, CURLOPT_WRITEDATA,  & response_string);
            curl_easy_setopt(curl, CURLOPT_HEADERDATA,  & header_string);
    
            char * url;
            long response_code;
            double elapsed;
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE,  & response_code);
            curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME,  & elapsed);
            curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL,  & url);
    
            CURLcode res = curl_easy_perform(curl);
            if (res != CURLE_OK)
            {
                if(isDEBUG) fprintf(stderr, "thread curl_easy_perform() failed: %s
    ", curl_easy_strerror(res));
                if(isDEBUG) cout << "pthread remove " << fn << endl;
                cout << "*** failed [" << fn << "] ***" <<endl;
                remove(fn.c_str());
            }
            else
            {
                if(isDEBUG) cout << "thread response_code " << response_code << endl;
                if(isDEBUG) cout << "thread response_string size " << response_string.size() << endl;
                //cout << "elapsed       " << elapsed << endl;
                //cout << "effective url " << url << endl;
                //cout << "response_string 
    " << response_string << endl;
                //cout << "header_string 
    " << header_string << endl;
                if( response_string.size() > 0 )
                {
                    //cout << fn << endl;
                    FILE *tsh = fopen(fn.c_str(), "wb");
                    if(tsh)
                    {
                        fwrite(response_string.c_str(), 1, response_string.size(), tsh);
                        fclose(tsh);
                    }
                    totalsize += response_string.size();
                    if(totalsize<1024)                sprintf(strtotalsize,"%10ld    ", totalsize);
                    else if(totalsize<1024*1024)      sprintf(strtotalsize,"%10.03f KB", (double)totalsize/1024);
                    else if(totalsize<1024*1024*1024) sprintf(strtotalsize,"%10.03f MB", (double)totalsize/1024/1024);
                    else                              sprintf(strtotalsize,"%10.03f GB", (double)totalsize/1024/1024/1024);
                }
                else
                {
                    cout << "*** response error [" << fn << "] ***" <<endl;
                    remove(fn.c_str());
                }
            }
        }
        curl_easy_cleanup(curl);
        curl = NULL;
        if(isDEBUG) cout << "pthread exit" << endl;
        deleteNode(pthread_self());
        pthread_exit(NULL);
        return NULL;    
    }
    
    string getbaseurl(string url)
    {
        string text=url;
        string pattern = "((.*)/)";
        regex express(pattern);
        regex_iterator<string::const_iterator> begin(text.cbegin(), text.cend(), express);
        for (auto iter = begin; iter != sregex_iterator(); iter++)
        {
            //cout << iter->str() << endl;
            return iter->str();
        }
    }
    
    string getindex(string text)
    {
        string ret = "";
        string pattern = "([0-9]{1,15})";
        regex express(pattern);
        regex_iterator<string::const_iterator> begin(text.cbegin(), text.cend(), express);
        for (auto iter = begin; iter != sregex_iterator(); iter++)
        {
            ret = iter->str();
        }
        if (ret == "" || ret.size() <= 0 ) return text;
        return ret;
    }
    
    static double duration = 0.0;
    static char strduration[100];
    static unsigned int dlidx = 1;
    static int isMAX = 0;
    double getlen(string text)
    {
        double rd = 0.0;
        string ret = "";
        string pattern = "([0-9.]{1,8})";
        regex express(pattern);
        regex_iterator<string::const_iterator> begin(text.cbegin(), text.cend(), express);
        for (auto iter = begin; iter != sregex_iterator(); iter++)
        {
            ret = iter->str();
        }
        if (ret == "" || ret.size() <= 0 ) return 0.0;
        rd = atof(ret.c_str());
        duration = duration + rd;
        
        int dhh=(unsigned int)(duration/3600);
        int dmm=(unsigned int)(duration)%3600/60;
        int dss=(unsigned int)(duration)%3600%60;
        int dms=(unsigned int)(duration*1000)%1000;
        sprintf(strduration,"%4d %5.03f %02d:%02d:%02d.%03d",
            dlidx, rd, dhh, dmm, dss, dms);
        dlidx++;
        if(g_maxduration != 0.0 && duration >= g_maxduration)
            isMAX = 1;
        return rd;
    }
    
    void string_replace( string &strBig, const string &strsrc, const string &strdst)
    {
        string::size_type pos = 0;
        string::size_type srclen = strsrc.size();
        string::size_type dstlen = strdst.size();
    
        while( (pos=strBig.find(strsrc, pos)) != string::npos )
        {
            strBig.replace( pos, srclen, strdst );
            pos += dstlen;
        }
    }
    
    static unsigned int dlcount = 0;
    static unsigned int firstdl = 0;
    static unsigned int dladdcount = 0;
    static unsigned int checknodowncount = 0;
    
    void gettsurl(string str)
    {
        string fullurl;
        string text=str;
        if(dlcount==0)
        {
            string pattern = "(#EXT-X(.*)|#EXTM3U)";
            regex express(pattern);
            regex_iterator<string::const_iterator> begin(text.cbegin(), text.cend(), express);
            for (auto iter = begin; iter != sregex_iterator(); iter++)
            {
                cout << iter->str() << endl;
                putmsg(iter->str().c_str());
            }
        }
        //string pattern = "((.*).ts)";
        string pattern = "(#EXTINF:(.*)|(.*).ts)";
        regex express(pattern);
        regex_iterator<string::const_iterator> begin(text.cbegin(), text.cend(), express);
        string extinfo;
    
        for (auto iter = begin; iter != sregex_iterator(); iter++)
        {
            fullurl = baseurl + iter->str();
            //cout << fullurl << endl;
            //cout << iter->str() << endl;
            //downts(fullurl, iter->str());
            if( iter->str().substr(0,1) == "#" )
            {
                extinfo = iter->str();
            }
            else 
            {
                string newfn = getindex(iter->str());
                string_replace(newfn, "/", "-");
                
                if(newfn.size()<=4)
                {
                    char tmp[50];
                    sprintf(tmp, "%05d", atoi(newfn.c_str()));
                    newfn = tmp;
                }
                newfn = newfn + ".ts";
                
                if (access(newfn.c_str(), 0)) {
                    getlen(extinfo);
                    cout << " " << strduration << " " << strtotalsize << " " << newfn << endl;
                    //cout << newfn << endl;
                    putmsg(extinfo.c_str());
                    putmsg(newfn.c_str());
                    FILE * tsh = fopen(newfn.c_str(), "wb");
                    if (tsh) {
                        fclose(tsh);
                    }
                    pthread_t thread;
                    targ ta;
                    memset( & ta, 0x00, sizeof(ta));
                    strcpy(ta.url, fullurl.c_str());
                    strcpy(ta.fn, newfn.c_str());
                    pthread_create( & thread, NULL, downts,  & ta);
                    listpthreads.push_back(thread);
                    dladdcount ++;
                }
            }
        }
        dlcount++;
    }
    
    void help()
    {
        cout << "Usage: hlslivedl [options] -i [http://...m3u8]" << endl;
        cout << "    -o set output filename" << endl;
        cout << "    -p set proxy http: https: socks4: socks4a: socks5: socks5h:" << endl;
        cout << "    -u set useragent" << endl;
        cout << "    -t set duration seconds" << endl;
        cout << "    -d debug mode" << endl;
        cout << "    Press [Q] to stop download" << endl;
        cout << "    Version 1.0.7 by NLSoft 2020.07" << endl;
    }
    
    int main(int argc, char** argv) {
        CURLcode res;
        static char urlm3u8[1024];
        memset(urlm3u8,0,1024);
        memset(g_proxy,0,100);
        memset(strduration,0,100);
        memset(strtotalsize,0,100);
        strcpy(strtotalsize, "             ");
    
        if(argc == 1)
        {
            help();
            exit(0);
        }
        strcpy(m3u8, "_index.m3u8");
        strcpy(g_useragent, "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1");
        
        int option_index = 0;
        char * user_name = NULL;
        while ((option_index = getopt(argc, argv, "p:o:u:i:t:d")) != -1) {
            switch (option_index) {
            case 'o':
                strcpy(m3u8, optarg);
                if(isDEBUG) printf("output filename is %s
    ", m3u8);
                break;
            case 'u':
                strcpy(g_useragent, optarg);
                if(isDEBUG) printf("useragent is %s
    ", g_useragent);
                break;
            case 'p':
                strcpy(g_proxy, optarg);
                if(isDEBUG) printf("proxy is %s
    ", g_proxy);
                break;
            case 'i':
                strcpy(urlm3u8, optarg);
                if(isDEBUG) printf("m3u8 url is %s
    ", urlm3u8);
                break;
            case 't':
                strcpy(g_maxlen, optarg);
                g_maxduration = atof(g_maxlen);
                if(isDEBUG) printf("g_maxlen is %s
    ", g_maxlen);
                break;
            case 'd':
                isDEBUG = 1;
                if(isDEBUG) printf("select debug mode
    ");
                break;
            }
        }
    
        if(strlen(urlm3u8)<=0)
        {
            help();
            exit(0);
        }
    
        FILE * m3u8h = fopen(m3u8, "wb");
        if (m3u8h) {
            fclose(m3u8h);
        }
    
        curl_global_init(CURL_GLOBAL_DEFAULT);    
        baseurl = getbaseurl(urlm3u8);
        if(isDEBUG) cout << baseurl << endl;
        auto curl = curl_easy_init();
        if (curl) {
            while (1) {
                dladdcount = 0;
                //curl_easy_setopt(curl, CURLOPT_URL, "https://api.github.com/repos/whoshuu/cpr/contributors?anon=true&key=value");
                curl_easy_setopt(curl, CURLOPT_URL, urlm3u8);
                curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
                //curl_easy_setopt(curl, CURLOPT_USERPWD, "user:pass");
                curl_easy_setopt(curl, CURLOPT_USERAGENT, g_useragent);
                curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
                curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
                curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
                curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
                curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 5000L);
                if(strlen(g_proxy)>0)
                    curl_easy_setopt(curl, CURLOPT_PROXY, g_proxy);
    
                string response_string;
                string header_string;
                curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction);
                curl_easy_setopt(curl, CURLOPT_WRITEDATA,  & response_string);
                curl_easy_setopt(curl, CURLOPT_HEADERDATA,  & header_string);
    
                char * url;
                long response_code;
                double elapsed;
                curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE,  & response_code);
                curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME,  & elapsed);
                curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL,  & url);
    
                res = curl_easy_perform(curl);
                if (res != CURLE_OK)
                {
                    if(isDEBUG) fprintf(stderr, "main curl_easy_perform() failed: %s
    ", curl_easy_strerror(res));
                }
                else 
                {
                    if(isDEBUG) cout << "main response_code " << response_code << endl;
                    //cout << "elapsed       " << elapsed << endl;
                    //cout << "effective url " << url << endl;
                    //cout << "response_string 
    " << response_string << endl;
                    //cout << "header_string 
    " << header_string << endl;
    
                    if( response_code == 0 ) continue;
                    if( response_code == 200 )
                    {
                        // skip first ts
                        if(firstdl != 0)
                            gettsurl(response_string);
                        firstdl ++;
                    }
                    else
                    {
                        waitThreads();
                        if(isDEBUG) cout << "#EXT-X-ENDLIST
    " << endl;
                        putmsg("#EXT-X-ENDLIST");
                        if(isDEBUG) cout << "main response_code " << response_code << "
    " << endl;
                        break;
                    };
                }
                char exitflag = '';
                if (_kbhit())
                {
                    exitflag = _getch();
                    if (exitflag == 'q' || exitflag == 'Q')
                    {
                        waitThreads();
                        if(isDEBUG) cout << "#EXT-X-ENDLIST
    " << endl;
                        putmsg("#EXT-X-ENDLIST");
                        break;
                    }
                }
                
                if(dladdcount>0) checknodowncount = 0;
                else checknodowncount ++;
                //printf("dladdcount %d checknodowncount %ld
    ", dladdcount, checknodowncount);
                
                // No files were downloaded in 60 seconds
                if(checknodowncount>=60 || isMAX)
                {
                    waitThreads();
                    if(isDEBUG) cout << "#EXT-X-ENDLIST
    " << endl;
                    putmsg("#EXT-X-ENDLIST");
                    break;
                }
                usleep(1000 * 1000);
                //sleep(1);
            }
        }
        curl_easy_cleanup(curl);
        curl = NULL;
        curl_global_cleanup();
        if(isDEBUG) cout << "main exit" << endl;
        return 0;
    }
    -----------------------------------------------------------------------------------
    echo g++ hlslivedl.cpp
    g++ -w hlslivedl.cpp -o hlslivedl.exe -DCURL_STATICLIB -ID:/MSYS/local/include -LD:/MSYS/local/lib -lcurl -lssl -lcrypto -lssl -lcrypto -lgdi32 -lwldap32 -lz -lws2_32 -lpsapi -lws2_32 -lmswsock -lshlwapi -lcrypt32 -static
    
    echo strip hlslivedl.exe
    strip hlslivedl.exe
    -----------------------------------------------------------------------------------
  • 相关阅读:
    Linux和Docker常用命令
    学习docker on windows (1): 为什么要使用docker
    使用xUnit为.net core程序进行单元测试(4)
    使用xUnit为.net core程序进行单元测试(3)
    高效地使用搜索引擎
    充分条件和必要条件
    上海全球“编程一小时”活动记
    C#.NET股票历史数据采集,【附18年历史数据和源代码】
    R学习笔记 ---- 系列文章
    平方根的原理
  • 原文地址:https://www.cnblogs.com/nlsoft/p/13347294.html
Copyright © 2011-2022 走看看