File indexing completed on 2024-05-12 16:21:17

0001 // SPDX-FileCopyrightText: 2021 Jonah Brüchert <jbb@kaidan.im>
0002 //
0003 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 
0005 #include <QApplication>
0006 #include <QQmlApplicationEngine>
0007 #include <QtQml>
0008 #include <QSortFilterProxyModel>
0009 #include <QUrl>
0010 #include <QQuickWindow>
0011 #include <QQuickStyle>
0012 #include <QStyle>
0013 #include <QStyleFactory>
0014 
0015 #include <KLocalizedContext>
0016 #include <KLocalizedString>
0017 #include <KCrash>
0018 #include <KAboutData>
0019 
0020 #include <mprisplugin.h>
0021 
0022 #include "clipboard.h"
0023 #include "searchmodel.h"
0024 #include "albummodel.h"
0025 #include "videoinfoextractor.h"
0026 #include "artistmodel.h"
0027 #include "userplaylistmodel.h"
0028 #include "errorhandler.h"
0029 #include "playlistmodel.h"
0030 #include "playerutils.h"
0031 #include "library.h"
0032 #include "thumbnailsource.h"
0033 #include "blur.h"
0034 #include "localplaylistmodel.h"
0035 #include "localplaylistsmodel.h"
0036 #include "playlistimporter.h"
0037 
0038 #include <ThreadedDatabase>
0039 
0040 constexpr auto URI = "org.kde.ytmusic";
0041 
0042 Q_DECL_EXPORT int main(int argc, char *argv[])
0043 {
0044     QApplication app(argc, argv);
0045 
0046     // set default style and icon theme
0047     QIcon::setFallbackThemeName(QStringLiteral("breeze"));
0048     if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE") && QQuickStyle::name().isEmpty()) {
0049         QQuickStyle::setStyle(QStringLiteral("org.kde.desktop"));
0050         QApplication::setStyle(QStyleFactory::create(QStringLiteral("Breeze")));
0051     }
0052 
0053     // WORKAROUND: Force QtMultimedia gstreamer backend
0054     /*
0055       ffmpeg unfortunately loses the connection to youtube's servers with the following error message after ~2 minutes
0056       at the time of writing (2023-10-29 with ffmpeg 6.0 and Qt 6.6.1)
0057         [tls @ 0x561f31d3d500] Error in the pull function.
0058         [tls @ 0x561f31d3d500] IO error: Connection reset by peer
0059         [mov,mp4,m4a,3gp,3g2,mj2 @ 0x561f3225e000] Packet corrupt (stream = 0, dts = 4590016).
0060         [mov,mp4,m4a,3gp,3g2,mj2 @ 0x561f3225e000] stream 0, offset 0x19c06c: partial file
0061         [aac @ 0x561f3244bc00] Input buffer exhausted before END element found
0062     */
0063     qputenv("QT_MEDIA_BACKEND", "gstreamer");
0064 
0065     QCoreApplication::setOrganizationName(QStringLiteral("KDE"));
0066     QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
0067     QCoreApplication::setApplicationName(QStringLiteral("audiotube"));
0068 
0069     KLocalizedString::setApplicationDomain("audiotube");
0070 
0071     KAboutData about(QStringLiteral("audiotube"),
0072                      i18n("AudioTube"),
0073                      QStringLiteral(AUDIOTUBE_VERSION_STRING),
0074                      i18nc("YouTube Music is a music streaming service by Google", "Stream music from YouTube Music"),
0075                      KAboutLicense::GPL_V3,
0076                      i18n("© 2021-2023 Jonah Brüchert, 2021-2023 KDE Community"));
0077     about.addAuthor(i18n("Jonah Brüchert"), i18n("Maintainer"), QStringLiteral("jbb@kaidan.im"));
0078     about.addAuthor(i18n("Mathis Brüchert"), i18n("Designer"), QStringLiteral("mbb@kaidan.im"));
0079     about.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails"));
0080     about.setOrganizationDomain("kde.org");
0081     about.setBugAddress("https://bugs.kde.org/describecomponents.cgi?product=audiotube");
0082     auto future = YTMusicThread::instance()->version();
0083     QCoro::connect(std::move(future), &app, [&about](const auto &version) {
0084         about.addComponent(QStringLiteral("ytmusicapi"),
0085                            i18nc("YouTube Music is a music streaming service by Google",
0086                                  "Unofficial API for YouTube Music"),
0087                            version);
0088         KAboutData::setApplicationData(about);
0089     });
0090 
0091     QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.audiotube")));
0092 
0093     KCrash::initialize();
0094 
0095     QQmlApplicationEngine engine;
0096 
0097     MprisPlugin(&engine).registerTypes("org.nemomobile.qtmpris");
0098 
0099     qmlRegisterType<SearchModel>(URI, 1, 0, "SearchModel");
0100     qmlRegisterType<AlbumModel>(URI, 1, 0, "AlbumModel");
0101     qmlRegisterType<VideoInfoExtractor>(URI, 1, 0, "VideoInfoExtractor");
0102     qmlRegisterType<ArtistModel>(URI, 1, 0, "ArtistModel");
0103     qmlRegisterType<PlaylistModel>(URI, 1, 0, "PlaylistModel");
0104     qmlRegisterUncreatableType<PlaybackHistoryModel>(URI, 1, 0, "PlaybackHistoryModel","");
0105     qmlRegisterType<QSortFilterProxyModel>(URI, 1, 0, "SortFilterModel");
0106     qmlRegisterType<Blur>(URI, 1, 0, "Blur");
0107     qmlRegisterType<LocalSearchModel>(URI, 1, 0, "LocalSearchModel");
0108     qmlRegisterType<LocalPlaylistModel>(URI, 1, 0, "LocalPlaylistModel");
0109     qmlRegisterType<LocalPlaylistsModel>(URI, 1, 0, "LocalPlaylistsModel");
0110     qmlRegisterType<PlaylistImporter>(URI, 1, 0, "PlaylistImporter");
0111     qmlRegisterSingletonType(URI, 1, 0, "About", [](QQmlEngine *engine, QJSEngine *) -> QJSValue {
0112         return engine->toScriptValue(KAboutData::applicationData());
0113     });
0114 
0115     qmlRegisterSingletonType<UserPlaylistModel>(URI, 1, 0, "UserPlaylistModel", [](QQmlEngine *, QJSEngine *) {
0116         return new UserPlaylistModel();
0117     });
0118     qmlRegisterSingletonType<ErrorHandler>(URI, 1, 0, "ErrorHandler", [](QQmlEngine *, QJSEngine *) {
0119         return new ErrorHandler();
0120     });
0121 
0122     qmlRegisterSingletonType<PlayerUtils>(URI, 1, 0, "PlayerUtils", [](QQmlEngine *, QJSEngine *) {
0123         return new PlayerUtils();
0124     });
0125     qmlRegisterSingletonType<Clipboard>(URI, 1, 0, "Clipboard", [](QQmlEngine *, QJSEngine *) {
0126         return new Clipboard();
0127     });
0128 
0129     qmlRegisterSingletonInstance<Library>(URI, 1, 0, "Library", &Library::instance());
0130     qmlRegisterType<ThumbnailSource>(URI, 1, 0, "ThumbnailSource");
0131     qmlRegisterAnonymousType<FavouriteWatcher>(URI, 1);
0132     qmlRegisterAnonymousType<WasPlayedWatcher>(URI, 1);
0133 
0134     engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
0135     engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
0136 
0137     if (engine.rootObjects().isEmpty()) {
0138         return -1;
0139     }
0140 
0141     return app.exec();
0142 }