Skip to content

Commit 9c1fa8f

Browse files
committed
Added support for youtube videos as audio source
1 parent 9f3c07a commit 9c1fa8f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1707
-150
lines changed

app/main.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "filesystem/file.h"
22
#include "logger.h"
33
#include "services/nextcloud/nextcloud.h"
4+
#include "services/youtube/youtube.h"
45
#include "settings/quick/settingsmanager.h"
56
#include "tools/audio/thumbnails/audiothumbnailprovider.h"
67
#include "updates/version.h"
@@ -140,6 +141,7 @@ auto main(int argc, char *argv[]) -> int
140141

141142
// Misc
142143
Files::File::init(Services::NextCloud::qmlInstance(&engine));
144+
Services::YouTube::instance()->setNetworkManager(engine.networkAccessManager());
143145

144146
engine.addImageProvider(u"audioElementIcons"_s, new AudioThumbnailProvider());
145147
engine.loadFromModule("ui"_L1, "Main"_L1);

app/ui/tools/audio/editor/views/ElementFileAddView.qml

+51-54
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,16 @@ Item {
1313

1414
function setMode(mode) {
1515
if (root.mode !== mode) {
16-
url_text_field.clear()
16+
url_text_field.clear();
1717
}
18-
19-
root.mode = mode
18+
root.mode = mode;
2019
}
2120

2221
Connections {
2322
target: AudioTool.editor
2423

2524
function onCurrentElementChanged() {
26-
root.setMode(0)
25+
root.setMode(0);
2726
}
2827
}
2928

@@ -54,8 +53,7 @@ Item {
5453

5554
CustomButton {
5655
id: spotify_button
57-
visible: AudioTool.editor.currentElement
58-
&& AudioTool.editor.currentElement.type === 0
56+
visible: AudioTool.editor.currentElement && AudioTool.editor.currentElement.type === 0
5957

6058
iconText: FontAwesome.spotify
6159
iconFont: FontAwesome.fontBrands
@@ -142,56 +140,55 @@ Item {
142140
iconText: FontAwesome.plus
143141

144142
onClicked: {
145-
AudioTool.editor.addUrl(url_text_field.text, root.mode)
143+
AudioTool.editor.addUrl(url_text_field.text, root.mode);
146144
}
147145
}
148146
}
149147

150-
// Rectangle {
151-
// id: youtube_adder
152-
// visible: root.mode > 1 && AudioTool.editor.currentElement
153-
// && AudioTool.editor.currentElement.type < 2
154-
// anchors.topMargin: 5
155-
// anchors.top: url_adder.bottom
156-
// anchors.right: parent.right
157-
// anchors.left: parent.left
158-
159-
// height: Sizes.toolbarHeight
160-
// color: palette.alternateBase
161-
162-
// CustomToolBarButton {
163-
// id: youtube_adder_icon
164-
// anchors.left: parent.left
165-
// anchors.leftMargin: 10
166-
// enabled: false
167-
168-
// iconText: FontAwesome.youtube
169-
// iconFont: FontAwesome.fontBrands
170-
// }
171-
172-
// TextField {
173-
// id: youtube_text_field
174-
// anchors.left: youtube_adder_icon.right
175-
// anchors.right: youtube_adder_button.left
176-
// anchors.top: parent.top
177-
// anchors.bottom: parent.bottom
178-
// anchors.leftMargin: 10
179-
// anchors.rightMargin: 10
180-
181-
// selectByMouse: true
182-
// placeholderText: qsTr("YouTube URL")
183-
// }
184-
185-
// CustomToolBarButton {
186-
// id: youtube_adder_button
187-
// anchors.right: parent.right
188-
// anchors.rightMargin: 10
189-
190-
// iconText: FontAwesome.plus
191-
192-
// onClicked: {
193-
// AudioTool.editor.addYtUrl(youtube_text_field.text)
194-
// }
195-
// }
196-
// }
148+
Rectangle {
149+
id: youtube_adder
150+
visible: root.mode > 1 && AudioTool.editor.currentElement && AudioTool.editor.currentElement.type < 2
151+
anchors.topMargin: 5
152+
anchors.top: url_adder.bottom
153+
anchors.right: parent.right
154+
anchors.left: parent.left
155+
156+
height: Sizes.toolbarHeight
157+
color: palette.alternateBase
158+
159+
CustomToolBarButton {
160+
id: youtube_adder_icon
161+
anchors.left: parent.left
162+
anchors.leftMargin: 10
163+
enabled: false
164+
165+
iconText: FontAwesome.youtube
166+
iconFont: FontAwesome.fontBrands
167+
}
168+
169+
TextField {
170+
id: youtube_text_field
171+
anchors.left: youtube_adder_icon.right
172+
anchors.right: youtube_adder_button.left
173+
anchors.top: parent.top
174+
anchors.bottom: parent.bottom
175+
anchors.leftMargin: 10
176+
anchors.rightMargin: 10
177+
178+
selectByMouse: true
179+
placeholderText: qsTr("YouTube URL")
180+
}
181+
182+
CustomToolBarButton {
183+
id: youtube_adder_button
184+
anchors.right: parent.right
185+
anchors.rightMargin: 10
186+
187+
iconText: FontAwesome.plus
188+
189+
onClicked: {
190+
AudioTool.editor.addYtUrl(youtube_text_field.text);
191+
}
192+
}
193+
}
197194
}

misc/scripts/generate-coverage.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ if [ -z "$dir" ]; then echo "Please specify a directory!" && exit 1; fi
99
fastcov --include src/ --lcov -o $dir/$filename
1010
lcov --remove $dir/$filename -o $dir/$filename_filtered '/usr/*' '*/**/tests/*' '*/**/*plugin_*.*' '*/**/*_qmltyperegistrations.cpp' '*/**/thirdparty/*' '*/**/*_autogen/*' --ignore-errors unused,unused
1111

12-
genhtml --ignore-errors unmapped -o $dir/coverage_html $dir/$filename_filtered
12+
genhtml --synthesize-missing --ignore-errors source,unmapped -o $dir/coverage_html $dir/$filename_filtered

src/CMakeLists.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ set(SRC_AUDIO
8181
tools/audio/thumbnails/loaders/tagimageloader.h
8282
tools/audio/thumbnails/loaders/tagimageloader.cpp
8383
tools/audio/thumbnails/loaders/webimageloader.h
84-
tools/audio/thumbnails/loaders/webimageloader.cpp)
84+
tools/audio/thumbnails/loaders/webimageloader.cpp
85+
tools/audio/thumbnails/loaders/youtubeimageloader.h
86+
tools/audio/thumbnails/loaders/youtubeimageloader.cpp
87+
)
8588

