Skip to content

Commit

Permalink
Goodbye MOC, hello Verdigris
Browse files Browse the repository at this point in the history
Thing that matters with this, this allows for templated InnertubeReply.
No more qOverload garbage. Breaks pretty much all old code, but it's for
the better, things are a lot less gross :D
  • Loading branch information
BowDown097 committed Nov 13, 2023
1 parent 3a3c263 commit a0c7330
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 70 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "lib/verdigris"]
path = lib/verdigris
url = https://github.com/woboq/verdigris
7 changes: 2 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ cmake_minimum_required(VERSION 3.16)
project(innertube-qt VERSION 1.0 LANGUAGES C CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

# Set up AUTOMOC, AUTORCC, and AUTOUIC
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
include(GNUInstallDirs)

# Compiler flags
Expand Down Expand Up @@ -116,6 +111,8 @@ add_library(innertube-qt
src/protobuf/simpleprotobuf.cpp src/protobuf/simpleprotobuf.h
)

target_include_directories(innertube-qt PUBLIC lib/verdigris/src)

# Define options in preprocessor
if(INNERTUBE_GET_STS)
target_compile_definitions(innertube-qt PUBLIC INNERTUBE_GET_STS)
Expand Down
11 changes: 6 additions & 5 deletions DOXYGEN_MAINPAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ Here, a context is created around a client of the WEB type, version 2.20230718.0
To make a request, use @ref InnerTube::get. This example code provides a good way to test if things are working:
```cpp
InnertubeReply* reply = InnerTube::instance().get<InnertubeEndpoints::Next>("dQw4w9WgXcQ");
connect(reply, qOverload<const InnertubeEndpoints::Next&>(&InnertubeReply::finished), this, [](const auto& next) {
qDebug() << next.response.primaryInfo.title.text;
auto reply = InnerTube::instance().get<InnertubeEndpoints::Next>("dQw4w9WgXcQ");
connect(reply, &InnertubeReply<InnertubeEndpoints::Next>::finished, this, [](const InnertubeEndpoints::Next& endpoint) {
qDebug() << endpoint.response.primaryInfo.title.text;
});
```

Here, a request is made to the @ref InnertubeEndpoints::Next "Next endpoint" supplied with the video ID for [the classic Rick Roll video](https://www.youtube.com/watch?v=dQw4w9WgXcQ).
Once the request finishes, the response is captured and the video title is printed (which should be "Rick Astley - Never Gonna Give You Up (Official Music Video)").

Expand All @@ -24,12 +25,12 @@ To make a **raw** request, use @ref InnerTube::getRaw. Like before, here's examp
```cpp
InnerTube::instance().createContext(InnertubeClient(InnertubeClient::ClientType::ANDROID_TESTSUITE, "1.9", "MOBILE"));

InnertubeReply* reply = InnerTube::instance().getRaw<InnertubeEndpoints::Next>({
auto reply = InnerTube::instance().getRaw<InnertubeEndpoints::Next>({
{ "playbackContext", InnertubePlaybackContext(false, "").toJson() },
{ "videoId", "dQw4w9WgXcQ" }
});

connect(reply, &InnertubeReply::finishedRaw, this, [](const QJsonValue& v) {
connect(reply, &InnertubeReply<InnertubeEndpoints::Next>::finishedRaw, this, [](const QJsonValue& v) {
qDebug() << InnertubeObjects::InnertubeString(v["contents"]["singleColumnWatchNextResults"]["results"]["results"]["contents"][0]["itemSectionRenderer"]["contents"][0]["videoMetadataRenderer"]["title"]).text;
});
```
Expand Down
1 change: 1 addition & 0 deletions lib/verdigris
Submodule verdigris added at 3f71ac
24 changes: 4 additions & 20 deletions src/innertube.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,6 @@
#include <QThreadPool>
#include <type_traits>

template<class T, class... U>
static constexpr bool innertube_is_any_v = std::disjunction_v<std::is_same<T, U>...>;

/**
* @brief An @ref InnertubeEndpoints::BaseEndpoint "Innertube endpoint" that returns data.
* @details The @ref InnertubeEndpoints::Like "Like", @ref InnertubeEndpoints::SendMessage "SendMessage",
* and @ref InnertubeEndpoints::Subscribe "Subscribe" endpoints cannot be used here.<br>
* Use their respective methods in the InnerTube class as opposed to the
* @ref InnerTube::get "get" and @ref InnerTube::getBlocking "getBlocking" methods.
*/
template<class C>
concept EndpointWithData = requires(C c)
{
[]<InnertubeEndpoints::CompTimeStr X>(InnertubeEndpoints::BaseEndpoint<X>&){}(c);
} && !innertube_is_any_v<C, InnertubeEndpoints::Like, InnertubeEndpoints::SendMessage, InnertubeEndpoints::Subscribe>;

/**
* @brief The main attraction. Pretty much all of the interfacing with the library happens here.
* @details ALL of the endpoint objects (and by extension, the @ref get, @ref getBlocking, @ref like, @ref sendMessage,
Expand Down Expand Up @@ -73,9 +57,9 @@ class InnerTube
* Don't worry, it's freed when the request finishes - you don't have to manually delete it.
*/
template<EndpointWithData E>
InnertubeReply* get(auto&&... args)
InnertubeReply<E>* get(auto&&... args)
{
InnertubeReply* reply = new InnertubeReply;
InnertubeReply<E>* reply = new InnertubeReply<E>;
QThreadPool::globalInstance()->start([this, reply, ...args = std::forward<decltype(args)>(args)] {
try
{
Expand Down Expand Up @@ -108,9 +92,9 @@ class InnerTube
* Don't worry, it's freed when the request finishes - you don't have to manually delete it.
*/
template<EndpointWithData E>
InnertubeReply* getRaw(const QJsonObject& body)
InnertubeReply<E>* getRaw(const QJsonObject& body)
{
InnertubeReply* reply = new InnertubeReply;
InnertubeReply<E>* reply = new InnertubeReply<E>;
QThreadPool::globalInstance()->start([this, reply, body] {
QJsonValue v = getRawBlocking<E>(body);
emit reply->finishedRaw(v);
Expand Down
23 changes: 23 additions & 0 deletions src/innertube/endpoints/innertubeendpoints.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,28 @@
#include "video/next.h"
#include "video/player.h"
#include "video/updatedmetadata.h"
#include <wobjectdefs.h>

W_REGISTER_ARGTYPE(InnertubeEndpoints::AccountMenu)
W_REGISTER_ARGTYPE(InnertubeEndpoints::BrowseChannel)
W_REGISTER_ARGTYPE(InnertubeEndpoints::BrowseHistory)
W_REGISTER_ARGTYPE(InnertubeEndpoints::BrowseHome)
W_REGISTER_ARGTYPE(InnertubeEndpoints::BrowseSubscriptions)
W_REGISTER_ARGTYPE(InnertubeEndpoints::BrowseTrending)
W_REGISTER_ARGTYPE(InnertubeEndpoints::Dislike)
W_REGISTER_ARGTYPE(InnertubeEndpoints::GetLiveChat)
W_REGISTER_ARGTYPE(InnertubeEndpoints::GetLiveChatReplay)
W_REGISTER_ARGTYPE(InnertubeEndpoints::GetNotificationMenu)
W_REGISTER_ARGTYPE(InnertubeEndpoints::Like)
W_REGISTER_ARGTYPE(InnertubeEndpoints::ModifyChannelPreference)
W_REGISTER_ARGTYPE(InnertubeEndpoints::Next)
W_REGISTER_ARGTYPE(InnertubeEndpoints::Player)
W_REGISTER_ARGTYPE(InnertubeEndpoints::RemoveLike)
W_REGISTER_ARGTYPE(InnertubeEndpoints::Search)
W_REGISTER_ARGTYPE(InnertubeEndpoints::SendMessage)
W_REGISTER_ARGTYPE(InnertubeEndpoints::Subscribe)
W_REGISTER_ARGTYPE(InnertubeEndpoints::UnseenCount)
W_REGISTER_ARGTYPE(InnertubeEndpoints::Unsubscribe)
W_REGISTER_ARGTYPE(InnertubeEndpoints::UpdatedMetadata)

#endif // INNERTUBEENDPOINTS_H
14 changes: 9 additions & 5 deletions src/innertube/innertubeexception.h
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
#ifndef INNERTUBEEXCEPTION_H
#define INNERTUBEEXCEPTION_H
#include <QException>
#include <wobjectdefs.h>

class InnertubeException : public QException
{
public:
enum class Severity { Normal, Minor };
explicit InnertubeException(const QString& message, Severity severity = Severity::Normal) : _message(message), _severity(severity) {}
QString message() const { return _message; }
Severity severity() const { return _severity; }
explicit InnertubeException(const QString& message, Severity severity = Severity::Normal)
: m_message(message), m_severity(severity) {}
QString message() const { return m_message; }
Severity severity() const { return m_severity; }
private:
QString _message;
Severity _severity;
QString m_message;
Severity m_severity;
};

W_REGISTER_ARGTYPE(InnertubeException)

#endif // INNERTUBEEXCEPTION_H
46 changes: 25 additions & 21 deletions src/innertube/innertubereply.h
Original file line number Diff line number Diff line change
@@ -1,34 +1,38 @@
#ifndef INNERTUBEREPLY_H
#define INNERTUBEREPLY_H
#include <wobjectimpl.h>
#include "endpoints/innertubeendpoints.h"
#include "innertubeexception.h"

template<class T, class... U>
static constexpr bool innertube_is_any_v = std::disjunction_v<std::is_same<T, U>...>;

/**
* @brief An @ref InnertubeEndpoints::BaseEndpoint "Innertube endpoint" that returns data.
* @details The @ref InnertubeEndpoints::Like "Like", @ref InnertubeEndpoints::SendMessage "SendMessage",
* and @ref InnertubeEndpoints::Subscribe "Subscribe" endpoints cannot be used here.<br>
* Use their respective methods in the InnerTube class as opposed to the
* @ref InnerTube::get "get" and @ref InnerTube::getBlocking "getBlocking" methods.
*/
template<class C>
concept EndpointWithData = requires(C c)
{
[]<InnertubeEndpoints::CompTimeStr X>(InnertubeEndpoints::BaseEndpoint<X>&){}(c);
} && !innertube_is_any_v<C, InnertubeEndpoints::Like, InnertubeEndpoints::SendMessage, InnertubeEndpoints::Subscribe>;

/**
* @brief An object that emits signals related to Innertube requests. Used by @ref InnerTube::get and @ref InnerTube::getRaw.
*/
template<EndpointWithData E>
class InnertubeReply : public QObject
{
Q_OBJECT
signals:
void exception(const InnertubeException&);
void finished(const InnertubeEndpoints::AccountMenu&);
void finished(const InnertubeEndpoints::BrowseChannel&);
void finished(const InnertubeEndpoints::BrowseHistory&);
void finished(const InnertubeEndpoints::BrowseHome&);
void finished(const InnertubeEndpoints::BrowseSubscriptions&);
void finished(const InnertubeEndpoints::BrowseTrending&);
void finished(const InnertubeEndpoints::GetLiveChat&);
void finished(const InnertubeEndpoints::GetLiveChatReplay&);
void finished(const InnertubeEndpoints::GetNotificationMenu&);
void finished(const InnertubeEndpoints::ModifyChannelPreference&);
void finished(const InnertubeEndpoints::Next&);
void finished(const InnertubeEndpoints::Player&);
void finished(const InnertubeEndpoints::Search&);
void finished(const InnertubeEndpoints::SendMessage&);
void finished(const InnertubeEndpoints::Subscribe&);
void finished(const InnertubeEndpoints::UnseenCount&);
void finished(const InnertubeEndpoints::UpdatedMetadata&);
void finishedRaw(const QJsonValue&);
W_OBJECT(InnertubeReply)
public /* signals */:
void exception(const InnertubeException& ex) W_SIGNAL(exception, ex)
void finished(const E& endpoint) W_SIGNAL(finished, endpoint)
void finishedRaw(const QJsonValue& value) W_SIGNAL(finishedRaw, value)
};

W_OBJECT_IMPL_INLINE(InnertubeReply<E>, template<EndpointWithData E>)

#endif // INNERTUBEREPLY_H
14 changes: 9 additions & 5 deletions src/innertube/itc-objects/innertubeauthstore.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@
#include "innertubecontext.h"
#include <QObject>
#include <QSettings>
#include <wobjectimpl.h>

#ifndef INNERTUBE_NO_WEBENGINE
#include <QNetworkCookie>
W_REGISTER_ARGTYPE(QNetworkCookie)
#endif

/**
* @brief Stores YouTube authentication credentials.
*/
class InnertubeAuthStore : public QObject
{
Q_OBJECT
W_OBJECT(InnertubeAuthStore)
public:
QString apisid;
QString hsid;
Expand Down Expand Up @@ -65,12 +67,14 @@ class InnertubeAuthStore : public QObject
* @return The authentication credentials as a JSON object to be used with @ref authenticateFromJson.
*/
QJsonObject toJson() const;
signals:
void authenticateSuccess();

void authenticateSuccess() W_SIGNAL(authenticateSuccess)
#ifndef INNERTUBE_NO_WEBENGINE
private slots:
void cookieAdded(const QNetworkCookie& cookie);
private:
void cookieAdded(const QNetworkCookie& cookie); W_SLOT(cookieAdded)
#endif
};

W_OBJECT_IMPL_INLINE(InnertubeAuthStore)

#endif // INNERTUBEAUTHSTORE_H
25 changes: 16 additions & 9 deletions src/sslhttprequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <QJsonObject>
#include <QSslSocket>
#include <QUrl>
#include <wobjectimpl.h>

struct SslHttpRequestError
{
Expand All @@ -22,7 +23,7 @@ struct SslHttpRequestError

class SslHttpRequest : public QObject
{
Q_OBJECT
W_OBJECT(SslHttpRequest)
public:
enum class RequestMethod { Get, Post };
explicit SslHttpRequest(const QString& url, RequestMethod method = RequestMethod::Get, QObject* parent = nullptr);
Expand All @@ -36,8 +37,9 @@ class SslHttpRequest : public QObject
void setBody(const QByteArray& requestBody, const QString& contentType) { m_contentType = contentType; m_requestBody = requestBody; }
void setBody(const QJsonObject& json);
void setHeaders(const QVariantMap& headers) { m_headers = headers; }
signals:
void finished(const QByteArray& response, const SslHttpRequestError& error = SslHttpRequestError());

void finished(const QByteArray& response, const SslHttpRequestError& error = SslHttpRequestError())
W_SIGNAL(finished, response, error)
private:
QString m_contentType;
bool m_emitPayload;
Expand All @@ -47,12 +49,17 @@ class SslHttpRequest : public QObject
QByteArray m_response;
QSslSocket* m_sslSocket;
QUrl m_url;
private slots:
void disconnected();
void errorOccurred(QAbstractSocket::SocketError error);
void makeRequest();
void readyRead();
void sslErrors(const QList<QSslError>& errors);

void disconnected(); W_SLOT(disconnected)
void errorOccurred(QAbstractSocket::SocketError error); W_SLOT(errorOccurred)
void makeRequest(); W_SLOT(makeRequest)
void readyRead(); W_SLOT(readyRead)
void sslErrors(const QList<QSslError>& errors); W_SLOT(sslErrors)
};

W_REGISTER_ARGTYPE(QAbstractSocket::SocketError)
W_REGISTER_ARGTYPE(QList<QSslError>)
W_REGISTER_ARGTYPE(SslHttpRequestError)
W_OBJECT_IMPL_INLINE(SslHttpRequest)

#endif // SSLHTTPREQUEST_H

0 comments on commit a0c7330

Please sign in to comment.