ytsearch/main.cpp
linarphy 55561b2a7f First version
Does not encode entry yet
2023-04-01 00:38:57 +02:00

126 lines
3.5 KiB
C++

#include <iostream>
#include <curl/curl.h>
#include <getopt.h>
#include "config.hpp"
/* check version */
#ifndef VERSION
#define VERSION "UNKNOWN"
#endif
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp){
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
/**
* goal: ytsearch -h|-v|search-term
*
* @example ytsearch "rick astley"
* https://www.youtube.com/watch?v=dQw4w9WgXcQ
*/
int main(int argc, char **argv)
{
if (argc < 2)
{
std::cout << "ytsearch: a search term is needed, try 'ytsearch -h' for more information" << std::endl;
return EXIT_FAILURE;
}
int opt;
std::string const video_url = { "https://www.youtube.com/watch?v=" };
std::string search_url = { "https://www.youtube.com/results?search_query=" };
while ((opt = getopt(argc, argv, "hv")) != -1)
{
switch (opt)
{
case 'h':
std::cout << "Usage: ytsearch [options...] search-term" << std::endl <<
" -h: show this help and quit" << std::endl <<
" -v: show version number and quit" << std::endl <<
std::endl <<
"Retrieve the url of a youtube video matching the searched terms" << std::endl;
return EXIT_SUCCESS;
case 'v':
std::cout << VERSION << std::endl;
return EXIT_SUCCESS;
default:
std::cout << "ytsearch: try 'ytsearch -h' for more information" << std::endl;
return EXIT_FAILURE;
}
}
search_url += argv[1];
CURL *curl;
std::string readBuffer;
curl = curl_easy_init();
if (!curl)
{
std::cout << "ytsearch: error in curl initialization" << std::endl;
return EXIT_FAILURE;
}
curl_easy_setopt(curl, CURLOPT_URL, search_url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
CURLcode res = { curl_easy_perform(curl) };
curl_easy_cleanup(curl);
if (res != 0)
{
std::cout << "ytsearch: error when fetching results: " << res << std::endl;
return EXIT_FAILURE;
}
std::string paths[4] = {
"navigationEndpoint",
"commandMetadata",
"webCommandMetadata",
"url",
};
size_t pos_start = readBuffer.find("ytInitialData");
std::string const error_api = "ytsearch: error, youtube's api changed: update ytsearch";
if (pos_start == std::string::npos or pos_start == 0)
{
std::cout << error_api << std::endl;
return EXIT_FAILURE;
}
size_t first_video = readBuffer.find("WEB_PAGE_TYPE_WATCH", pos_start);
if (first_video == std::string::npos or pos_start == 0)
{
std::cout << error_api << std::endl;
return EXIT_FAILURE;
}
while (first_video > pos_start + 60) // type is defined a little after url, we want a video
{
for (std::string name : paths)
{
pos_start = { readBuffer.find(name, pos_start) };
if (pos_start == std::string::npos or pos_start == 0)
{
std::cout << error_api << std::endl;
return EXIT_FAILURE;
}
}
}
size_t size = { pos_start + 15 }; // size of " url":"/watch?v= ", last part of paths
size_t pos_end = { readBuffer.find('"', size) };
std::cout << video_url << readBuffer.substr(size, pos_end - size) << std::endl;
return EXIT_SUCCESS;
}