8689
set_source_files_properties(${SRC_AUDIO} PROPERTIES UNITY_GROUP "audio")
8790

src/common/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ target_link_libraries(common
151151
Qt6::Qml
152152
Qt6::Quick
153153
Qt6::Network
154+
Qt6::Multimedia
154155
PRIVATE
155156
${MARKDOWN_LIBRARY}
156157
${QTKEYCHAIN_LIBRARY})

src/common/utils/fileutils.cpp

+21-1
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,32 @@ auto FileUtils::getMimeType(const QString &filename) -> FileUtils::MimeType
145145
{
146146
if (const auto extension = FileUtils::suffix(filename); extensionToMimeType.contains(extension))
147147
{
148-
return extensionToMimeType[extension];
148+
return extensionToMimeType.value(extension);
149149
}
150150

151151
return MimeType::Unknown;
152152
}
153153

154+
auto FileUtils::mimeTypeStringToFileFormat(const QString &string) -> QMediaFormat::FileFormat
155+
{
156+
if (mimeTypeToFileQtFormat.contains(string))
157+
{
158+
return mimeTypeToFileQtFormat.value(string);
159+
}
160+
161+
return QMediaFormat::FileFormat::UnspecifiedFormat;
162+
}
163+
164+
auto FileUtils::audioCodecNameToEnum(const QString &string) -> QMediaFormat::AudioCodec
165+
{
166+
if (audioCodecToQtCodec.contains(string))
167+
{
168+
return audioCodecToQtCodec.value(string);
169+
}
170+
171+
return QMediaFormat::AudioCodec::Unspecified;
172+
}
173+
154174
auto FileUtils::splitFileNameAndSuffix(const QString &fileName) -> std::pair<QString, QString>
155175
{
156176
auto suffix = FileUtils::suffix(fileName);

src/common/utils/fileutils.h

+29-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include <QHash>
4+
#include <QMediaFormat>
45
#include <QString>
56
#include <QStringList>
67

@@ -40,7 +41,9 @@ class FileUtils
4041
XM
4142
};
4243

43-
static auto getMimeType(const QString &filename) -> MimeType;
44+
[[nodiscard]] static auto getMimeType(const QString &filename) -> MimeType;
45+
[[nodiscard]] static auto mimeTypeStringToFileFormat(const QString &string) -> QMediaFormat::FileFormat;
46+
[[nodiscard]] static auto audioCodecNameToEnum(const QString &string) -> QMediaFormat::AudioCodec;
4447

