소스 검색

Добавлены кнопки оценки видео

Вадим Королёв 1 년 전
부모
커밋
eef1e50cd0
13개의 변경된 파일211개의 추가작업 그리고 133개의 파일을 삭제
  1. 3 0
      .gitignore
  2. 1 0
      build.sh
  3. 0 86
      example-byid.json
  4. 64 9
      lib/Database/database.cpp
  5. 6 1
      lib/Database/database.hpp
  6. 2 0
      lib/Database/entities.hpp
  7. 8 9
      lib/YoutubeApi/youtubeapi.cpp
  8. 4 2
      lib/YoutubeApi/youtubeapi.hpp
  9. 1 0
      run.sh
  10. 75 21
      src/YTMPV/MainWindow.cpp
  11. 33 3
      src/YTMPV/MainWindow.hpp
  12. 9 2
      src/YTMPV/core.cpp
  13. 5 0
      src/YTMPV/core.hpp

+ 3 - 0
.gitignore

@@ -1,3 +1,6 @@
 YTMPV.geany
 YTMPV.geany
 example.json
 example.json
 db.sqlite3
 db.sqlite3
+db.sqlite3-shm
+db.sqlite3-wal
+example-byid.json

+ 1 - 0
build.sh

@@ -0,0 +1 @@
+stdbuf -i0 -o0 -e0 meson compile -C bin

+ 0 - 86
example-byid.json

@@ -1,86 +0,0 @@
-{
-  "kind": "youtube#videoListResponse",
-  "etag": "10hqyf6pWj1OW3ahOJ8P3HNePMc",
-  "items": [
-    {
-      "kind": "youtube#video",
-      "etag": "nIzxni7ijKW-6GYWAx398GYwOuY",
-      "id": "Ks-_Mh1QhMc",
-      "snippet": {
-        "publishedAt": "2012-10-01T15:27:35Z",
-        "channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
-        "title": "Your body language may shape who you are | Amy Cuddy | TED",
-        "description": "Body language affects how others see us, but it may also change how we see ourselves. Social psychologist Amy Cuddy argues that \"power posing\" -- standing in a posture of confidence, even when we don't feel confident -- can boost feelings of confidence, and might have an impact on our chances for success. (Note: Some of the findings presented in this talk have been referenced in an ongoing debate among social scientists about robustness and reproducibility. Read Amy Cuddy's response here: http://ideas.ted.com/inside-the-debate-about-power-posing-a-q-a-with-amy-cuddy/)\n\nGet TED Talks recommended just for you! Learn more at https://www.ted.com/signup.\n\nThe TED Talks channel features the best talks and performances from the TED Conference, where the world's leading thinkers and doers give the talk of their lives in 18 minutes (or less). Look for talks on Technology, Entertainment and Design -- plus science, business, global issues, the arts and more.\n\nFollow TED on Twitter: http://www.twitter.com/TEDTalks\nLike TED on Facebook: https://www.facebook.com/TED\n\nSubscribe to our channel: https://www.youtube.com/TED",
-        "thumbnails": {
-          "default": {
-            "url": "https://i.ytimg.com/vi/Ks-_Mh1QhMc/default.jpg",
-            "width": 120,
-            "height": 90
-          },
-          "medium": {
-            "url": "https://i.ytimg.com/vi/Ks-_Mh1QhMc/mqdefault.jpg",
-            "width": 320,
-            "height": 180
-          },
-          "high": {
-            "url": "https://i.ytimg.com/vi/Ks-_Mh1QhMc/hqdefault.jpg",
-            "width": 480,
-            "height": 360
-          },
-          "standard": {
-            "url": "https://i.ytimg.com/vi/Ks-_Mh1QhMc/sddefault.jpg",
-            "width": 640,
-            "height": 480
-          },
-          "maxres": {
-            "url": "https://i.ytimg.com/vi/Ks-_Mh1QhMc/maxresdefault.jpg",
-            "width": 1280,
-            "height": 720
-          }
-        },
-        "channelTitle": "TED",
-        "tags": [
-          "Amy Cuddy",
-          "TED",
-          "TEDTalk",
-          "TEDTalks",
-          "TED Talk",
-          "TED Talks",
-          "TEDGlobal",
-          "brain",
-          "business",
-          "psychology",
-          "self",
-          "success"
-        ],
-        "categoryId": "22",
-        "liveBroadcastContent": "none",
-        "defaultLanguage": "en",
-        "localized": {
-          "title": "Your body language may shape who you are | Amy Cuddy | TED",
-          "description": "Body language affects how others see us, but it may also change how we see ourselves. Social psychologist Amy Cuddy argues that \"power posing\" -- standing in a posture of confidence, even when we don't feel confident -- can boost feelings of confidence, and might have an impact on our chances for success. (Note: Some of the findings presented in this talk have been referenced in an ongoing debate among social scientists about robustness and reproducibility. Read Amy Cuddy's response here: http://ideas.ted.com/inside-the-debate-about-power-posing-a-q-a-with-amy-cuddy/)\n\nGet TED Talks recommended just for you! Learn more at https://www.ted.com/signup.\n\nThe TED Talks channel features the best talks and performances from the TED Conference, where the world's leading thinkers and doers give the talk of their lives in 18 minutes (or less). Look for talks on Technology, Entertainment and Design -- plus science, business, global issues, the arts and more.\n\nFollow TED on Twitter: http://www.twitter.com/TEDTalks\nLike TED on Facebook: https://www.facebook.com/TED\n\nSubscribe to our channel: https://www.youtube.com/TED"
-        },
-        "defaultAudioLanguage": "en"
-      },
-      "contentDetails": {
-        "duration": "PT21M3S",
-        "dimension": "2d",
-        "definition": "hd",
-        "caption": "true",
-        "licensedContent": true,
-        "contentRating": {},
-        "projection": "rectangular"
-      },
-      "statistics": {
-        "viewCount": "24753756",
-        "likeCount": "430018",
-        "favoriteCount": "0",
-        "commentCount": "9776"
-      }
-    }
-  ],
-  "pageInfo": {
-    "totalResults": 1,
-    "resultsPerPage": 1
-  }
-}

