Просмотр исходного кода

Добавлен класс ядра приложения

Вадим Королёв 1 год назад
Родитель
Сommit
a07743a81d

+ 21 - 13
lib/YoutubeApi/youtubeapi.cpp

@@ -1,15 +1,15 @@
 #include "youtubeapi.hpp"
 
 namespace ytapi {
-    
-    void youtubeInit(std::string key) {
-        api_key = key;
-    }
 
-    bool performCurlRequest(std::string url, std::stringstream* response) {
-        // Добавляем ключ к каждому запросу
+    bool performCurlRequest(
+        std::string url,
+        std::string key,
+        std::stringstream* response)
+    {
+        // Добавляем ключ авторизации к запросу
         url += "&key=";
-        url += api_key;
+        url += key;
         
         CURL* handle = curl_easy_init();
         if (!handle) return false;
@@ -67,7 +67,7 @@ namespace ytapi {
         return written;
     }
 
-    std::vector<std::string> getVideoIDsByQuery(std::string query) {
+    std::vector<std::string> getVideoIDsByQuery(std::string query, std::string key) {
         std::stringstream response;
         std::string url;
 
@@ -75,7 +75,7 @@ namespace ytapi {
         url += "https://youtube.googleapis.com/youtube/v3/search?type=video&part=snippet&maxResult=25&q=";
         url += urlencode(query);
         
-        performCurlRequest(url, &response);
+        performCurlRequest(url, key, &response);
         return getVideoIDsFromJSON(response.str());
     }
 
@@ -93,16 +93,17 @@ namespace ytapi {
         return output;
     }
 
-    video_result getVideoByYTID(std::string yt_id) {
+    // TODO: https://www.returnyoutubedislike.com/
+    Video getVideoByYTID(std::string yt_id, std::string key) {
         std::stringstream response;
-        std::stringstream response_rating;
+        //std::stringstream response_rating;
         std::string url;
-        video_result output;
+        Video output;
 
         // -- Общие сведения --
         url = "https://youtube.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id=";
         url += yt_id;
-        performCurlRequest(url, &response);
+        performCurlRequest(url, key, &response);
 
         auto json = nlohmann::json::parse(response);
         auto item = json["items"][0];
@@ -149,4 +150,11 @@ namespace ytapi {
     {
         performCurlDownload(url, save_path);
     }
+
+    void setVideoRating(std::string ytid, int rating, std::string secret)
+    {
+        std::string url = "https://
+        performCurlRequest(
+        );
+    }
 }

+ 18 - 10
lib/YoutubeApi/youtubeapi.hpp

@@ -5,14 +5,13 @@
 #include <vector>
 #include <sstream>
 #include <nlohmann/json.hpp>
-#include <iostream>
 #include <Database/entities.hpp>
 #include <Database/database.hpp>
 
 namespace ytapi {
     
     // Детали видео - необработанные
-    struct video_result {
+    struct Video {
         std::string     yt_id;          // ID на YouTube
         Glib::ustring   title;          // Название
         Glib::ustring   description;    // Описание
@@ -25,12 +24,6 @@ namespace ytapi {
         std::string     big_thumbnail;  // URL самого большого превью
     };
     
-    // API ключ
-    static std::string api_key;
-
-    // Записывает ключ API в api_key
-    void youtubeInit(std::string key);
-    
     // Функция записи данных в std::ostringstream
     size_t writeHttpToString(char* ptr, size_t size, size_t nmemb, void *userdata);
 
@@ -48,14 +41,23 @@ namespace ytapi {
     std::vector<std::string> getVideoIDsByQuery(std::string query);
 
     // Возвращает объект видео, получив данные по API
-    video_result getVideoByYTID(std::string yt_id);
+    // ytid - id видео на YouTube
+    // key - ключ авторизации
+    Video getVideoByYTID(std::string ytid, std::string key);
 
     // Возвращает список видео id из JSON списка видео
     std::vector<std::string> getVideoIDsFromJSON(std::string input);
 
     // Подгатавливает, настраивает и выполняет CURL запрос. Возвращает
     // успешность вызова
-    bool performCurlRequest(std::string url, std::stringstream* response);
+    // url - адрес запроса
+    // key - oAuth ключ клиента
+    // response - указатель на строковый поток, в который записывается ответ
+    // запроса
+    bool performCurlRequest(
+        std::string url,
+        std::string key,
+        std::stringstream* response);
 
     // Подгатавливает, настраивает и выполняет CURL запрос на скачивание файла
     // Файл сохранится в save_path
@@ -71,4 +73,10 @@ namespace ytapi {
     // url: ссылка на превью
     // save_path: путь сохранения на диске
     void downloadThumbnail(std::string url, std::string save_path);
+
+    // Устанавливает оценку видео
+    // ytid - видео с каким ytid ставится оценка
+    // rating - оценка видео (0 - нет, 1 - лайк, 2 - дизлайк)
+    // secret - ключ пользователя
+    void setVideoRating(std::string ytid, int rating, std::string secret);
 }

+ 1 - 1
src/YTMPV/PrefWindow.hpp

@@ -13,7 +13,7 @@ namespace components {
         PrefWindow(
             BaseObjectType* cobject,
             const Glib::RefPtr<Gtk::Builder>& refBuilder);
-        static PrefWindow* create(Gtk::Window& parent);
+        static PrefWindow* create(Gtk::Window& parent, core::Core appcore);
 
     protected:
         Glib::RefPtr<Gtk::Builder> m_builder;

+ 8 - 13
src/YTMPV/app.cpp

@@ -8,15 +8,14 @@
 
 namespace components {
 
-    App::App()
+    App::App(core::Core appcore)
         : Gtk::Application("com.github.aquadim.YTMPV")
-        , m_current_yt_id()
-    {
-    }
+        , m_core(appcore)
+    {}
 
-    Glib::RefPtr<App> App::create()
+    Glib::RefPtr<App> App::create(core::Core appcore)
     {
-        auto app = Glib::make_refptr_for_instance<App>(new App());
+        auto app = Glib::make_refptr_for_instance<App>(new App(appcore));
         return app;
     }
 
@@ -53,7 +52,7 @@ namespace components {
     MainWindow* App::createMainWindow()
     {
         // Создание окна и привязка обработчика к его сигналу скрытия
-        auto appwindow = MainWindow::create();
+        auto appwindow = MainWindow::create(this);
         add_window(*appwindow);
         appwindow->signal_hide().connect(
             [appwindow](){ delete appwindow; });
@@ -63,7 +62,8 @@ namespace components {
     PrefWindow* App::createPrefWindow()
     {
         auto prefwindow = PrefWindow::create(
-            *get_active_window()
+            *get_active_window(),
+            &this->m_core
         );
         add_window(*prefwindow);
         prefwindow->signal_hide().connect(
@@ -98,9 +98,4 @@ namespace components {
         std::string url = "https://youtu.be/" + core::AppState::current_yt_id;
         m_main_window->get_clipboard()->set_text(url);
     }
-
-    void App::setCurrentYTID(std::string yt_id)
-    {
-        this->m_current_yt_id = yt_id;
-    }
 }

+ 6 - 8
src/YTMPV/app.hpp

@@ -10,22 +10,20 @@ namespace components {
     // Класс приложения
     class App: public Gtk::Application
     {
-    public:
-        static Glib::RefPtr<App> create();
 
-        // Устанавливает текущий id видео
-        void setCurrentYTID(std::string yt_id);
+    public:
+        // Создаёт ссылку на приложение
+        // secret_key - oAuth2.0 ключ пользователя
+        static Glib::RefPtr<App> create(core::Core appcore);
 
     protected:
-        App();
+        // secret_key - oAuth2.0 ключ пользователя
+        App(core::Core appcore);
 
         void on_startup() override;
 
         void on_activate() override;
 
-        // ID "выбранного" на данный момент видео
-        std::string m_current_yt_id;
-
         // Закрывает приложение
         void onActionQuit();
 

+ 56 - 52
src/YTMPV/core.cpp

@@ -1,25 +1,64 @@
 #include "core.hpp"
-#include <giomm/icon.h>
-#include <gtkmm/image.h>
-#include <gtkmm/label.h>
-#include <string>
+#include <nlohmann/json.hpp>
+#include <YoutubeApi/youtubeapi.hpp>
+#include <Database/database.hpp>
+
+using json = nlohmann::json;
 
 namespace core {
 
-    std::string AppState::current_yt_id = "";
-    
-    db::video getVideoByYTID(std::string yt_id)
+    Core::Core(
+        std::string client_secret,
+        std::string config_path,
+        int quality,
+        bool launch_fullscreen,
+        bool launch_ontop,
+        bool launch_miniplayer,)
+        // Инициализация
+        : m_client_secret(client_secret)
+        , m_selected_ytid("")
+        , m_config_path(config_path)
+        , m_db_path(db_path)
+        , m_quality(quality)
+        , m_launch_fullscreen(launch_fullscreen),
+        , m_launch_ontop(launch_ontop),
+        , m_launch_miniplayer(launch_miniplayer),
+    {}
+
+    Core::setQuality(int quality)
+    {
+        this->m_quality = quality;
+    }
+
+    Core::saveConfig()
+    {
+        // Сериализация
+        json j_object = {
+            {"quality",     this->m_quality},
+            {"fullscreen",  this->m_launch_fullscreen},
+            {"ontop",       this->m_launch_ontop},
+            {"miniplayer",  this->m_launch_miniplayer},
+        };
+        std::string dump = j_object.dump(1, '\t');
+
+        // Запись в файл
+        std::ofstream config(m_config_path);
+        config << dump;
+        config.close();
+    }
+
+    db::video Core::getVideoByYTID(std::string ytid)
     {
         db::video output;
 
         // 1. Проверить есть ли видео в базе данных
-        if (db::getVideoByYTID(yt_id, &output)) {
+        if (db::getVideoByYTID(ytid, &output)) {
             return output;
         }
 
         // Видео в базе данных нет. Получаем информацию о нём через ytapi
-        ytapi::video_result found_video = ytapi::getVideoByYTID(yt_id);
-        output.yt_id        = yt_id;
+        ytapi::Video found_video = ytapi::getVideoByYTID(ytid);
+        output.ytid         = ytid;
         output.title        = found_video.title;
         output.description  = found_video.description;
         output.published_at = found_video.published_at;
@@ -31,7 +70,7 @@ namespace core {
         db::author video_author;
         if (!db::getAuthorByYTID(found_video.author_yt_id, &video_author)) {
             // Автора в базе данных нет. Добавляем
-            video_author.yt_id = found_video.author_yt_id;
+            video_author.ytid = found_video.author_yt_id;
             video_author.name = found_video.author_name;
             db::addAuthor(&video_author);
         }
@@ -43,50 +82,15 @@ namespace core {
         return output;
     }
 
-    std::string getYTDLFormat(int height)
-    {
-        return "bestvideo[height<=?"+std::to_string(height)+"]+bestaudio/best";
-    }
-
-    std::string Settings::s_config_path("");
-    std::string Settings::s_api_key("");
-    int Settings::s_quality(0);
-    
-    void Settings::setConfigPath(std::string config_path)
-    {
-        s_config_path = config_path;
-    }
-
-    void Settings::setApiKey(std::string api_key)
-    {
-        ytapi::youtubeInit(api_key);
-        s_api_key = api_key;
-    }
-
-    void Settings::setQuality(int quality)
-    {
-        s_quality = quality;
-    }
-
-    std::string Settings::getConfigPath()
-    {
-        return s_config_path;
-    }
-
-    std::string Settings::getApiKey()
-    {
-        return s_api_key;
-    }
-
-    int Settings::getQuality()
+    std::string Core::getYTDLFormat()
     {
-        return s_quality;
+        return "bestvideo[height<=?"+std::to_string(m_quality)+"]+bestaudio/best";
     }
 
-    // Устанавливает оценку в бд и посылает запрос на оценку
-    void rateVideo(std::string yt_id, int rating)
+    // Устанавливает оценку в БД и посылает запрос на оценку
+    void rateVideo(std::string ytid, VideoRating rating)
     {
-        db::setVideoRating(yt_id, rating);
-        //ytapi::setVideoRating(yt_id, rating);
+        db::setVideoRating(ytid, static_cast<int>(rating));
+        ytapi::setVideoRating(ytid, static_cast<int>(rating), m_client_secret);
     }
 }

+ 70 - 30
src/YTMPV/core.hpp

@@ -1,46 +1,86 @@
 #pragma once
-#include <YoutubeApi/youtubeapi.hpp>
-#include <Database/database.hpp>
+
+#include <Database/entities.hpp>
 #include <fstream>
 #include <iostream>
 #include <string>
+#include <vector>
 
-namespace core {
-    
-    // Возвращает объект видео по его Youtube id
-    db::video getVideoByYTID(std::string yt_id);
+// Реализация приложения без графики
+// Содержит функции и переменные, которые необходимы во всех компонентах
+// Класс отделён от класса App для улучшенной поддержки тестирования
+// TODO: тестирование класса
 
-    // Возвращает строку формата yt-dl
-    std::string getYTDLFormat(int height);
+namespace core
+{
 
-    // Оценивает видео
-    // rating: 0 - нет, 1 - лайк, 2 - дизлайк
-    void rateVideo(std::string yt_id, int rating);
+    enum class VideoRating
+    {
+        NONE,
+        LIKE,
+        DISLIKE,
+    };
 
-    class AppState
+    class Core
     {
         public:
-        // ID "выбранного" на данный момент видео
-        static std::string current_yt_id;
-    };
+            Core(
+                std::string client_secret,
+                std::string config_path,
+                int quality,
+                bool launch_fullscreen,
+                bool launch_ontop,
+                bool launch_miniplayer,
+            );
+
+            // Устанавливает качество видео
+            void setQuality(int quality);
+
+            // Сохраняет настройки в файл по пути m_config_path
+            void saveConfig();
+
+            // Возвращает объект видео по его ytid
+            db::video getVideoByYTID(std::string ytid);
+
+            // Возвращает строку формата yt-dl в соответствии с m_quality
+            std::string getYTDLFormat();
+
+            // Оценивает видео
+            // rating: 0 - нет, 1 - лайк, 2 - дизлайк
+            // Если оценка текущего видео такова же как и устанавливаемая,
+            // запрос не произойдёт в целях экономии квоты API
+            void rateVideo(std::string ytid, VideoRating rating);
+
+            // Возвращает параметры запуска MPV
+            std::vector getMPVLaunchParams();
+
+        protected:
+            // oAuth-ключ пользователя
+            std::string m_client_secret;
+
+            // ytid "выбранного" видео
+            // Необходимо для:
+            // - Запуска MPV
+            // - Копирования ссылки видео
+            // - Открытия веб-страницы видео
+            std::string m_selected_ytid;
+
+            // Путь к файлу настроек
+            std::string m_config_path;
 
-    class Settings {
-    public:
-        static void setConfigPath(std::string config_path);
-        static void setApiKey(std::string api_key);
-        static void setQuality(int quality);
+            // С каким качеством запускать видео
+            // Значение в переменной подставится в параметр запуска MPV
+            // --ytdl-format
+            int m_quality;
 
-        static std::string getConfigPath();
-        static std::string getApiKey();
-        static int getQuality();
+            // Запускать ли MPV в полноэкранном режиме по умолчанию?
+            bool m_launch_fullscreen;
 
-        // Путь к файлу настроек
-        static std::string s_config_path;
-        
-        // Ключ API
-        static std::string s_api_key;
+            // Запускать ли MPV над всеми окнами?
+            bool m_launch_ontop;
 
-        // Качество воспроизводимых видео (240/360/480/720/1080p)
-        static int s_quality;
+            // Запускать ли MPV над всеми окнами, в углу экрана?
+            // Имеет приоритет над m_launch_fullscreen и m_launch_ontop
+            bool m_launch_miniplayer;
     };
 }

+ 25 - 9
src/YTMPV/ytmpv.cpp

@@ -14,28 +14,44 @@ int main(int argc, char** argv) {
     // -- Локаль --
     std::locale::global(std::locale(""));
 
-    // -- Получение данных из файла настроек --
+    // -- Конструирование путей файлов --
     std::string config_path = Glib::build_filename(
         Glib::get_user_config_dir(),
         "ytmpv",
         "config.json");
+
+    std::string secrets_path = Glib::build_filename(
+        Glib::get_user_config_dir(),
+        "ytmpv",
+        "secrets.json");
+
+    std::string db_path = Glib::build_filename(
+        Glib::get_user_cache_dir(),
+        "ytmpv",
+        "db.sqlite3");
+    
     std::ifstream config_file(config_path);
     auto j_config = json::parse(config_file);
     config_file.close();
 
-    // TODO
-    std::string db_path = "/home/vad/Code/YTMPV/db.sqlite3";
+    std::ifstream secrets_file(secrets_path);
+    auto j_secrets = json::parse(secrets_file);
+    secrets_file.close();
     
     // -- Общая инициализация --
     curl_global_init(CURL_GLOBAL_ALL);
-    db::databaseInit(db_path);
+    db::databaseInit(db_path)
     db::createTables();
-    ytapi::youtubeInit(j_config["apikey"]);
 
     // -- Запуск приложения --
-    core::Settings::setConfigPath(config_path);
-    core::Settings::setApiKey(j_config["apikey"]);
-    core::Settings::setQuality(j_config["quality"]);
-    auto app = components::App::create();
+    core::Core appcore = core::Core(
+        j_secrets["installed"]["client_secret"],
+        config_path,
+        j_config["quality"],
+        j_config["fullscreen"],
+        j_config["ontop"],
+        j_config["miniplayer"],
+    );
+    auto app = components::App::create(appcore);
     return app->run(argc, argv);
 }