4548
private:
4649
static auto splitFileNameAndSuffix(const QString &fileName) -> std::pair<QString, QString>;
@@ -62,6 +65,31 @@ class FileUtils
6265
{QStringLiteral("nst"), MimeType::MOD}, {QStringLiteral("wow"), MimeType::MOD},
6366
{QStringLiteral("s3m"), MimeType::S3M}, {QStringLiteral("it"), MimeType::IT},
6467
{QStringLiteral("xm"), MimeType::XM},
68+
};
69+
70+
inline static const auto mimeTypeToFileQtFormat = QHash<QString, QMediaFormat::FileFormat>{
71+
{QStringLiteral("audio/aac"), QMediaFormat::FileFormat::AAC},
72+
{QStringLiteral("audio/mpeg"), QMediaFormat::FileFormat::MP3},
73+
{QStringLiteral("audio/ogg"), QMediaFormat::FileFormat::Ogg},
74+
{QStringLiteral("audio/wav"), QMediaFormat::FileFormat::Wave},
75+
{QStringLiteral("audio/webm"), QMediaFormat::FileFormat::WebM},
76+
{QStringLiteral("audio/mp4"), QMediaFormat::FileFormat::Mpeg4Audio},
77+
};
6578

79+
inline static const auto audioCodecToQtCodec = QHash<QString, QMediaFormat::AudioCodec>{
80+
{QStringLiteral("opus"), QMediaFormat::AudioCodec::Opus},
81+
{QStringLiteral("mp4a.40.1"), QMediaFormat::AudioCodec::AAC},
82+
{QStringLiteral("mp4a.40.2"), QMediaFormat::AudioCodec::AAC},
83+
{QStringLiteral("mp4a.40.17"), QMediaFormat::AudioCodec::AAC},
84+
{QStringLiteral("vorbis"), QMediaFormat::AudioCodec::Vorbis},
85+
{QStringLiteral("wma"), QMediaFormat::AudioCodec::WMA},
86+
{QStringLiteral("ac3"), QMediaFormat::AudioCodec::AC3},
87+
{QStringLiteral("aac"), QMediaFormat::AudioCodec::AAC},
88+
{QStringLiteral("alac"), QMediaFormat::AudioCodec::ALAC},
89+
{QStringLiteral("eac3"), QMediaFormat::AudioCodec::EAC3},
90+
{QStringLiteral("mp3"), QMediaFormat::AudioCodec::MP3},
91+
{QStringLiteral("wav"), QMediaFormat::AudioCodec::Wave},
92+
{QStringLiteral("wave"), QMediaFormat::AudioCodec::Wave},
93+
{QStringLiteral("flac"), QMediaFormat::AudioCodec::FLAC},
6694
};
6795
};

src/services/CMakeLists.txt

+14-1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ set(SRC_GOOGLEDRIVE
7878
google/googledrivereply.h
7979
)
8080

81+
set(SRC_YOUTUBE
82+
youtube/youtube.h
83+
youtube/youtube.cpp
84+
youtube/pipedconnector.h
85+
youtube/pipedconnector.cpp
86+
youtube/videoid.h
87+
youtube/videoid.cpp
88+
youtube/playlistid.h
89+
youtube/playlistid.cpp
90+
)
91+
8192
qt_add_library(services STATIC)
8293

8394
qt_add_qml_module(services
@@ -87,6 +98,7 @@ qt_add_qml_module(services
8798
${SRC_REST}
8899
${SRC_SPOTIFY}
89100
${SRC_NEXTCLOUD}
101+
${SRC_YOUTUBE}
90102
# GoogleDrive support is disabled, see comment in googledrive.h
91103
# ${SRC_GOOGLEDRIVE}
92104
)
@@ -105,7 +117,8 @@ target_include_directories(services
105117
${CMAKE_CURRENT_SOURCE_DIR}
106118
PRIVATE
107119
${CMAKE_CURRENT_SOURCE_DIR}/spotify
108-
${CMAKE_CURRENT_SOURCE_DIR}/nextcloud)
120+
${CMAKE_CURRENT_SOURCE_DIR}/nextcloud
121+
${CMAKE_CURRENT_SOURCE_DIR}/youtube)
109122
# ${CMAKE_CURRENT_SOURCE_DIR}/google
110123

