📜 ⬆️ ⬇️

Qt: working with Vkontakte API and Phonon

Qt
The article describes the interaction of Qt with software interfaces such as Vkontakte API and Phonon, in real-life examples and detailed descriptions.
At the end of the article there is a link to the repository with the source code which you can download and run for free.

Immediately I would like to note that I am not a guru, but the program code can, it would be, better to implement. I will gladly accept your amendments, comments and criticism.
In order not to duplicate information - I will provide links to the Wiki.
Also, probably, it should be noted that the program was tested and developed on the Gnu / Linux / Gentoo platform (KDE) and I did not check it under other platforms and distributions but it should work.

General algorithm:

1. Authorization in the social network through the Vkontakte API (hereinafter VC).
2. Getting a list of your favorite audio recordings.
3. Creating a list of tracks from the list for Phonon.
4. Implementation of the ability to download the selected song.

Authorization

According to the documentation , for authorization you need to add a component that would allow us to view the web page. Qt has a WebKit for this . In the component you need to load the page vkontakte.ru/login.php where the user can go through the authorization procedure. Upon successful authorization, VC will redirect to the page /login_success.html and add the necessary data for us in the URL , such as session ID, salt and user ID, which we will need to retrieve (parse) from the link.
We connect the necessary modules to the project:
QT += webkit \
network \
xml \
phonon

Since VC returns data to XML (or JSON), we need to connect the XML module in order to simplify our life and not reinvent the wheel.
The network module will allow you to download the desired song from the VC and generally work with the network.
')
Phonon module will allow us to play our video locally or from the network. It also supports playing video files. It is a pleasure to work with this software interface, but about it later.
To work with VK, I have a VKAuth class in whose constructor I pass my application identifier. You can get it by registering your application in VK.
  1. #include <QApplication>
  2. #include <QObject>
  3. #include "mainwindow.h"
  4. #include "VKAuth.h"
  5. int main ( int argc, char ** argv )
  6. {
  7. QApplication app ( argc, argv ) ;
  8. QApplication :: setApplicationName ( "VKaudio" ) ;
  9. QApplication :: setApplicationVersion ( "0.01a" ) ;
  10. MainWindow window ;
  11. VKAuth vkAuth ( "2169954" ) ; // my application id
  12. QObject :: connect ( & vkAuth, SIGNAL ( unsuccess ( ) ) ,
  13. & app, SLOT ( quit ( ) )
  14. ) ;
  15. QObject :: connect ( & vkAuth, SIGNAL ( success ( QStringList ) ) ,
  16. & window, SLOT ( slotSuccess ( QStringList ) )
  17. ) ;
  18. vkAuth. show ( ) ;
  19. return app. exec ( ) ;
  20. }

The VKAuth class sends a success signal upon successful authorization and, as an argument, sends a list of links to songs and an unsuccess signal if the user refuses authorization.
Signal about successful authorization to connect to the slot of the main window of the application (the player itself) which adds the transferred list to the list of audio recordings, this process will be described in more detail later. The user’s denial of authorization signal is connected to the program shutdown slot (output).
  1. VKAuth :: VKAuth ( QString app, QWidget * parent ) : QWebView ( parent )
  2. {
  3. QObject :: connect ( this , SIGNAL ( urlChanged ( QUrl ) ) ,
  4. SLOT ( slotLinkChanged ( QUrl ) )
  5. ) ;
  6. m_app = app ;
  7. loadLoginPage ( ) ;
  8. }

As I wrote above, the application identifier and a pointer to the ancestor object are passed to the constructor. The class is inherited from QWebView which implements work with WebKit and allows us to display the required web page to the user. QWebView has a signal that is sent each time the page is changed. Since in the case of successful authorization, VC makes redirection (address change) exactly this signal suits us very well to catch this moment. As an argument, it passes a new link, which we will parse .
The loadLoginPage () method implements loading the required page for authorization
  1. void VKAuth :: loadLoginPage ( ) {
  2. QUrl url ( " vkontakte.ru/login.php" ) ;
  3. url. addQueryItem ( "app" , m_app ) ;
  4. url. addQueryItem ( "layout" , "popup" ) ;
  5. url. addQueryItem ( "type" , "browser" ) ;
  6. url. addQueryItem ( "settings" , "8" ) ;
  7. load ( url ) ;
  8. }