+ 64 - 9
lib/Database/database.cpp

@@ -1,4 +1,5 @@
 #include "database.hpp"
 #include "database.hpp"
+#include <iostream>
 
 
 namespace db {
 namespace db {
     // Объект работы с БД
     // Объект работы с БД
@@ -21,7 +22,7 @@ namespace db {
     void createTables() {
     void createTables() {
         char* message_error;
         char* message_error;
         int status;
         int status;
-        std::array<std::string, 2> queries = {
+        std::array<std::string, 3> queries = {
             "CREATE TABLE IF NOT EXISTS \"author\" (\
             "CREATE TABLE IF NOT EXISTS \"author\" (\
                 \"id\"	INTEGER,\
                 \"id\"	INTEGER,\
                 \"yt_id\"	TEXT,\
                 \"yt_id\"	TEXT,\
@@ -41,17 +42,21 @@ namespace db {
                 \"dur_s\"	INTEGER NOT NULL DEFAULT 0,\
                 \"dur_s\"	INTEGER NOT NULL DEFAULT 0,\
                 \"views_count\"	INTEGER NOT NULL DEFAULT 0,\
                 \"views_count\"	INTEGER NOT NULL DEFAULT 0,\
                 \"likes_count\"	INTEGER NOT NULL DEFAULT 0,\
                 \"likes_count\"	INTEGER NOT NULL DEFAULT 0,\
+                \"rate_status\"	INTEGER NOT NULL DEFAULT 0,\
                 PRIMARY KEY(\"id\" AUTOINCREMENT)\
                 PRIMARY KEY(\"id\" AUTOINCREMENT)\
-            )"
+            )",
+
+            "PRAGMA journal_mode=WAL"
         };
         };
 
 
         databaseOpen();
         databaseOpen();
-        for (int i = 0; i < 2; i++) {
+        for (int i = 0; i < 3; i++) {
             status = sqlite3_exec(DB, queries[i].c_str(), NULL, 0, &message_error);
             status = sqlite3_exec(DB, queries[i].c_str(), NULL, 0, &message_error);
 
 
             if (status != SQLITE_OK) {
             if (status != SQLITE_OK) {
                 std::cerr << "Error creating tables" << std::endl; 
                 std::cerr << "Error creating tables" << std::endl; 
                 sqlite3_free(message_error);
                 sqlite3_free(message_error);
+                databaseClose();
                 return;
                 return;
             }
             }
         }
         }
@@ -61,7 +66,8 @@ namespace db {
     bool getVideoByYTID(std::string yt_id, video* output) {
     bool getVideoByYTID(std::string yt_id, video* output) {
         sqlite3_stmt* stmt;
         sqlite3_stmt* stmt;
         std::string query =
         std::string query =
-        "SELECT v.id,v.yt_id,v.title,v.description,a.id,a.yt_id,a.name,v.published_at"
+        "SELECT v.id, v.yt_id, v.title, v.description, a.id, a.yt_id, a.name,"
+        " v.published_at, v.views_count, v.rate_status"
         " FROM video v LEFT JOIN author a ON v.author_id=a.id"
         " FROM video v LEFT JOIN author a ON v.author_id=a.id"
         " WHERE v.yt_id=?";
         " WHERE v.yt_id=?";
         databaseOpen();
         databaseOpen();
@@ -77,6 +83,7 @@ namespace db {
         int result = sqlite3_step(stmt);
         int result = sqlite3_step(stmt);
         if (result == SQLITE_DONE) {
         if (result == SQLITE_DONE) {
             // Видео не найдено
             // Видео не найдено
+            databaseClose();
             return false;
             return false;
         }
         }
 
 
@@ -104,6 +111,8 @@ namespace db {
         output->published_at = std::string(reinterpret_cast<const char*>(
         output->published_at = std::string(reinterpret_cast<const char*>(
             sqlite3_column_text(stmt, 7)
             sqlite3_column_text(stmt, 7)
         ));
         ));
+        output->views_count = sqlite3_column_int(stmt, 8);
+        output->rate_status = sqlite3_column_int(stmt, 9);
         output->author_obj = video_author;
         output->author_obj = video_author;
 
 
         // Завершение
         // Завершение
@@ -129,7 +138,8 @@ namespace db {
         // Выполнение
         // Выполнение
         int result = sqlite3_step(stmt);
         int result = sqlite3_step(stmt);
         if (result == SQLITE_DONE) {
         if (result == SQLITE_DONE) {
-            // Видео не найдено
+            // Автор не найден
+            databaseClose();
             return false;
             return false;
         }
         }
 
 
@@ -162,11 +172,13 @@ namespace db {
         databaseClose();
         databaseClose();
     }
     }
 
 
-    void addVideo(video* v) {
+    void addVideo(video* v)
+    {
         sqlite3_stmt* stmt;
         sqlite3_stmt* stmt;
         std::string query =
         std::string query =
-        "INSERT INTO video (yt_id, title, description, author_id, published_at)"
-        " VALUES(?, ?, ?, ?, ?)";
+        "INSERT INTO video (yt_id, title, description, author_id, published_at,"
+        " views_count, rate_status)"
+        " VALUES(?, ?, ?, ?, ?, ?, ?)";
         databaseOpen();
         databaseOpen();
 
 
         sqlite3_prepare_v2(DB, query.c_str(), query.length(), &stmt, nullptr);
         sqlite3_prepare_v2(DB, query.c_str(), query.length(), &stmt, nullptr);
@@ -175,6 +187,8 @@ namespace db {
         sqlite3_bind_text(stmt, 3, v->description.c_str(), v->description.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);
         sqlite3_bind_int(stmt, 4, v->author_obj.id);
         sqlite3_bind_text(stmt, 5, v->published_at.c_str(), v->published_at.length(), SQLITE_STATIC);
         sqlite3_bind_text(stmt, 5, v->published_at.c_str(), v->published_at.length(), SQLITE_STATIC);
+        sqlite3_bind_int(stmt, 6, v->views_count);
+        sqlite3_bind_int(stmt, 7, static_cast<int>(v->rate_status));
         sqlite3_step(stmt);
         sqlite3_step(stmt);
 
 
         v->id = sqlite3_last_insert_rowid(DB);
         v->id = sqlite3_last_insert_rowid(DB);
@@ -182,4 +196,45 @@ namespace db {
         sqlite3_finalize(stmt);
         sqlite3_finalize(stmt);
         databaseClose();
         databaseClose();
     }
     }
-}
+
+    void setVideoRating(std::string yt_id, int rating)
+    {
+        sqlite3_stmt* stmt;
+        std::string query =
+        "UPDATE video SET rate_status=? WHERE yt_id=?";
+        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);
+
+        std::cout << sqlite3_step(stmt);
+        sqlite3_finalize(stmt);
+        databaseClose();
+    }
+
+    int getVideoRating(std::string yt_id)
+    {
+        sqlite3_stmt* stmt;
+        std::string query =
+        "SELECT v.rate_status"
+        " FROM video v"
+        " WHERE v.yt_id=?";
+
+        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);
+
+        // Выполнение
+        int output;
+        int result = sqlite3_step(stmt);
+        if (result == SQLITE_DONE) {
+            // Видео не найдено
+            output = 3;
+        }
+        output = sqlite3_column_int(stmt, 0);
+
+        databaseClose();
+        return output;
+    }
+}

+ 6 - 1
lib/Database/database.hpp

@@ -2,7 +2,6 @@
 #include "entities.hpp"
 #include "entities.hpp"
 #include <sqlite3.h>
 #include <sqlite3.h>
 #include <string>
 #include <string>
-#include <iostream>
 #include <array>
 #include <array>
 #include <glibmm/ustring.h>
 #include <glibmm/ustring.h>
 
 
@@ -35,4 +34,10 @@ namespace db {
     
     
     // Добавляет видео в базу данных
     // Добавляет видео в базу данных
     void addAuthor(author* a);
     void addAuthor(author* a);
+
+    // Устанавливает оценку видео
+    void setVideoRating(std::string yt_id, int rating);
+
+    // Возвращает рейтинг видео
+    int getVideoRating(std::string yt_id);
 }
 }

+ 2 - 0
lib/Database/entities.hpp

@@ -17,6 +17,8 @@ namespace db {
         Glib::ustring title;
         Glib::ustring title;
         Glib::ustring description;
         Glib::ustring description;
         std::string published_at;
         std::string published_at;
+        int views_count;
+        int rate_status;
         author author_obj;
         author author_obj;
     };
     };
 }
 }

+ 8 - 9
lib/YoutubeApi/youtubeapi.cpp

@@ -14,7 +14,7 @@ namespace ytapi {
         CURL* handle = curl_easy_init();
         CURL* handle = curl_easy_init();
         if (!handle) return false;
         if (!handle) return false;
         curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
         curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
-        curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &write_http_data);
+        curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &writeHttpToString);
         curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
         curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
         curl_easy_perform(handle);
         curl_easy_perform(handle);
         curl_easy_cleanup(handle);
         curl_easy_cleanup(handle);
@@ -54,7 +54,7 @@ namespace ytapi {
         return result;
         return result;
     }
     }
 
 
-    size_t write_http_data(char* ptr, size_t size, size_t nmemb, void* userdata)
+    size_t writeHttpToString(char* ptr, size_t size, size_t nmemb, void* userdata)
     {
     {
         std::string data((const char*) ptr, (size_t) size * nmemb);
         std::string data((const char*) ptr, (size_t) size * nmemb);
         *((std::stringstream*) userdata) << data;
         *((std::stringstream*) userdata) << data;
@@ -95,23 +95,20 @@ namespace ytapi {
 
 
     video_result getVideoByYTID(std::string yt_id) {
     video_result getVideoByYTID(std::string yt_id) {
         std::stringstream response;
         std::stringstream response;
+        std::stringstream response_rating;
         std::string url;
         std::string url;
         video_result output;
         video_result output;
 
 
-        // Формирование URL
-        url += "https://youtube.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id=";
+        // -- Общие сведения --
+        url = "https://youtube.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id=";
         url += yt_id;
         url += yt_id;
-
-        // Выполнение запроса
         performCurlRequest(url, &response);
         performCurlRequest(url, &response);
 
 
-        // Парсинг
-        // TODO:stats
         auto json = nlohmann::json::parse(response);
         auto json = nlohmann::json::parse(response);
         auto item = json["items"][0];
         auto item = json["items"][0];
         auto snippet = item["snippet"];
         auto snippet = item["snippet"];
         auto content_details = item["contentDetails"];
         auto content_details = item["contentDetails"];
-        // auto stats = item["statistics"];
+        auto stats = item["statistics"];
 
 
         output.yt_id            = yt_id;
         output.yt_id            = yt_id;
         output.title            = Glib::ustring(snippet["title"]);
         output.title            = Glib::ustring(snippet["title"]);
@@ -120,6 +117,8 @@ namespace ytapi {
         output.author_yt_id     = std::string(snippet["channelId"]);
         output.author_yt_id     = std::string(snippet["channelId"]);
         output.author_name      = Glib::ustring(snippet["channelTitle"]);
         output.author_name      = Glib::ustring(snippet["channelTitle"]);
         output.published_at     = std::string(snippet["publishedAt"]);
         output.published_at     = std::string(snippet["publishedAt"]);
+        output.views_count      = std::stoi(stats["viewCount"].template get<std::string>());
+        output.rating           = 0;
 
 
         return output;
         return output;
     }
     }

+ 4 - 2
lib/YoutubeApi/youtubeapi.hpp

@@ -10,7 +10,7 @@
 #include <Database/database.hpp>
 #include <Database/database.hpp>
 
 
 namespace ytapi {
 namespace ytapi {
-
+    
     // Детали видео - необработанные
     // Детали видео - необработанные
     struct video_result {
     struct video_result {
         std::string     yt_id;          // ID на YouTube
         std::string     yt_id;          // ID на YouTube
@@ -20,6 +20,8 @@ namespace ytapi {
         std::string     author_yt_id;   // ID автора на YouTube
         std::string     author_yt_id;   // ID автора на YouTube
         Glib::ustring   author_name;    // Имя автора на YouTube
         Glib::ustring   author_name;    // Имя автора на YouTube
         std::string     published_at;   // Когда было опубликовано
         std::string     published_at;   // Когда было опубликовано
+        int             views_count;    // Количество просмотров
+        int             rating;         // Моя оценка видео
     };
     };
     
     
     // API ключ
     // API ключ
@@ -29,7 +31,7 @@ namespace ytapi {
     void youtubeInit(std::string key);
     void youtubeInit(std::string key);
     
     
     // Функция записи данных в std::ostringstream
     // Функция записи данных в std::ostringstream
-    size_t write_http_data(char* ptr, size_t size, size_t nmemb, void *userdata);
+    size_t writeHttpToString(char* ptr, size_t size, size_t nmemb, void *userdata);
 
 
     // Функция записи данных в файл на диске
     // Функция записи данных в файл на диске
     size_t writeHttpToFile(void* ptr, size_t size, size_t nmemb, FILE* userdata);
     size_t writeHttpToFile(void* ptr, size_t size, size_t nmemb, FILE* userdata);

+ 1 - 0
run.sh

@@ -0,0 +1 @@
+stdbuf -i0 -o0 -e0 ./bin/src/YTMPV/ytmpv

+ 75 - 21
src/YTMPV/MainWindow.cpp

@@ -12,18 +12,7 @@
 #include <gtkmm/label.h>
 #include <gtkmm/label.h>
 #include <gdkmm/pixbuf.h>
 #include <gdkmm/pixbuf.h>
 #include <glibmm/fileutils.h>
 #include <glibmm/fileutils.h>
-
-namespace
-{
-    struct PlayButtonParams
-    {
-        // Какой id видео
-        std::string yt_id;
-
-        // Кнопка
-        Gtk::Button* caller;
-    };
-}
+#include <glibmm/markup.h>
 
 
 namespace components {
 namespace components {
 
 
@@ -46,6 +35,11 @@ namespace components {
         , m_search_button()
         , m_search_button()
         , m_search_scroller()
         , m_search_scroller()
         , m_search_video_list()
         , m_search_video_list()
+
+        , m_rate_container(Gtk::Orientation::HORIZONTAL)
+        , m_rate_dislike_btn()
+        , m_rate_meh_btn()
+        , m_rate_like_btn()
     {
     {
         set_title("YTMPV");
         set_title("YTMPV");
         set_default_size(1024, 640);
         set_default_size(1024, 640);
@@ -93,6 +87,24 @@ namespace components {
         m_search_body.set_position(256);
         m_search_body.set_position(256);
         m_search_layout.append(m_search_body);
         m_search_layout.append(m_search_body);
 
 
+        // Кнопки оценки видео
+        m_rate_dislike_btn.set_child(*(core::getButtonContents("Dislike", "face-angry-symbolic")));
+        m_rate_meh_btn.set_child(*(core::getButtonContents("No rating", "face-plain-symbolic")));
+        m_rate_like_btn.set_child(*(core::getButtonContents("Like", "face-smile-big-symbolic")));
+        m_rate_container.set_homogeneous();
+        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}));
+        m_rate_meh_btn.signal_clicked().connect(sigc::bind(
+            sigc::mem_fun(*this, &MainWindow::onRateButtonClicked),
+            RateButtonParams {0} ));
+        m_rate_like_btn.signal_clicked().connect(sigc::bind(
+            sigc::mem_fun(*this, &MainWindow::onRateButtonClicked),
+            RateButtonParams {1} ));
+        
         // WelcomeView
         // WelcomeView
         // TODO
         // TODO
 
 
@@ -187,7 +199,8 @@ namespace components {
             obj.author_obj.name,
             obj.author_obj.name,
             obj.description,
             obj.description,
             obj.published_at,
             obj.published_at,
-            obj.yt_id
+            obj.yt_id,
+            obj.views_count
         );
         );
     }
     }
 
 
@@ -235,8 +248,8 @@ namespace components {
         Glib::ustring channel_title,
         Glib::ustring channel_title,
         Glib::ustring description,
         Glib::ustring description,
         std::string published_at,
         std::string published_at,
-        std::string yt_id
-    )
+        std::string yt_id,
+        int views_count)
     {
     {
         // Главный объект
         // Главный объект
         auto layout = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
         auto layout = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
@@ -252,23 +265,33 @@ namespace components {
         auto thumbnail = Gtk::make_managed<Gtk::Picture>();
         auto thumbnail = Gtk::make_managed<Gtk::Picture>();
         auto lbl_title = Gtk::make_managed<Gtk::Label>();
         auto lbl_title = Gtk::make_managed<Gtk::Label>();
         auto lbl_channel_title = Gtk::make_managed<Gtk::Label>(channel_title);
         auto lbl_channel_title = Gtk::make_managed<Gtk::Label>(channel_title);
+        auto lbl_views_count = Gtk::make_managed<Gtk::Label>();
         auto lbl_description = Gtk::make_managed<Gtk::Label>(description);
         auto lbl_description = Gtk::make_managed<Gtk::Label>(description);
         auto lbl_published_at = Gtk::make_managed<Gtk::Label>(published_at);
         auto lbl_published_at = Gtk::make_managed<Gtk::Label>(published_at);
 
 
         // Установка названия видео (выделение полужирным)
         // Установка названия видео (выделение полужирным)
-        lbl_title->set_markup("<b>"+title+"</b>");
+        Glib::ustring title_escaped = Glib::Markup::escape_text(title);
+        lbl_title->set_markup("<b>"+title_escaped+"</b>");
+
+        // Количество просмотров
+        lbl_views_count->set_text(std::to_string(views_count) + " " + "views");
 
 
         // Добавление кнопки "Воспроизвести"
         // Добавление кнопки "Воспроизвести"
         auto btn_play = Gtk::make_managed<Gtk::Button>();
         auto btn_play = Gtk::make_managed<Gtk::Button>();
         btn_play->set_child(*(core::getButtonContents("Play video", "player_play")));
         btn_play->set_child(*(core::getButtonContents("Play video", "player_play")));
         btn_play->set_hexpand();
         btn_play->set_hexpand();
-        auto btn_event = PlayButtonParams{ yt_id, btn_play };
         btn_play->signal_clicked().connect(sigc::bind(
         btn_play->signal_clicked().connect(sigc::bind(
             sigc::mem_fun(*this, &MainWindow::onPlayButtonClicked),
             sigc::mem_fun(*this, &MainWindow::onPlayButtonClicked),
-            btn_play
+            PlayButtonParams {yt_id, btn_play}
         ));
         ));
         box_buttons->append(*btn_play);
         box_buttons->append(*btn_play);
 
 
+        // Кнопки оценки видео
+        int my_rating = db::getVideoRating(yt_id);
+        setCurrentVideo(yt_id, my_rating);
+        m_rate_container.unparent();            
+        box_buttons->append(m_rate_container);
+
         // Добавление кнопки "Открыть в веб-браузере"
         // Добавление кнопки "Открыть в веб-браузере"
         auto btn_webbrowser = Gtk::make_managed<Gtk::Button>();
         auto btn_webbrowser = Gtk::make_managed<Gtk::Button>();
         btn_webbrowser->set_child(*(core::getButtonContents("Open in web browser", "web-browser")));
         btn_webbrowser->set_child(*(core::getButtonContents("Open in web browser", "web-browser")));
@@ -280,7 +303,6 @@ namespace components {
 
 
         // Скачивание большого превью
         // Скачивание большого превью
         std::string thumbnail_path = "thumbnails/large/" + yt_id + ".jpg";
         std::string thumbnail_path = "thumbnails/large/" + yt_id + ".jpg";
-        std::cout << thumbnail_path << std::endl;
         Glib::RefPtr<Gdk::Pixbuf> thumbnail_pixbuf;
         Glib::RefPtr<Gdk::Pixbuf> thumbnail_pixbuf;
         try {
         try {
             thumbnail_pixbuf = Gdk::Pixbuf::create_from_file(thumbnail_path);
             thumbnail_pixbuf = Gdk::Pixbuf::create_from_file(thumbnail_path);
@@ -302,7 +324,7 @@ namespace components {
         thumbnail->set_paintable(thumbnail_texture);
         thumbnail->set_paintable(thumbnail_texture);
         thumbnail->set_content_fit(Gtk::ContentFit::COVER);
         thumbnail->set_content_fit(Gtk::ContentFit::COVER);
         thumbnail->set_hexpand();
         thumbnail->set_hexpand();
-        thumbnail->set_size_request(-1, 360);
+        thumbnail->set_size_request(-1, 240);
 
 
         lbl_description->set_halign(Gtk::Align::START);
         lbl_description->set_halign(Gtk::Align::START);
         lbl_description->set_selectable();
         lbl_description->set_selectable();
@@ -310,6 +332,7 @@ namespace components {
 
 
         box_textdetails->append(*lbl_title);
         box_textdetails->append(*lbl_title);
         box_textdetails->append(*lbl_channel_title);
         box_textdetails->append(*lbl_channel_title);
+        box_textdetails->append(*lbl_views_count);
         box_textdetails->append(*lbl_description);
         box_textdetails->append(*lbl_description);
         box_textdetails->append(*lbl_published_at);
         box_textdetails->append(*lbl_published_at);
         box_textdetails->append(*btn_webbrowser);
         box_textdetails->append(*btn_webbrowser);
@@ -331,13 +354,44 @@ namespace components {
         // TODO:
         // TODO:
         // - Windows
         // - Windows
         // - MacOs
         // - MacOs
-        std::string command = "xdg-open https://youtu.be/"+yt_id;
+        std::string command = "xdg-open https://youtube.com/watch?v="+yt_id;
         system(command.c_str());
         system(command.c_str());
     }
     }
 
 
     void MainWindow::onPlayButtonClicked(PlayButtonParams e)
     void MainWindow::onPlayButtonClicked(PlayButtonParams e)
     {
     {
+        e.caller->set_sensitive(false);
+        e.caller->set_child(*(core::getButtonContents("mpv started", "window-duplicate")));
         MainWindow::playSingleVideo(e.yt_id);
         MainWindow::playSingleVideo(e.yt_id);
     }
     }
 
 
+    void MainWindow::onRateButtonClicked(RateButtonParams e)
+    {
+        setCurrentVideo(m_current_yt_id, e.rating);
+        core::rateVideo(m_current_yt_id, e.rating);
+    }
+
+    void MainWindow::setCurrentVideo(std::string yt_id, int rating)
+    {
+        m_current_yt_id = yt_id;
+        
+        m_current_rating = 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:
+                m_rate_meh_btn.set_state_flags(Gtk::StateFlags::CHECKED);
+                break;
+            case 1:
+                m_rate_like_btn.set_state_flags(Gtk::StateFlags::CHECKED);
+                break;
+            case 2:
+                m_rate_dislike_btn.set_state_flags(Gtk::StateFlags::CHECKED);
+                break;
+            default:
+                break;
+        }
+    }
 }
 }

+ 33 - 3
src/YTMPV/MainWindow.hpp

@@ -15,6 +15,23 @@
 #include <gtkmm/paned.h>
 #include <gtkmm/paned.h>
 #include <gtkmm/menubutton.h>
 #include <gtkmm/menubutton.h>
 
 
+namespace
+{
+    struct PlayButtonParams
+    {
+        // Какой id видео
+        std::string yt_id;
+        // Кнопка
+        Gtk::Button* caller;
+    };
+
+    struct RateButtonParams
+    {
+        // Какая оценка
+        int rating;
+    };
+}
+
 namespace components {
 namespace components {
 
 
     // Главное окно приложения
     // Главное окно приложения
@@ -50,6 +67,14 @@ namespace components {
         // Модель выбора
         // Модель выбора
         Glib::RefPtr<Gtk::SingleSelection> selection_model;
         Glib::RefPtr<Gtk::SingleSelection> selection_model;
 
 
+        /// ДЕТАЛИ ВИДЕО ///
+        Gtk::Box m_rate_container;
+        Gtk::Button m_rate_dislike_btn;
+        Gtk::Button m_rate_meh_btn;
+        Gtk::Button m_rate_like_btn;
+        int m_current_rating;
+        std::string m_current_yt_id;
+
         // --События--
         // --События--
         // Клик кнопки поиска
         // Клик кнопки поиска
         void onSearchButtonClicked();
         void onSearchButtonClicked();
@@ -60,7 +85,9 @@ namespace components {
         // При выборе видео из списка
         // При выборе видео из списка
         void onVideoChanged();
         void onVideoChanged();
         // При клике на кнопку "Воспроизвести"
         // При клике на кнопку "Воспроизвести"
-        void onPlayButtonClicked();
+        void onPlayButtonClicked(PlayButtonParams e);
+        // При клике на кнопку оценки видео
+        void onRateButtonClicked(RateButtonParams e);
 
 
         // --Общее--
         // --Общее--
         // Устанавливает боковую панель просмотра видео в обычный режим
         // Устанавливает боковую панель просмотра видео в обычный режим
@@ -72,10 +99,13 @@ namespace components {
             Glib::ustring channel_title,
             Glib::ustring channel_title,
             Glib::ustring description,
             Glib::ustring description,
             std::string published_at,
             std::string published_at,
-            std::string yt_id);
+            std::string yt_id,
+            int views_count);
         // Проигрывает конкретное видео
         // Проигрывает конкретное видео
         void playSingleVideo(std::string yt_id);
         void playSingleVideo(std::string yt_id);
         // Открывает страницу видео в YouTube
         // Открывает страницу видео в YouTube
         void openVideoInWebBrowser(std::string yt_id);
         void openVideoInWebBrowser(std::string yt_id);
+        // Устанавливает текущие переменные при просмотре информации о видео
+        void setCurrentVideo(std::string yt_id, int rating);
     };
     };
-}
+}

+ 9 - 2
src/YTMPV/core.cpp

@@ -12,8 +12,6 @@ namespace core {
 
 
         // 1. Проверить есть ли видео в базе данных
         // 1. Проверить есть ли видео в базе данных
         if (db::getVideoByYTID(yt_id, &output)) {
         if (db::getVideoByYTID(yt_id, &output)) {
-            // Видео в базе данных найдено
-            std::cout << "Видео найдено в базе данных!" << std::endl;
             return output;
             return output;
         }
         }
 
 
@@ -23,6 +21,8 @@ namespace core {
         output.title        = found_video.title;
         output.title        = found_video.title;
         output.description  = found_video.description;
         output.description  = found_video.description;
         output.published_at = found_video.published_at;
         output.published_at = found_video.published_at;
+        output.views_count  = found_video.views_count;
+        output.rate_status  = found_video.rating;
 
 
         // Ищем автора в базе данных
         // Ищем автора в базе данных
         db::author video_author;
         db::author video_author;
@@ -97,4 +97,11 @@ namespace core {
     {
     {
         return s_quality;
         return s_quality;
     }
     }
+
+    // Устанавливает оценку в бд и посылает запрос на оценку
+    void rateVideo(std::string yt_id, int rating)
+    {
+        db::setVideoRating(yt_id, rating);
+        //ytapi::setVideoRating(yt_id, rating);
+    }
 }
 }

+ 5 - 0
src/YTMPV/core.hpp

@@ -8,6 +8,7 @@
 #include <string>
 #include <string>
 
 
 namespace core {
 namespace core {
+  
     // Возвращает объект видео по его Youtube id
     // Возвращает объект видео по его Youtube id
     db::video getVideoByYTID(std::string yt_id);
     db::video getVideoByYTID(std::string yt_id);
 
 
@@ -17,6 +18,10 @@ namespace core {
     // Возвращает строку формата yt-dl
     // Возвращает строку формата yt-dl
     std::string getYTDLFormat(int height);
     std::string getYTDLFormat(int height);
 
 
+    // Оценивает видео
+    // rating: 0 - нет, 1 - лайк, 2 - дизлайк
+    void rateVideo(std::string yt_id, int rating);
+
     class Settings {
     class Settings {
     public:
     public:
         static void setConfigPath(std::string config_path);
         static void setConfigPath(std::string config_path);