Вадим Королёв před 1 rokem
rodič
revize
a58cf3d033

+ 22 - 23
lib/Database/database.cpp

@@ -25,14 +25,14 @@ namespace db {
         std::array<std::string, 3> queries = {
             "CREATE TABLE IF NOT EXISTS \"author\" (\
                 \"id\"	INTEGER,\
-                \"yt_id\"	TEXT,\
+                \"ytid\"	TEXT,\
                 \"name\"	TEXT,\
                 PRIMARY KEY(\"id\" AUTOINCREMENT)\
             )",
 
             "CREATE TABLE IF NOT EXISTS \"video\" (\
                 \"id\"	INTEGER,\
-                \"yt_id\"	TEXT,\
+                \"ytid\"	TEXT,\
                 \"title\"	TEXT NOT NULL,\
                 \"description\"	TEXT NOT NULL,\
                 \"author_id\"	INTEGER NOT NULL,\
@@ -55,7 +55,7 @@ namespace db {
             status = sqlite3_exec(DB, queries[i].c_str(), NULL, 0, &message_error);
 
             if (status != SQLITE_OK) {
-                std::cerr << "Error creating tables" << std::endl; 
+                std::cerr << "Error creating tables: " << status << std::endl; 
                 sqlite3_free(message_error);
                 databaseClose();
                 return;
@@ -64,13 +64,13 @@ namespace db {
         databaseClose();
     }
 
-    bool getVideoByYTID(std::string yt_id, video* output) {
+    bool getVideoByYTID(std::string ytid, video* output) {
         sqlite3_stmt* stmt;
         std::string query =
-        "SELECT v.id, v.yt_id, v.title, v.description, a.id, a.yt_id, a.name,"
+        "SELECT v.id, v.ytid, v.title, v.description, a.id, a.ytid, a.name,"
         " v.published_at, v.views_count, v.rate_status, v.big_thumbnail"
         " FROM video v LEFT JOIN author a ON v.author_id=a.id"
-        " WHERE v.yt_id=?";
+        " WHERE v.ytid=?";
         databaseOpen();
         author video_author;
 
@@ -78,7 +78,7 @@ namespace db {
         sqlite3_prepare_v2(DB, query.c_str(), query.length(), &stmt, nullptr);
 
         // Привязка данных
-        sqlite3_bind_text(stmt, 1, yt_id.c_str(), yt_id.length(), SQLITE_STATIC);
+        sqlite3_bind_text(stmt, 1, ytid.c_str(), ytid.length(), SQLITE_STATIC);
 
         // Выполнение
         int result = sqlite3_step(stmt);
@@ -92,7 +92,7 @@ namespace db {
         // unsigned const char* конвертируется в std::string
         // https://stackoverflow.com/a/804131
         video_author.id = sqlite3_column_int(stmt, 4);
-        video_author.yt_id = std::string(reinterpret_cast<const char*>(
+        video_author.ytid = std::string(reinterpret_cast<const char*>(
             sqlite3_column_text(stmt, 5)
         ));
         video_author.name = Glib::ustring(reinterpret_cast<const char*>(
@@ -100,7 +100,7 @@ namespace db {
         ));
 
         output->id = sqlite3_column_int(stmt, 0);
-        output->yt_id = std::string(reinterpret_cast<const char*>(
+        output->ytid = std::string(reinterpret_cast<const char*>(
             sqlite3_column_text(stmt, 1)
         ));
         output->title = Glib::ustring(reinterpret_cast<const char*>(
@@ -125,19 +125,19 @@ namespace db {
         return true;
     }
 
-    bool getAuthorByYTID(std::string yt_id, author* output) {
+    bool getAuthorByYTID(std::string ytid, author* output) {
         sqlite3_stmt* stmt;
         std::string query =
         "SELECT a.id, a.name"
         " FROM author a"
-        " WHERE a.yt_id=?";
+        " WHERE a.ytid=?";
         databaseOpen();
 
         // Подготовка
         sqlite3_prepare_v2(DB, query.c_str(), query.length(), &stmt, nullptr);
 
         // Привязка данных
-        sqlite3_bind_text(stmt, 1, yt_id.c_str(), yt_id.length(), SQLITE_STATIC);
+        sqlite3_bind_text(stmt, 1, ytid.c_str(), ytid.length(), SQLITE_STATIC);
 
         // Выполнение
         int result = sqlite3_step(stmt);
@@ -162,11 +162,11 @@ namespace db {
     void addAuthor(author* a) {
         sqlite3_stmt* stmt;
         std::string query =
-        "INSERT INTO author (yt_id, name) VALUES(?, ?)";
+        "INSERT INTO author (ytid, name) VALUES(?, ?)";
         databaseOpen();
 
         sqlite3_prepare_v2(DB, query.c_str(), query.length(), &stmt, nullptr);
-        sqlite3_bind_text(stmt, 1, a->yt_id.c_str(), a->yt_id.length(), SQLITE_STATIC);
+        sqlite3_bind_text(stmt, 1, a->ytid.c_str(), a->ytid.length(), SQLITE_STATIC);
         sqlite3_bind_text(stmt, 2, a->name.c_str(), a->name.bytes(), SQLITE_STATIC);
         sqlite3_step(stmt);
 
@@ -180,13 +180,13 @@ namespace db {
     {
         sqlite3_stmt* stmt;
         std::string query =
-        "INSERT INTO video (yt_id, title, description, author_id, published_at,"
+        "INSERT INTO video (ytid, title, description, author_id, published_at,"
         " views_count, rate_status, big_thumbnail)"
         " VALUES(?, ?, ?, ?, ?, ?, ?, ?)";
         databaseOpen();
 
         sqlite3_prepare_v2(DB, query.c_str(), query.length(), &stmt, nullptr);
-        sqlite3_bind_text(stmt, 1, v->yt_id.c_str(), v->yt_id.length(), SQLITE_STATIC);
+        sqlite3_bind_text(stmt, 1, v->ytid.c_str(), v->ytid.length(), SQLITE_STATIC);
         sqlite3_bind_text(stmt, 2, v->title.c_str(), v->title.bytes(), SQLITE_STATIC);
         sqlite3_bind_text(stmt, 3, v->description.c_str(), v->description.bytes(), SQLITE_STATIC);
         sqlite3_bind_int(stmt, 4, v->author_obj.id);
@@ -202,33 +202,32 @@ namespace db {
         databaseClose();
     }
 
-    void setVideoRating(std::string yt_id, int rating)
+    void setVideoRating(std::string ytid, int rating)
     {
         sqlite3_stmt* stmt;
         std::string query =
-        "UPDATE video SET rate_status=? WHERE yt_id=?";
+        "UPDATE video SET rate_status=? WHERE ytid=?";
         databaseOpen();
 
         sqlite3_prepare_v2(DB, query.c_str(), query.length(), &stmt, nullptr);
         sqlite3_bind_int(stmt, 1, rating);
-        sqlite3_bind_text(stmt, 2, yt_id.c_str(), yt_id.length(), SQLITE_STATIC);
+        sqlite3_bind_text(stmt, 2, ytid.c_str(), ytid.length(), SQLITE_STATIC);
 
-        std::cout << sqlite3_step(stmt);
         sqlite3_finalize(stmt);
         databaseClose();
     }
 
-    int getVideoRating(std::string yt_id)
+    int getVideoRating(std::string ytid)
     {
         sqlite3_stmt* stmt;
         std::string query =
         "SELECT v.rate_status"
         " FROM video v"
-        " WHERE v.yt_id=?";
+        " WHERE v.ytid=?";
 
         databaseOpen();
         sqlite3_prepare_v2(DB, query.c_str(), query.length(), &stmt, nullptr);
-        sqlite3_bind_text(stmt, 1, yt_id.c_str(), yt_id.length(), SQLITE_STATIC);
+        sqlite3_bind_text(stmt, 1, ytid.c_str(), ytid.length(), SQLITE_STATIC);
 
         // Выполнение
         int output;

+ 2 - 2
lib/Database/entities.hpp

@@ -6,14 +6,14 @@ namespace db {
     // Канал
     struct author {
         int id;
-        std::string yt_id;
+        std::string ytid;
         Glib::ustring name;
     };
 
     // Видео
     struct video {
         int id;
-        std::string yt_id;
+        std::string ytid;
         Glib::ustring title;
         Glib::ustring description;
         std::string published_at;

+ 36 - 7
lib/YoutubeApi/youtubeapi.cpp

@@ -1,4 +1,5 @@
 #include "youtubeapi.hpp"
+#include <iostream>
 
 namespace ytapi {
 
@@ -7,9 +8,10 @@ namespace ytapi {
         std::string key,
         std::stringstream* response)
     {
-        // Добавляем ключ авторизации к запросу
-        url += "&key=";
-        url += key;
+        // Добавляем ключ API
+        url += "&key=" + key;
+
+        // TODO: oAuth
         
         CURL* handle = curl_easy_init();
         if (!handle) return false;
@@ -67,7 +69,8 @@ namespace ytapi {
         return written;
     }
 
-    std::vector<std::string> getVideoIDsByQuery(std::string query, std::string key) {
+    std::vector<std::string> getVideoIDsByQuery(std::string query, std::string key)
+    {
         std::stringstream response;
         std::string url;
 
@@ -151,10 +154,36 @@ namespace ytapi {
         performCurlDownload(url, save_path);
     }
 
-    void setVideoRating(std::string ytid, int rating, std::string secret)
+    /*void setVideoRating(std::string ytid, int rating, std::string secret)
     {
-        std::string url = "https://
+        std::string url = "https://www.googleapis.com/youtube/v3/videos/rate";
+        url += "?id=" + ytid;
+        url += "&rating=";
+
+        std::string rating_value;
+        switch(rating)
+        {
+            case 0:
+                rating_value = "none";
+                break;
+            case 1:
+                rating_value = "like";
+                break;
+            case 2:
+                rating_value = "dislike";
+                break;
+            default:
+                rating_value = "none";
+                break;
+        }
+        url += rating;
+
+        std::stringstream response;
         performCurlRequest(
+            url,
+            secret,
+            &response
         );
-    }
+        // TODO: обработка ошибок
+    }*/
 }

+ 5 - 4
lib/YoutubeApi/youtubeapi.hpp

@@ -38,11 +38,12 @@ namespace ytapi {
     std::string urldecode(const std::string& encoded);
 
     // Получает список YT_ID видео по запросу
-    std::vector<std::string> getVideoIDsByQuery(std::string query);
+    // key - ключ api
+    std::vector<std::string> getVideoIDsByQuery(std::string query, std::string key);
 
     // Возвращает объект видео, получив данные по API
     // ytid - id видео на YouTube
-    // key - ключ авторизации
+    // key - ключ api
     Video getVideoByYTID(std::string ytid, std::string key);
 
     // Возвращает список видео id из JSON списка видео
@@ -51,7 +52,7 @@ namespace ytapi {
     // Подгатавливает, настраивает и выполняет CURL запрос. Возвращает
     // успешность вызова
     // url - адрес запроса
-    // key - oAuth ключ клиента
+    // key - ключ api
     // response - указатель на строковый поток, в который записывается ответ
     // запроса
     bool performCurlRequest(
@@ -78,5 +79,5 @@ namespace ytapi {
     // ytid - видео с каким ytid ставится оценка
     // rating - оценка видео (0 - нет, 1 - лайк, 2 - дизлайк)
     // secret - ключ пользователя
-    void setVideoRating(std::string ytid, int rating, std::string secret);
+    //void setVideoRating(std::string ytid, int rating, std::string secret);
 }

+ 34 - 34
src/YTMPV/MainWindow.cpp

@@ -11,20 +11,28 @@
 #include <gdkmm/pixbuf.h>
 #include <glibmm.h>
 
+#include <iostream>
+
 namespace components {
 
-    MainWindow* MainWindow::create()
+    MainWindow* MainWindow::create(core::Core* appcore)
     {
         auto builder = Gtk::Builder::create_from_resource("/src/YTMPV/res/MainWindow.ui");
-        auto win = Gtk::Builder::get_widget_derived<MainWindow>(builder, "mainWindow");
+        auto win = Gtk::Builder::get_widget_derived<MainWindow>(
+            builder,
+            "mainWindow",
+            appcore);
         assert(win != nullptr);
         return win;
     }
     
     MainWindow::MainWindow(
         BaseObjectType* cobject,
-        const Glib::RefPtr<Gtk::Builder>& refBuilder)
+        const Glib::RefPtr<Gtk::Builder>& refBuilder,
+        core::Core* appcore)
+        // init
         : Gtk::ApplicationWindow(cobject)
+        , m_appcore(appcore)
         , m_builder(refBuilder)
         , m_rate_container(Gtk::Orientation::HORIZONTAL)
     {
@@ -83,15 +91,16 @@ namespace components {
         m_rate_container.append(*m_rate_dislike_btn);
         m_rate_container.append(*m_rate_meh_btn);
         m_rate_container.append(*m_rate_like_btn);
+
         m_rate_dislike_btn->signal_clicked().connect(sigc::bind(
             sigc::mem_fun(*this, &MainWindow::onRateButtonClicked),
-            RateButtonParams {2}));
+            RateButtonParams { core::VideoRating::DISLIKE }));
         m_rate_meh_btn->signal_clicked().connect(sigc::bind(
             sigc::mem_fun(*this, &MainWindow::onRateButtonClicked),
-            RateButtonParams {0} ));
+            RateButtonParams { core::VideoRating::NONE } ));
         m_rate_like_btn->signal_clicked().connect(sigc::bind(
             sigc::mem_fun(*this, &MainWindow::onRateButtonClicked),
-            RateButtonParams {1} ));
+            RateButtonParams { core::VideoRating::LIKE } ));
         
         // WelcomeView
         // TODO
@@ -104,11 +113,11 @@ namespace components {
 
         // Получить новые результаты поиска
         db::video found;
-        std::vector<std::string> video_ids = ytapi::getVideoIDsByQuery(query.release());
+        std::vector<std::string>video_ids = m_appcore->searchVideos(query);
 
         // Добавить в список видео найденные видео
-        for (auto id : video_ids) {
-            found = core::getVideoByYTID(id);
+        for (auto ytid : video_ids) {
+            found = m_appcore->getVideoByYTID(ytid);
             m_video_storage->append(VideoModel::create(found));
         }
     }
@@ -159,7 +168,7 @@ namespace components {
         if (!lbl_title) return;
 
         // Загрузка изображения превью видео с диска
-        std::string thumbnail_path = "thumbnails/default/" + obj.yt_id + ".jpg";
+        std::string thumbnail_path = "thumbnails/default/" + obj.ytid + ".jpg";
         Glib::RefPtr<Gdk::Pixbuf> thumbnail_pixbuf;
         try {
             thumbnail_pixbuf = Gdk::Pixbuf::create_from_file(thumbnail_path, 120, 90);
@@ -167,7 +176,7 @@ namespace components {
             switch (e.code()) {
                 case Glib::FileError::Code::NO_SUCH_ENTITY:
                     // Превью на диске не найдено. Скачиваем...
-                    ytapi::downloadMediumThumnail(obj.yt_id, thumbnail_path);
+                    ytapi::downloadMediumThumnail(obj.ytid, thumbnail_path);
                     // ...и присваиваем
                     thumbnail_pixbuf = Gdk::Pixbuf::create_from_file(thumbnail_path, 120, 90);
                     break;
@@ -197,29 +206,20 @@ namespace components {
             obj.author_obj.name,
             obj.description,
             obj.published_at,
-            obj.yt_id,
+            obj.ytid,
             obj.views_count,
             obj.big_thumbnail
         );
     }
 
-    void MainWindow::playSingleVideo(std::string yt_id)
+    void MainWindow::playSingleVideo(std::string ytid)
     {
         // Запуск mpv в заднем фоне
-        std::string yt_url = "https://youtu.be/"+yt_id;
+        std::string yt_url = "https://youtu.be/"+ytid;
 
-        // TODO: Заставить работать
         Glib::spawn_async(
             "",
-            {
-                "/usr/bin/mpv",
-                "--no-terminal",
-                "--ytdl-format=\""+core::getYTDLFormat(core::Settings::getQuality())+"\"",
-                yt_url
-                // TODO: --fullscreen
-                // TODO: --ontop
-                // TODO: мини-проигрыватель
-            },
+            m_appcore->getMPVLaunchParams(ytid),
             Glib::SpawnFlags::DEFAULT
         );
     }
@@ -285,7 +285,7 @@ namespace components {
 
         // Кнопки оценки видео
         int my_rating = db::getVideoRating(yt_id);
-        updateVideoDetails(yt_id, my_rating);
+        updateVideoDetails(yt_id, static_cast<core::VideoRating>(my_rating));
 
         // Добавление кнопки "Открыть в веб-браузере"
         auto btn_webbrowser = Gtk::make_managed<Gtk::Button>("Open in web browser");
@@ -363,28 +363,28 @@ namespace components {
 
     void MainWindow::onRateButtonClicked(RateButtonParams e)
     {
-        updateVideoDetails(m_current_yt_id, e.rating);
-        core::rateVideo(m_current_yt_id, e.rating);
+        std::string current_ytid = m_appcore->getSelectedYTID();
+        updateVideoDetails(current_ytid, e.rating);
+        m_appcore->rateVideo(current_ytid, e.rating);
     }
 
-    void MainWindow::updateVideoDetails(std::string yt_id, int rating)
+    void MainWindow::updateVideoDetails(std::string ytid, core::VideoRating rating)
     {
-        core::AppState::current_yt_id = yt_id;
-        m_current_yt_id = yt_id;
-        m_current_rating = rating;
+        m_appcore->setSelectedYTID(ytid);
+        m_appcore->setCurrentRating(rating);
         
         m_rate_dislike_btn->set_state_flags(Gtk::StateFlags::NORMAL, true);
         m_rate_like_btn->set_state_flags(Gtk::StateFlags::NORMAL, true);
         m_rate_meh_btn->set_state_flags(Gtk::StateFlags::NORMAL, true);
         switch (rating)
         {
-            case 0:
+            case core::VideoRating::NONE:
                 m_rate_meh_btn->set_state_flags(Gtk::StateFlags::CHECKED);
                 break;
-            case 1:
+            case core::VideoRating::LIKE:
                 m_rate_like_btn->set_state_flags(Gtk::StateFlags::CHECKED);
                 break;
-            case 2:
+            case core::VideoRating::DISLIKE:
                 m_rate_dislike_btn->set_state_flags(Gtk::StateFlags::CHECKED);
                 break;
             default:

+ 8 - 6
src/YTMPV/MainWindow.hpp

@@ -1,5 +1,7 @@
 #pragma once
+
 #include "VideoModel.hpp"
+#include "core.hpp"
 #include <gtkmm/window.h>
 #include <gtkmm/applicationwindow.h>
 #include <gtkmm/box.h>
@@ -29,7 +31,7 @@ namespace
     struct RateButtonParams
     {
         // Какая оценка
-        int rating;
+        core:: VideoRating rating;
     };
 }
 
@@ -39,12 +41,14 @@ namespace components {
     class MainWindow : public Gtk::ApplicationWindow
     {
     public:
+        static MainWindow* create(core::Core* appcore);
         MainWindow(
             BaseObjectType* cobject,
-            const Glib::RefPtr<Gtk::Builder>& refBuilder);
-        static MainWindow* create();
+            const Glib::RefPtr<Gtk::Builder>& refBuilder,
+            core::Core* appcore);
 
     protected:
+        core::Core* m_appcore;
         Glib::RefPtr<Gtk::Builder> m_builder;
         Gtk::Box m_layout;
         Gtk::Box m_head;
@@ -88,8 +92,6 @@ namespace components {
         // --Общее--
         // Оценка выбранного видео
         int m_current_rating;
-        // Youtube id у выбранного видео
-        std::string m_current_yt_id;
         // Устанавливает боковую панель просмотра видео в обычный режим
         void searchSidebarRegularMode();
 
@@ -116,7 +118,7 @@ namespace components {
         // Открывает страницу видео в YouTube
         void openVideoInWebBrowser(std::string yt_id);
         // Обновляет текущие переменные при просмотре информации о видео
-        void updateVideoDetails(std::string yt_id, int rating);
+        void updateVideoDetails(std::string yt_id, core::VideoRating rating);
         // Выполняет основную часть поиска видео
         void searchVideoByQuery(Glib::ustring query);
     };

+ 13 - 26
src/YTMPV/PrefWindow.cpp

@@ -14,8 +14,11 @@ using json = nlohmann::json;
 namespace components {
     PrefWindow::PrefWindow(
         BaseObjectType* cobject,
-        const Glib::RefPtr<Gtk::Builder>& refBuilder)
+        const Glib::RefPtr<Gtk::Builder>& refBuilder,
+        core::Core* appcore)
+        // init
         : Gtk::Window(cobject)
+        , m_appcore(appcore)
         , m_builder(refBuilder)
     {
         // Заполнение выпадающего списка качества видео
@@ -31,12 +34,10 @@ namespace components {
         m_quality_dropdown = m_builder->get_widget<Gtk::DropDown>("quality_entry");
         m_cancel_btn = m_builder->get_widget<Gtk::Button>("cancel_button");
         m_save_btn = m_builder->get_widget<Gtk::Button>("saveclose_button");
-        m_apikey_entry = m_builder->get_widget<Gtk::Entry>("apikey_entry");
 
         assert(m_quality_dropdown != nullptr);
         assert(m_cancel_btn != nullptr);
         assert(m_save_btn != nullptr);
-        assert(m_apikey_entry != nullptr);
 
         auto string_list = Gtk::StringList::create(quality_strings);
         m_quality_dropdown->set_model(string_list);
@@ -46,10 +47,8 @@ namespace components {
         m_cancel_btn->signal_clicked().connect(sigc::mem_fun(
             *this, &PrefWindow::onCancelClicked));
 
-        m_apikey_entry->set_text(core::Settings::getApiKey());
-
         int selected_quality_index;
-        switch(core::Settings::getQuality())
+        switch(m_appcore->getQuality())
         {
             case 240:
                 selected_quality_index = 0;
@@ -74,11 +73,14 @@ namespace components {
     }
 
     PrefWindow* PrefWindow::create(
-        Gtk::Window& parent
-    )
+        Gtk::Window& parent,
+        core::Core* appcore)
     {
         auto builder = Gtk::Builder::create_from_resource("/src/YTMPV/res/PrefsWindow.ui");
-        auto win = Gtk::Builder::get_widget_derived<PrefWindow>(builder, "prefs_dialog");
+        auto win = Gtk::Builder::get_widget_derived<PrefWindow>(
+            builder,
+            "prefs_dialog",
+            appcore);
         assert(win != nullptr);
 
         win->set_transient_for(parent);
@@ -98,10 +100,6 @@ namespace components {
 
     void PrefWindow::saveConfig()
     {
-        // -- Получение данных --
-        // Ключ API
-        Glib::ustring api_key = m_apikey_entry->get_text();
-
         // Качество
         int quality;
         int sel_index = m_quality_dropdown->get_selected();
@@ -126,19 +124,8 @@ namespace components {
                 quality = 240;
                 break;
         }
+        m_appcore->setQuality(quality);
         
-        // Сериализация
-        json j_object = { {"apikey", api_key}, {"quality", quality} };
-        std::string dump = j_object.dump(1, '\t');
-
-        // Запись в файл
-        std::ofstream config(core::Settings::getConfigPath());
-        config << dump;
-        config.close();
-
-        // Пере-инициализация библиотек
-        ytapi::youtubeInit(api_key);
-        core::Settings::setApiKey(api_key);
-        core::Settings::setQuality(quality);
+        m_appcore->saveConfig();
     }
 }

+ 6 - 2
src/YTMPV/PrefWindow.hpp

@@ -1,4 +1,6 @@
 #pragma once
+
+#include "core.hpp"
 #include <gtkmm/window.h>
 #include <glibmm.h>
 #include <gtkmm/builder.h>
@@ -10,12 +12,14 @@ namespace components {
     class PrefWindow : public Gtk::Window
     {
     public:
+        static PrefWindow* create(Gtk::Window& parent, core::Core* appcore);
         PrefWindow(
             BaseObjectType* cobject,
-            const Glib::RefPtr<Gtk::Builder>& refBuilder);
-        static PrefWindow* create(Gtk::Window& parent, core::Core appcore);
+            const Glib::RefPtr<Gtk::Builder>& refBuilder,
+            core::Core* appcore);
 
     protected:
+        core::Core* m_appcore;
         Glib::RefPtr<Gtk::Builder> m_builder;
         Gtk::DropDown* m_quality_dropdown;
         Gtk::Entry* m_apikey_entry;

+ 33 - 35
src/YTMPV/app.cpp

@@ -1,5 +1,4 @@
 #include "app.hpp"
-#include "core.hpp"
 #include <iostream>
 #include <giomm/menu.h>
 #include <gtkmm/builder.h>
@@ -8,17 +7,17 @@
 
 namespace components {
 
-    App::App(core::Core appcore)
-        : Gtk::Application("com.github.aquadim.YTMPV")
-        , m_core(appcore)
-    {}
-
-    Glib::RefPtr<App> App::create(core::Core appcore)
+    Glib::RefPtr<App> App::create(core::Core* appcore)
     {
         auto app = Glib::make_refptr_for_instance<App>(new App(appcore));
         return app;
     }
 
+    App::App(core::Core* appcore)
+        : Gtk::Application("com.github.aquadim.YTMPV")
+        , m_appcore(appcore)
+    {}
+
     void App::on_startup()
     {
         Gtk::Application::on_startup();
@@ -39,7 +38,6 @@ namespace components {
         auto menu_builder = Gtk::Builder::create_from_resource("/src/YTMPV/res/MainMenu.ui");
         auto menu = menu_builder->get_object<Gio::Menu>("menu");
         assert(menu != nullptr);
-        
         set_menubar(menu);
     }
 
@@ -49,28 +47,6 @@ namespace components {
         m_main_window->present();
     }
 
-    MainWindow* App::createMainWindow()
-    {
-        // Создание окна и привязка обработчика к его сигналу скрытия
-        auto appwindow = MainWindow::create(this);
-        add_window(*appwindow);
-        appwindow->signal_hide().connect(
-            [appwindow](){ delete appwindow; });
-        return appwindow;
-    }
-
-    PrefWindow* App::createPrefWindow()
-    {
-        auto prefwindow = PrefWindow::create(
-            *get_active_window(),
-            &this->m_core
-        );
-        add_window(*prefwindow);
-        prefwindow->signal_hide().connect(
-            [prefwindow](){ delete prefwindow; });
-        return prefwindow;
-    }
-
     void App::onActionQuit()
     {
         auto windows = get_windows();
@@ -89,13 +65,35 @@ namespace components {
 
     void App::onActionCopyYTID()
     {
-        m_main_window->get_clipboard()->set_text(core::AppState::current_yt_id);
+        m_main_window->get_clipboard()->set_text(
+            m_appcore->getSelectedYTID());
     }
-    
+
     void App::onActionCopyYTLink()
     {
-        std::cout << core::AppState::current_yt_id << std::endl;
-        std::string url = "https://youtu.be/" + core::AppState::current_yt_id;
-        m_main_window->get_clipboard()->set_text(url);
+        m_main_window->get_clipboard()->set_text(
+            m_appcore->getSelectedYTLink());
+    }
+    
+    MainWindow* App::createMainWindow()
+    {
+        // Создание окна и привязка обработчика к его сигналу скрытия
+        auto appwindow = MainWindow::create(m_appcore);
+        add_window(*appwindow);
+        appwindow->signal_hide().connect(
+            [appwindow](){ delete appwindow; });
+        return appwindow;
+    }
+
+    PrefWindow* App::createPrefWindow()
+    {
+        auto prefwindow = PrefWindow::create(
+            *get_active_window(),
+            m_appcore
+        );
+        add_window(*prefwindow);
+        prefwindow->signal_hide().connect(
+            [prefwindow](){ delete prefwindow; });
+        return prefwindow;
     }
 }

+ 9 - 6
src/YTMPV/app.hpp

@@ -4,6 +4,7 @@
 #include <glibmm/ustring.h>
 #include "MainWindow.hpp"
 #include "PrefWindow.hpp"
+#include "core.hpp"
 
 namespace components {
 
@@ -13,12 +14,10 @@ namespace components {
 
     public:
         // Создаёт ссылку на приложение
-        // secret_key - oAuth2.0 ключ пользователя
-        static Glib::RefPtr<App> create(core::Core appcore);
+        static Glib::RefPtr<App> create(core::Core* appcore);
 
     protected:
-        // secret_key - oAuth2.0 ключ пользователя
-        App(core::Core appcore);
+        App(core::Core* appcore);
 
         void on_startup() override;
 
@@ -36,12 +35,16 @@ namespace components {
         // Копирует ссылку на видео
         void onActionCopyYTLink();
 
-        MainWindow* m_main_window;
-
         // Создаёт главное окно приложения
         MainWindow* createMainWindow();
 
         // Создаёт окно настроек приложения
         PrefWindow* createPrefWindow();
+
+        // Ссылка на главное окно
+        MainWindow* m_main_window;
+
+        // Ссылка на ядро
+        core::Core* m_appcore;
     };
 }

+ 71 - 13
src/YTMPV/core.cpp

@@ -8,29 +8,28 @@ using json = nlohmann::json;
 namespace core {
 
     Core::Core(
-        std::string client_secret,
+        std::string apikey,
         std::string config_path,
         int quality,
         bool launch_fullscreen,
         bool launch_ontop,
-        bool launch_miniplayer,)
+        bool launch_miniplayer)
         // Инициализация
-        : m_client_secret(client_secret)
+        : m_apikey(apikey)
         , 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),
+        , m_launch_fullscreen(launch_fullscreen)
+        , m_launch_ontop(launch_ontop)
+        , m_launch_miniplayer(launch_miniplayer)
     {}
 
-    Core::setQuality(int quality)
+    void Core::setQuality(int quality)
     {
         this->m_quality = quality;
     }
 
-    Core::saveConfig()
+    void Core::saveConfig()
     {
         // Сериализация
         json j_object = {
@@ -57,7 +56,7 @@ namespace core {
         }
 
         // Видео в базе данных нет. Получаем информацию о нём через ytapi
-        ytapi::Video found_video = ytapi::getVideoByYTID(ytid);
+        ytapi::Video found_video = ytapi::getVideoByYTID(ytid, m_apikey);
         output.ytid         = ytid;
         output.title        = found_video.title;
         output.description  = found_video.description;
@@ -84,13 +83,72 @@ namespace core {
 
     std::string Core::getYTDLFormat()
     {
-        return "bestvideo[height<=?"+std::to_string(m_quality)+"]+bestaudio/best";
+        return
+            "--ytdl-format=bestvideo[height<=?"
+            +std::to_string(m_quality)
+            +"]+bestaudio/best";
     }
 
     // Устанавливает оценку в БД и посылает запрос на оценку
-    void rateVideo(std::string ytid, VideoRating rating)
+    void Core::rateVideo(std::string ytid, VideoRating rating)
     {
+        // TODO: ничего не делать если текущая оценка такая же как и rating
         db::setVideoRating(ytid, static_cast<int>(rating));
-        ytapi::setVideoRating(ytid, static_cast<int>(rating), m_client_secret);
+        /*ytapi::setVideoRating(ytid, static_cast<int>(rating), m_client_secret);*/
+    }
+
+    std::vector<std::string> Core::getMPVLaunchParams(std::string ytid)
+    {
+        // TODO: добавить настройку пути к MPV
+        std::vector<std::string> output;
+
+        // Путь до MPV
+        output.push_back("/usr/bin/mpv");
+
+        // Ссылка на видео
+        output.push_back(getYTLink(ytid));
+
+        // Без терминала
+        output.push_back("--no-terminal");
+
+        // С качеством:
+        output.push_back(getYTDLFormat());
+
+        return output;
+    }
+
+    std::string Core::getSelectedYTID()
+    {
+        return m_selected_ytid;
+    }
+
+    std::string Core::getSelectedYTLink()
+    {
+        return getYTLink(m_selected_ytid);
+    }
+
+    std::string Core::getYTLink(std::string ytid)
+    {
+        return "https://youtube.com/watch?v=" + ytid;
+    }
+
+    void Core::setSelectedYTID(std::string ytid)
+    {
+        m_selected_ytid = ytid;
+    }
+
+    void Core::setCurrentRating(VideoRating rating)
+    {
+        m_current_rating = rating;
+    }
+
+    int Core::getQuality()
+    {
+        return m_quality;
+    }
+
+    std::vector<std::string> Core::searchVideos(std::string query)
+    {
+        return ytapi::getVideoIDsByQuery(query, m_apikey);
     }
 }

+ 28 - 5
src/YTMPV/core.hpp

@@ -25,12 +25,12 @@ namespace core
     {
         public:
             Core(
-                std::string client_secret,
+                std::string apikey,
                 std::string config_path,
                 int quality,
                 bool launch_fullscreen,
                 bool launch_ontop,
-                bool launch_miniplayer,
+                bool launch_miniplayer
             );
 
             // Устанавливает качество видео
@@ -52,11 +52,32 @@ namespace core
             void rateVideo(std::string ytid, VideoRating rating);
 
             // Возвращает параметры запуска MPV
-            std::vector getMPVLaunchParams();
+            std::vector<std::string> getMPVLaunchParams(std::string ytid);
+
+            // Возвращает ytid выбранного видео
+            std::string getSelectedYTID();
+
+            // Возвращает ссылку на выбранное видео
+            std::string getSelectedYTLink();
+
+            // Возвращает ссылку на видео по его ytid
+            std::string getYTLink(std::string ytid);
+
+            // Устанавливает выбранное видео
+            void setSelectedYTID(std::string ytid);
+
+            // Устанавливает оценку выбранного видео
+            void setCurrentRating(VideoRating rating);
+
+            // Возвращает качество видео
+            int getQuality();
+
+            // Возвращает список из ytid по запросу
+            std::vector<std::string> searchVideos(std::string query);
 
         protected:
-            // oAuth-ключ пользователя
-            std::string m_client_secret;
+            // ключ API
+            std::string m_apikey;
 
             // ytid "выбранного" видео
             // Необходимо для:
@@ -65,6 +86,8 @@ namespace core
             // - Открытия веб-страницы видео
             std::string m_selected_ytid;
 
+            VideoRating m_current_rating;
+
             // Путь к файлу настроек
             std::string m_config_path;
 

+ 2 - 22
src/YTMPV/res/PrefsWindow.ui

@@ -16,26 +16,6 @@
         <property name="margin-start">16</property>
         <property name="margin-top">16</property>
         <property name="row-spacing">16</property>
-        <child>
-          <object class="GtkLabel" id="apikey_label">
-            <property name="label" translatable="yes">_API key:</property>
-            <property name="mnemonic-widget">apikey_entry</property>
-            <property name="use-underline">True</property>
-            <property name="xalign">1</property>
-            <layout>
-              <property name="column">0</property>
-              <property name="row">0</property>
-            </layout>
-          </object>
-        </child>
-        <child>
-          <object class="GtkEntry" id="apikey_entry">
-            <layout>
-              <property name="column">1</property>
-              <property name="row">0</property>
-            </layout>
-          </object>
-        </child>
         <child>
           <object class="GtkLabel" id="quality_label">
             <property name="label" translatable="yes">_Preferred quality:</property>
@@ -44,7 +24,7 @@
             <property name="xalign">1</property>
             <layout>
               <property name="column">0</property>
-              <property name="row">1</property>
+              <property name="row">0</property>
             </layout>
           </object>
         </child>
@@ -52,7 +32,7 @@
           <object class="GtkDropDown" id="quality_entry">
             <layout>
               <property name="column">1</property>
-              <property name="row">1</property>
+              <property name="row">0</property>
             </layout>
           </object>
         </child>

+ 16 - 8
src/YTMPV/ytmpv.cpp

@@ -8,6 +8,14 @@
 #include <fstream>
 #include <nlohmann/json.hpp>
 
+// TODO:
+/*
+ * 1. Окно настройки
+ * 2. oAuth
+ * 3. Оценка видео
+ * 4. Проверка подключения (curl возвращает пустые строки json)
+*/
+
 using json = nlohmann::json;
 
 int main(int argc, char** argv) {
@@ -20,10 +28,10 @@ int main(int argc, char** argv) {
         "ytmpv",
         "config.json");
 
-    std::string secrets_path = Glib::build_filename(
+    /*std::string secrets_path = Glib::build_filename(
         Glib::get_user_config_dir(),
         "ytmpv",
-        "secrets.json");
+        "secrets.json");*/
 
     std::string db_path = Glib::build_filename(
         Glib::get_user_cache_dir(),
@@ -34,24 +42,24 @@ int main(int argc, char** argv) {
     auto j_config = json::parse(config_file);
     config_file.close();
 
-    std::ifstream secrets_file(secrets_path);
+    /*std::ifstream secrets_file(secrets_path);
     auto j_secrets = json::parse(secrets_file);
-    secrets_file.close();
+    secrets_file.close();*/
     
     // -- Общая инициализация --
     curl_global_init(CURL_GLOBAL_ALL);
-    db::databaseInit(db_path)
+    db::databaseInit(db_path);
     db::createTables();
 
     // -- Запуск приложения --
     core::Core appcore = core::Core(
-        j_secrets["installed"]["client_secret"],
+        j_config["apikey"],
         config_path,
         j_config["quality"],
         j_config["fullscreen"],
         j_config["ontop"],
-        j_config["miniplayer"],
+        j_config["miniplayer"]
     );
-    auto app = components::App::create(appcore);
+    auto app = components::App::create(&appcore);
     return app->run(argc, argv);
 }