The QUrl class makes working with links very, very convenient.
QUrl breaks the link into parts.
image
image
image
The addQueryItem method adds GET parameters to the link, where the first argument is the name of the parameter and the second is its value.
Table with parameter and possible values:
image
The settings value is necessary for us in order for the user to allow us access to his audio recordings.
We pass the generated URL to the QwebView :: load () class method and it will load the page for authorization.
image
Suppose a user has passed authorization - VC redirects, a signal is sent with a new address, and control is given to the slotLinkChanged slot, which is responsible for processing the signal about changing the address.
  1. void VKAuth :: slotLinkChanged ( QUrl url )
  2. {
  3. if ( "/api/login_success.html" == url. path ( ) ) {
  4. QRegExp regexp ( " \" mid \ " : ([^,] +), \" secret \ " : \" ([^,] +) \ " , \" sid \ " : \" ([^,] + ) \ " " ) ;
  5. QString str = url. fragment ( ) ;
  6. if ( - 1 ! = regexp. indexIn ( str ) ) {
  7. m_mid = regexp. cap ( 1 ) ;
  8. m_secret = regexp. cap ( 2 ) ;
  9. m_sid = regexp. cap ( 3 ) ;
  10. QUrl request ( " api.vkontakte.ru/api.php?" ) ;
  11. QString sig ( getSig ( m_mid, m_secret, "api_id =" + m_app + "method = audio.get" + "uid =" + m_mid + "v = 3.0" ) ) ;
  12. request. addQueryItem ( "api_id" , m_app ) ;
  13. request. addQueryItem ( "method" , "audio.get" ) ;
  14. request. addQueryItem ( "uid" , m_mid ) ;
  15. request. addQueryItem ( "v" , "3.0" ) ;
  16. request. addQueryItem ( "sig" , sig ) ;
  17. request. addQueryItem ( "sid" , m_sid ) ;
  18. QNetworkAccessManager * manager = new QNetworkAccessManager ( this ) ;
  19. m_http = manager - > get ( QNetworkRequest ( request ) ) ;
  20. QObject :: connect ( m_http, SIGNAL ( finished ( ) ) , this , SLOT ( slotDone ( ) ) ) ;
  21. }
  22. } else if ( "/api/login_failure.html" == url. path ( ) ) {
  23. emit unsuccess ( ) ;
  24. }
  25. }
  26. QByteArray VKAuth :: getSig ( QString mid, QString secret, QString params = "" ) {
  27. QString str ;
  28. str. append ( mid ) ;
  29. if ( ! params. isEmpty ( ) )
  30. str. append ( params ) ;
  31. str. append ( secret ) ;
  32. return QCryptographicHash :: hash ( str. toUtf8 ( ) , QCryptographicHash :: Md5 ) . toHex ( ) ;
  33. }

In the slot, we check where we were redirected if the redirection was on login_success.html then the authorization was successful (according to the documentation ).
We create a regular expression that will help us obtain the necessary data for further work with the VC, namely:
mid, secret, sid.
image
To get a list of audio recordings, we need to call the audio.get method.
The Qhttp class simplifies working with the high-level HTTP protocol .
Pass the required parameters as GET parameters and create a hash (method - getSig).
The Qhttp :: done signal that is sent when the request is completed (the response is received) is connected to the slotDone slot.
  1. void VKAuth :: slotDone ( bool error ) {
  2. if ( error )
  3. emit unsuccess ( ) ;
  4. else {
  5. QStringList list ; // list with mp3
  6. QDomDocument dom ;
  7. dom. setContent ( m_http - > readAll ( ) ) ;
  8. QDomElement root = dom. firstChildElement ( ) ; // <response> root element
  9. QDomNode audioElement = root. firstChildElement ( ) ; // <audio>
  10. while ( ! audioElement. isNull ( ) ) {
  11. QString url = audioElement
  12. . toElement ( )
  13. . elementsByTagName ( "url" )
  14. . item ( 0 )
  15. . toElement ( ) // <url>
  16. . text ( ) ;
  17. list. append ( url ) ;
  18. audioElement = audioElement. nextSibling ( ) ; // next element
  19. }
  20. emit success ( list ) ; // load player window with playlist
  21. hide ( ) ; // hide this window
  22. }
  23. m_http - > close ( ) ;
  24. }

The VKAuth :: slotDone slot reads the received XML and creates a list of links to mp3 songs and then sends a success signal with the same list, closes the join and hides the window. Authorization completed list of songs received.

Phonon

It's very easy to work with Phonon, you need to create a meta object (MediaObject class), create audio (and / or video) output and combine them through Phonon :: createPath.
In the method MainWindow :: appendToList () - we add the list to mp3 files to the list.
  1. void MainWindow :: appendToList ( ) {
  2. if ( VKAudioList. isEmpty ( ) )
  3. return ;
  4. int index = sources. size ( ) ;
  5. foreach ( QString string, VKAudioList ) {
  6. Phonon :: MediaSource source ( string ) ;
  7. sources. append ( source ) ;
  8. }
  9. if ( ! sources. isEmpty ( ) )
  10. metaInformationResolver - > setCurrentSource ( sources. at ( index ) ) ;
  11. }


Download selected song

To download a song, you need to select it and click Download at the top of the program window, a dialog will appear in which you need to choose a place to download the file, naturally you should have rights to create (write) a file.
The button click signal is connected to the MainWindow :: slotDownload () slot.
image
  1. void MainWindow :: slotDownload ( ) {
  2. QString path = QFileDialog :: getExistingDirectory ( this , "Save to ..." ) ;
  3. if ( ! path. isEmpty ( ) ) {
  4. m_file = new QFile ;
  5. int item = musicTable - > currentRow ( ) ;
  6. Phonon :: MediaSource src = sources. at ( item ) ;
  7. m_pSrcUrl = new QUrl ( src. url ( ) ) ;
  8. m_file - > setFileName ( path + "/" + musicTable - > item ( item, musicTable - > currentColumn ( ) ) - > text ( ) + ".mp3" ) ;
  9. if ( m_file - > open ( QIODevice :: ReadWrite ) ) {
  10. m_pTcpSocket = new QTcpSocket ( this ) ;
  11. m_pTcpSocket - > connectToHost ( m_pSrcUrl - > host ( ) , 80 , QIODevice :: ReadWrite ) ;
  12. connect ( m_pTcpSocket, SIGNAL ( connected ( ) ) , this , SLOT ( slotConnected ( ) ) ) ;
  13. connect ( m_pTcpSocket, SIGNAL ( readyRead ( ) ) , this , SLOT ( slotReadyRead ( ) ) ) ;
  14. connect ( m_pTcpSocket, SIGNAL ( error ( QAbstractSocket :: SocketError ) ) ,
  15. this , SLOT ( slotDownloadError ( QAbstractSocket :: SocketError ) )
  16. ) ;
  17. connect ( m_pTcpSocket, SIGNAL ( disconnected ( ) ) , this , SLOT ( slotDisconnected ( ) ) ) ;
  18. } else
  19. QMessageBox :: warning ( this , "Warning" , "File open Err!" ) ;
  20. }
  21. }

After choosing a directory and successfully opening (creating) a file of a song that has not yet been downloaded, you need to send a request to the VC server to “give” the song to us. This will help us QtcpSocket which implements and greatly simplifies working with TCP / IP. After successfully connecting to the VC server, send an HTTP request to it:
  1. void MainWindow :: slotConnected ( ) {
  2. m_pTcpSocket - > write ( QString ( "GET" + m_pSrcUrl - > path ( ) + " \ r \ n " ) . toAscii ( ) ) ;
  3. }

To which he gives us the contents of the song that we need to write to the file:
  1. void MainWindow :: slotReadyRead ( ) {
  2. m_file - > write ( m_pTcpSocket - > read ( m_pTcpSocket - > bytesAvailable ( ) ) ) ;
  3. }

After the VC returns the entire contents of the song, it closes the connection, and it remains for us to close the file.
  1. void MainWindow :: slotDisconnected ( ) {
  2. m_file - > close ( ) ;
  3. m_pTcpSocket - > close ( ) ;
  4. }


As promised, at the beginning of the article, the link to the repository ( Mercurial ): https://bitbucket.org/denis_medved/vkaudio
Many thanks to Max Schlee and bhv for the wonderful book “Qt 4.5: Professional C ++ programming” and of course to the entire open community.

upd :
Instead of outdated QHttp and QTcpSocket, QNetworkAccessManager is used.

Source: https://habr.com/ru/post/115397/


All Articles