111124
target_link_libraries(services

src/services/rest/restserviceconnector.cpp

+12-2
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,24 @@ using namespace Services;
77

88
constexpr auto RATELIMIT_TIMEOUT = std::chrono::seconds(2);
99

10-
RESTServiceConnector::RESTServiceConnector(QNetworkAccessManager &networkManager,
10+
RESTServiceConnector::RESTServiceConnector(QNetworkAccessManager *networkManager,
1111
const QLoggingCategory &loggingCategory, QStringList &&recoverableErrors,
1212
QObject *parent)
13-
: QObject(parent), m_networkManager(networkManager), m_loggingCategory(loggingCategory),
13+
: QObject(parent), m_loggingCategory(loggingCategory), m_networkManager(networkManager),
1414
m_recoverableErrors(std::move(recoverableErrors))
1515
{
1616
}
1717

18+
auto RESTServiceConnector::networkManager() const -> QNetworkAccessManager *
19+
{
20+
return m_networkManager;
21+
}
22+
23+
void RESTServiceConnector::setNetworkManager(QNetworkAccessManager *networkManager)
24+
{
25+
m_networkManager = networkManager;
26+
}
27+
1828
auto RESTServiceConnector::isOnCooldown() const -> bool
1929
{
2030
return m_isOnCooldown;

src/services/rest/restserviceconnector.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class RESTServiceConnector : public QObject
2222
{
2323
Q_OBJECT
2424
public:
25-
RESTServiceConnector(QNetworkAccessManager &networkManager, const QLoggingCategory &loggingCategory,
25+
RESTServiceConnector(QNetworkAccessManager *networkManager, const QLoggingCategory &loggingCategory,
2626
QStringList &&recoverableErrors, QObject *parent = nullptr);
2727

2828
virtual void grantAccess() = 0;
@@ -35,6 +35,9 @@ class RESTServiceConnector : public QObject
3535
virtual auto customRequest(const QNetworkRequest &req, const QByteArray &verb, const QByteArray &data)
3636
-> QFuture<RestReply> = 0;
3737

38+
[[nodiscard]] auto networkManager() const -> QNetworkAccessManager *;
39+
void setNetworkManager(QNetworkAccessManager *networkManager);
40+
3841
signals:
3942
void accessGranted();
4043
void statusChanged(const Status::Type &type, const QString &message);
@@ -68,7 +71,6 @@ protected slots:
6871
void updateTokenExpireTime(std::chrono::seconds expiresIn);
6972
[[nodiscard]] auto isTokenExpired() const -> bool;
7073

71-
QNetworkAccessManager &m_networkManager;
7274
const QLoggingCategory &m_loggingCategory;
7375
bool m_wasConfigured = false;
7476

@@ -79,6 +81,7 @@ protected slots:
7981
void handleRateLimit(std::pair<QPromise<RestReply>, RestRequest> &&pair,
8082
const QList<QNetworkReply::RawHeaderPair> &headers);
8183

84+
QNetworkAccessManager *m_networkManager = nullptr;
8285
bool m_isOnCooldown = false;
8386
size_t m_maxConcurrentRequests = 1;
8487
int m_nextQueueId = 1;

src/services/rest/restserviceconnectorlocal.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ using namespace Qt::Literals::StringLiterals;
1111
using namespace Services;
1212
using namespace Common::Settings;
1313

14-
RESTServiceConnectorLocal::RESTServiceConnectorLocal(QNetworkAccessManager &networkManager, O2 *o2,
14+
RESTServiceConnectorLocal::RESTServiceConnectorLocal(QNetworkAccessManager *networkManager, O2 *o2,
1515
const QLoggingCategory &loggingCategory, QObject *parent = nullptr)
1616
: RESTServiceConnector(networkManager, loggingCategory, {}, parent), m_o2(o2)
1717
{
@@ -207,7 +207,7 @@ auto RESTServiceConnectorLocal::customRequest(const QNetworkRequest &request, co
207207

208208
auto RESTServiceConnectorLocal::makeRequestor() -> O2Requestor *
209209
{
210-
auto *requestor = new O2Requestor(&m_networkManager, m_o2, this);
210+
auto *requestor = new O2Requestor(networkManager(), m_o2, this);
211211

212212
connect(requestor,
213213
QOverload<int, QNetworkReply::NetworkError, QString, QByteArray, QList<QNetworkReply::RawHeaderPair>>::of(

src/services/rest/restserviceconnectorlocal.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class RESTServiceConnectorLocal : public RESTServiceConnector
2929
{
3030
Q_OBJECT
3131
public:
32-
RESTServiceConnectorLocal(QNetworkAccessManager &networkManager, O2 *o2, const QLoggingCategory &loggingCategory,
32+
RESTServiceConnectorLocal(QNetworkAccessManager *networkManager, O2 *o2, const QLoggingCategory &loggingCategory,
3333
QObject *parent);
3434

3535
void grantAccess() override;

0 commit comments

Comments
 (0)