~ubuntu-branches/ubuntu/lucid/lastfm/lucid

« back to all changes in this revision

Viewing changes to src/lastfmapplication.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Devid Filoni
  • Date: 2008-07-14 16:46:20 UTC
  • mfrom: (1.2.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 16.
  • Revision ID: james.westby@ubuntu.com-20080714164620-cattx7c83ihhnk4l
Tags: upstream-1.5.1.31879.dfsg
ImportĀ upstreamĀ versionĀ 1.5.1.31879.dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/***************************************************************************
2
 
 *   Copyright (C) 2005 - 2007 by                                          *
 
2
 *   Copyright (C) 2005 - 2008 by                                          *
3
3
 *      Christian Muehlhaeuser, Last.fm Ltd <chris@last.fm>                *
4
4
 *      Erik Jaelevik, Last.fm Ltd <erik@last.fm>                          *
5
5
 *      Jono Cole, Last.fm Ltd <jono@last.fm>                              *
23
23
#include "lastfmapplication.h"
24
24
 
25
25
#include "configwizard.h"
 
26
#include "confirmdialog.h"
26
27
#include "container.h"
27
28
#include "LastMessageBox.h"
28
29
#include "libFingerprint/FingerprintCollector.h"
29
30
#include "libFingerprint/FingerprintQueryer.h"
30
31
#include "logger.h"
31
32
#include "loginwidget.h"
32
 
#include "MediaDeviceConfirmDialog.h"
 
33
#include "MediaDeviceScrobbler.h"
33
34
#include "playercommands.h"
34
35
#include "playerlistener.h"
35
36
#include "User.h"
39
40
#include "Scrobbler-1.2.h"
40
41
#include "LastFmSettings.h"
41
42
#include "version.h"
42
 
#include "Bootstrapper/PluginBootstrapper.h"
43
43
#include "CachedHttpJanitor.h"
 
44
#include "interfaces/ExtensionInterface.h"
 
45
 
 
46
#ifndef Q_WS_X11
 
47
    #include "Bootstrapper/PluginBootstrapper.h"
 
48
#endif
44
49
 
45
50
#include "MooseCommon.h"
46
51
#include "UnicornCommon.h"
57
62
 
58
63
#ifdef Q_WS_MAC
59
64
    #include "itunesscript.h"
 
65
    #include "ITunesPluginInstaller.h"
60
66
    #include <Carbon/Carbon.h>
61
67
#endif
62
68
 
68
74
          m_user( 0 ),
69
75
          m_activeNorman( 0 ),
70
76
          m_state( State::Stopped ),
71
 
          m_proxyTestDone( false )
 
77
          m_proxyTestDone( false ),
 
78
          m_extensionsLoaded( false )
72
79
{
73
80
    #ifdef Q_WS_MAC
74
81
        m_pidFile.setFileName( MooseUtils::savePath( "lastfm.pid" ) );
84
91
    // TODO why do we do this, and do we need to?
85
92
    //   Good question, this was added as a hacky workaround to try and fix
86
93
    //   some QHttp wonkiness we had on some machines back in the day. Best
87
 
    //   not removing it.
 
94
    //   not removing it. May fix obscure bugs like, ws.as.com is not accessible
 
95
    //   for 3 or 4 minutes
 
96
    // NOTE it's prolly a good thing for feedback reasons anyway, the UI appears
 
97
    //   quicker and thus feels like it starts faster
88
98
    QTimer::singleShot( 0, this, SLOT( init() ) );
89
99
 
90
100
    // We're doing this here because the version.h file is updated just before
98
108
    CachedHttp::setCustomUserAgent( QString( "Last.fm Client " ) + LASTFM_CLIENT_VERSION );
99
109
    CachedHttp::setCustomCachePath( MooseUtils::cachePath() );
100
110
 
101
 
    // Start helper daemon _before_ we bind any sockets, otherwise they might
102
 
    // end up being blocked by the child-process.
103
 
    MooseUtils::installHelperApp();
104
 
 
105
111
    // do this asap to prevent multiple instances instantiating
106
112
    m_control = new QTcpServer( this );
107
 
    connect( m_control, SIGNAL(newConnection()), SLOT(onControlConnect()) );
 
113
    connect( m_control, SIGNAL( newConnection() ), SLOT( onControlConnect() ) );
108
114
 
109
115
    // Try to use the default port, otherwise revert to using an available port
110
116
    // and write the port we're using to the Settings. sendToInstance() will use
114
120
 
115
121
    The::settings().setControlPort( m_control->serverPort() );
116
122
 
117
 
    connect( this, SIGNAL(event( int, QVariant )), SLOT(onAppEvent( int, QVariant )) );
 
123
    connect( this, SIGNAL( event( int, QVariant ) ), SLOT( onAppEvent( int, QVariant ) ) );
118
124
 
119
125
    initLogger();
120
126
    initTranslator();
125
131
    setWindowIcon( QIcon( MooseUtils::dataPath( "icons/as.ico" ) ) );
126
132
  #endif
127
133
 
 
134
  #ifdef WIN32
 
135
    MooseUtils::disableHelperApp();    
 
136
  #endif
 
137
 
128
138
    // This is needed so that relative paths will work on Windows regardless
129
139
    // of where the app is launched from.
130
140
    QDir::setCurrent( applicationDirPath() );
132
142
    // this must be set before dialogs spawn in init() or whatever
133
143
    m_user = new User( this );
134
144
 
135
 
    //REFACTOR this class merged here!
 
145
    // TODO this class merged here!
136
146
    The::webService(); //init webservice stuff
137
 
    connect( The::webService(), SIGNAL(result( Request* )), SLOT(onRequestReturned( Request* )) );
138
 
 
139
 
    // HACK: this is only here to fix the bug where the initial wizard runs and we're behind a proxy.
140
 
    // We need to have done these tests before running the VerifyUserRequest, otherwise the Http object
141
 
    // will not pick an autodetected proxy if there is one. Refactor for 1.5.
142
 
    if( !The::settings().isUseProxy() )
143
 
    {
144
 
        ProxyTestRequest* proxyOnTest = new ProxyTestRequest( true );
145
 
        ProxyTestRequest* proxyOffTest = new ProxyTestRequest( false );
146
 
        proxyOnTest->start();
147
 
        proxyOffTest->start();
148
 
    }
 
147
    connect( The::webService(), SIGNAL( result( Request* ) ), SLOT( onRequestReturned( Request* ) ) );
149
148
 
150
149
    // Scrobbler must be initialised before the listener, otherwise crashes might ensue
151
150
    m_scrobbler = new ScrobblerManager( this );
168
167
    new ITunesScript( this, m_listener );
169
168
  #endif
170
169
 
171
 
    m_fpCollector = new FingerprintCollector( 1, this );
 
170
    m_fpCollector = new FingerprintCollector( 1 /*number of threads*/, this );
172
171
    m_fpQueryer = new FingerprintQueryer( this );
173
172
    connect( m_fpQueryer, SIGNAL( trackFingerprinted( TrackInfo, bool ) ),
174
173
                          SLOT( onFingerprintQueryDone( TrackInfo, bool ) ) );
175
174
 
176
175
    m_radio = new Radio( this );
177
 
    connect( m_radio, SIGNAL(stateChanged( RadioState )), SLOT(onRadioStateChanged( RadioState )) );
 
176
    connect( m_radio, SIGNAL( stateChanged( RadioState ) ), SLOT( onRadioStateChanged( RadioState ) ) );
178
177
 
179
178
    m_container = new Container;
180
 
    connect( m_container, SIGNAL(becameVisible()), SLOT(fetchMetaData()) );
 
179
    connect( m_container, SIGNAL( becameVisible() ), SLOT( fetchMetaData() ) );
181
180
 
182
181
    // Look for expired cached files and remove them
183
182
    new CachedHttpJanitor( MooseUtils::cachePath(), this );
185
184
    m_playbackEndedTimer = new QTimer( this );
186
185
    m_playbackEndedTimer->setSingleShot( true );
187
186
    m_playbackEndedTimer->setInterval( 200 );
188
 
    connect( m_playbackEndedTimer, SIGNAL(timeout()), SLOT(onPlaybackEndedTimerTimeout()) );
 
187
    connect( m_playbackEndedTimer, SIGNAL( timeout() ), SLOT( onPlaybackEndedTimerTimeout() ) );
 
188
 
 
189
    // This is needed for the app to shut down properly when asked by Windows
 
190
    // to shut down.
 
191
    connect( this, SIGNAL( endSession() ), SLOT( quit() ) );
189
192
}
190
193
 
191
194
 
203
206
    }
204
207
  #endif
205
208
 
 
209
 
206
210
    foreach ( QString const arg, qApp->arguments().mid( 1 ) ) //skip arg[0] the appname+path
207
211
        parseCommand( arg );
208
212
 
209
213
    // Need to save the state from before we run the wizard as the wizard will change it
210
214
    bool firstRunBeforeWizard = The::settings().isFirstRun();
211
215
 
 
216
    #ifdef Q_OS_MAC
 
217
      // the installation of the plugin is done in the ctor
 
218
      ITunesPluginInstaller pluginInstaller;
 
219
      pluginInstaller.install();
 
220
    #endif
 
221
 
 
222
 
212
223
    if ( The::settings().isFirstRun() )
213
224
    {
214
 
        LOG( 3, "First run, launching config wizard\n" );
 
225
        // HACK: this is only here to fix the bug where the initial wizard runs and we're behind a proxy.
 
226
        // We need to have done these tests before running the VerifyUserRequest, otherwise the Http object
 
227
        // will not pick an autodetected proxy if there is one. Refactor for 1.5.
 
228
        if( The::settings().isFirstRun() && !The::settings().isUseProxy() )
 
229
        {
 
230
            ProxyTestRequest* proxyOnTest = new ProxyTestRequest( true );
 
231
            ProxyTestRequest* proxyOffTest = new ProxyTestRequest( false );
 
232
            proxyOnTest->start();
 
233
            proxyOffTest->start();
 
234
        }
 
235
 
 
236
        LOGL( 3, "First run, launching config wizard" );
215
237
        QFile( MooseUtils::savePath( "mediadevice.db" ) ).remove();
216
238
 
217
239
        ConfigWizard wiz( NULL, ConfigWizard::Login );
 
240
 
218
241
        if ( wiz.exec() == QDialog::Rejected )
219
242
        {
220
243
            // If user cancels config wizard, we need to exit
222
245
            return;
223
246
        }
224
247
    }
 
248
  #ifndef LINUX
 
249
    else
 
250
    {
 
251
      #ifdef Q_OS_MAC
 
252
        // We just installed the plugin for the first time
 
253
        bool const needsBootstrap = pluginInstaller.needsTwiddlyBootstrap();
 
254
      #else
 
255
        // The update wizard invites the user to upgrade the iTunes plugin, and
 
256
        // it did this last time the client was invoked. iTunes has been 
 
257
        // stoppped, so the new plugin will be running when we Twiddly starts
 
258
        // iTunes during the bootstrap
 
259
        bool needsBootstrap = The::settings().weWereJustUpgraded();
 
260
      #endif
 
261
        
 
262
        //NOTE some of this code is duplicated in Container::updateCheckDone()
 
263
        if ( needsBootstrap )
 
264
        {
 
265
            // the NULL is strange, and I have no idea why we do it here or above
 
266
            // hopefully whoever did it in the first place will comment it :P
 
267
            ConfigWizard( NULL, ConfigWizard::MediaDevice ).exec();
 
268
        }
 
269
    }
 
270
  #endif
 
271
 
 
272
  #ifdef QT_NO_DEBUG
 
273
  #ifndef LINUX
 
274
    {
 
275
        qDebug() << "Preferred application for lastfm url-scheme:" << UnicornUtils::preferredAppForUrlScheme( QUrl( "lastfm://" ) );
 
276
        qDebug() << "Our app:" << MooseUtils::bundleDirPath();
 
277
 
 
278
        bool b = ( MooseUtils::bundleDirPath() == UnicornUtils::preferredAppForUrlScheme( QUrl( "lastfm://" ) ) );
 
279
        if ( !b )
 
280
        {
 
281
            ConfirmDialog d( m_container );
 
282
            d.setText( tr( "The Last.fm Client is currently not the default player for Last.fm streams.\nDo you want to change that? *CHANEGCOPY*" ) );
 
283
            d.setOperationString( "urlhandler" );
 
284
 
 
285
            if ( d.exec() )
 
286
            {
 
287
                qDebug() << "Setting client as the default lastfm url-scheme handler worked:" << UnicornUtils::setPreferredAppForUrlScheme( QUrl( "lastfm://" ), MooseUtils::bundleDirPath() );
 
288
            }
 
289
        }
 
290
    }
 
291
  #endif
 
292
  #endif
225
293
 
226
294
    // Do we have a current user?
227
295
    QString currentUser = The::settings().currentUsername();
248
316
        }
249
317
    }
250
318
    else
 
319
    {
251
320
        setUser( currentUser );
252
 
 
253
 
    // This is needed for the app to shut down properly when asked by Windows
254
 
    // to shut down.
255
 
    //TODO move to ctor?
256
 
    connect( this, SIGNAL( endSession() ), SLOT( quit() ) );
 
321
    }
257
322
 
258
323
    if ( !arguments().contains( "-tray" ) && !arguments().contains( "--tray" ) )
259
324
        m_container->show();
327
392
    logger.Init( MooseUtils::logPath( filename ), false );
328
393
    logger.SetLevel( Logger::Debug );
329
394
 
330
 
    LOGL( 1, "App version: " << The::settings().version() );
 
395
    LOGL( 3, "App version: " << The::settings().version() );
 
396
    LOGL( 3, "OS version: " << UnicornUtils::getOSVersion() );
331
397
}
332
398
 
333
399
 
383
449
void
384
450
LastFmApplication::setUser( const QString& username )
385
451
{
 
452
    Q_ASSERT( !username.isEmpty() );
 
453
    
386
454
    The::settings().setCurrentUsername( username );
387
455
    The::webService()->setUsername( username );
388
456
    The::webService()->setPassword( The::currentUser().password() );
389
457
 
 
458
    if ( !m_extensionsLoaded )
 
459
    {
 
460
        // Can't load extensions until we have a current user
 
461
        loadExtensions();
 
462
        m_extensionsLoaded = true;
 
463
    }
 
464
 
390
465
    if ( m_user )
391
466
    {
392
467
        // we no longer care about any signals this user may emit
397
472
    m_handshaked = false;
398
473
    m_user = new User( username, this );
399
474
 
400
 
// CAREFUL: this ifndef spawns and ends in the next method (onProxyTestComplete)
401
 
// FIXME: oh please fix me
402
475
#ifndef Q_WS_X11
403
476
    m_proxyTestDone = false;
404
477
    if( !The::settings().isUseProxy() )
407
480
        ProxyTestRequest* proxyOnTest = new ProxyTestRequest( true );
408
481
        ProxyTestRequest* proxyOffTest = new ProxyTestRequest( false );
409
482
 
410
 
        connect( The::webService() , SIGNAL( proxyTestResult( bool ) ),
411
 
                 this,               SLOT( onProxyTestComplete( bool ) ) );
 
483
        disconnect( The::webService(), SIGNAL( proxyTestResult( bool, WebRequestResultCode ) ),
 
484
                    this,              SLOT( onProxyTestComplete( bool, WebRequestResultCode ) ) );
 
485
        connect( The::webService(), SIGNAL( proxyTestResult( bool, WebRequestResultCode ) ),
 
486
                this,              SLOT( onProxyTestComplete( bool, WebRequestResultCode ) ) );
412
487
 
413
488
        proxyOnTest->start();
414
489
        proxyOffTest->start();
417
492
    else
418
493
    {
419
494
        emit event( Event::UserChanged, username );
420
 
        onProxyTestComplete( false );
 
495
        onProxyTestComplete( false, Request_Success );
421
496
    }
422
 
 
423
 
    emit event( Event::UserChanged, username );
 
497
#else
 
498
    m_proxyTestDone = false;
 
499
    onProxyTestComplete( false, Request_Success  );
 
500
#endif
424
501
}
425
502
 
426
503
 
427
504
void
428
 
LastFmApplication::onProxyTestComplete( bool proxySet )
 
505
LastFmApplication::onProxyTestComplete( bool proxySet, WebRequestResultCode result, bool authProxyTimerComplete )
429
506
{
430
507
    // The only reason for this slot is to delay the handshake until we know whether
431
508
    // to use the proxy or not.
 
509
    qDebug() << "*********** ProxyTest Complete Result code = " << result;
 
510
 
432
511
    
 
512
    if( result == Request_ProxyAuthenticationRequired && !authProxyTimerComplete )
 
513
    {
 
514
        // Proxy authentication required.
 
515
        // Wait 3 seconds to see if a direct connection is available before 
 
516
        // prompting the user for login details.
 
517
        QTimer::singleShot( 3000, this, SLOT( onAuthenticatedProxyTimerTimeout() ) );
 
518
        return;
 
519
    }
 
520
 
433
521
    if ( m_proxyTestDone ) return;
434
522
    m_proxyTestDone = true;
435
 
 
 
523
    
 
524
#ifndef Q_WS_X11
436
525
    LOGL( 3, ( proxySet ? "" : "not "  ) << "using autodetected proxy settings" );
 
526
#endif
437
527
 
438
528
    // HACK: since we're in a different function, we need to set the username again
439
529
    // as it was a parameter to setUser.
440
530
    QString username = m_user->settings().username();
441
 
#else
 
531
 
442
532
    emit event( Event::UserChanged, username );
443
 
#endif
444
533
 
445
534
    QString password = m_user->settings().password();
446
535
    QString version = The::settings().version();
452
541
    m_fpQueryer->setUsername( username );
453
542
    m_fpQueryer->setPasswordMd5( password );
454
543
    m_fpQueryer->setPasswordMd5Lower( password ); // FIXME: surely they can't be the same!
 
544
    m_fpQueryer->setVersion( The::settings().version() );
455
545
 
456
546
    // init radio YTIO
457
547
    m_radio->init( username, password, version );
477
567
}
478
568
 
479
569
 
 
570
////////////////////////////////////////////////////////////////////////////
 
571
// After a timeout period this will determine if a direct connection has  // 
 
572
// been established by another proxyTestRequest. If not then              //
 
573
// authentication required dialog is shown before resuming user handshake.//
 
574
////////////////////////////////////////////////////////////////////////////
 
575
void
 
576
LastFmApplication::onAuthenticatedProxyTimerTimeout()
 
577
{
 
578
    if( !m_proxyTestDone )
 
579
    {
 
580
        LastMessageBox::information( tr( "Proxy Authentication Required" ),
 
581
            tr( "The proxy autodetection has detected a proxy server but does not have enough information to authenticate.\n\n"
 
582
            "Please set the proxy settings to manual and enter the username and password required." ) );
 
583
 
 
584
        The::container().showSettingsDialog( 3 );
 
585
        onProxyTestComplete( true, Request_ProxyAuthenticationRequired, true );
 
586
    }
 
587
}
 
588
 
 
589
 
 
590
void
 
591
LastFmApplication::loadExtensions()
 
592
{
 
593
    foreach( QString path, Moose::extensionPaths() )
 
594
    {
 
595
        LOGL( 3, "Loading extension: " << path );
 
596
        QObject* plugin = QPluginLoader( path ).instance();
 
597
        if ( !plugin )
 
598
            {
 
599
            LOGL( 1, "Failed to load " << path );
 
600
            continue;
 
601
        }
 
602
 
 
603
        LOGL( 3, "Extension loaded" );
 
604
        ExtensionInterface* iExtension = qobject_cast<ExtensionInterface *>( plugin );
 
605
        if ( iExtension )
 
606
        {
 
607
            QSettings* us = new CurrentUserSettings( this );
 
608
            iExtension->setSettings( us );
 
609
        }
 
610
    }
 
611
}
 
612
 
 
613
 
480
614
#ifdef WIN32
481
615
bool
482
616
LastFmApplication::winEventFilter( MSG * msg, long * result )
587
721
void
588
722
LastFmApplication::onScrobblePointReached( const TrackInfo& track )
589
723
{
590
 
    if ( track.playerId() == "itw" || track.playerId() == "osx" )
591
 
    {
592
 
        ItunesScrobbleHistory().append( track );
593
 
    }
594
 
 
595
724
    emit event( Event::ScrobblePointReached, QVariant::fromValue( track ) );
596
725
}
597
726
 
602
731
    if ( code == Scrobbler::Handshaken )
603
732
    {
604
733
        QString const username = data.toString();
605
 
        ScrobbleCache cache = ScrobbleCache::mediaDeviceCache( username );
606
 
        // we show a nothing to scrobble message if empty usually
607
 
        // but this is an automated check, and not user-triggered
608
 
        if ( !cache.tracks().isEmpty() )
609
 
            scrobbleMediaDeviceCache( username );
610
 
    }
611
 
}
612
 
 
613
 
 
614
 
void
615
 
LastFmApplication::scrobbleMediaDeviceCache( const QString& username )
616
 
{
617
 
    ScrobbleCache icache = ScrobbleCache::mediaDeviceCache( username );
618
 
    ScrobbleCache cache( username );
619
 
 
620
 
    if ( icache.tracks().isEmpty() )
621
 
    {
622
 
        //FIXME messages that interupt suck
623
 
        LastMessageBox::information( tr( "Nothing To Scrobble" ),
624
 
                                     tr( "Your iPod has nothing new to scrobble." ) );
625
 
        return;
626
 
    }
627
 
 
628
 
    MediaDeviceConfirmDialog dialog( username, m_container );
629
 
    if ( dialog.exec() == QDialog::Accepted )
630
 
    {
631
 
        // copy tracks playCount() times so we submit the correct number of plays
632
 
        QList<TrackInfo> tracks;
633
 
        foreach ( TrackInfo const t, dialog.tracks() )
634
 
        {
635
 
            for ( int y = 0; y < t.playCount(); ++y )
636
 
                tracks += t;
637
 
 
638
 
            // will add each track (but not each single play!) to the recently listened tracks
639
 
            emit event( Event::MediaDeviceTrackScrobbled, QVariant::fromValue( t ) );
640
 
        }
641
 
 
642
 
        cache.append( tracks );
643
 
    }
644
 
 
645
 
    //HACK since our automatic sync iPod handling blows, we monitor all
646
 
    // iTunes plays and check we aren't submitting regular iTunes plays
647
 
    QFile f( icache.path() );
648
 
    f.open( QFile::Text | QFile::ReadOnly );
649
 
    QDomDocument xml;
650
 
    xml.setContent( &f );
651
 
    QString const databaseLastModified = xml.documentElement().attribute( "lastItunesUpdate" );
652
 
    f.close();
653
 
 
654
 
    // will be empty if not an iPod mediadevice cache
655
 
    if ( !databaseLastModified.isEmpty() )
656
 
    {
657
 
        ItunesScrobbleHistory().prune( databaseLastModified.toUInt() );
658
 
    }
659
 
 
660
 
    //FIXME possible race condition since LastFmHelper also writes to this file
661
 
    QFile::remove( icache.path() );
662
 
 
663
 
    m_scrobbler->scrobble( cache );
 
734
        IPodScrobbler( username, m_container ).exec();
 
735
    }
664
736
}
665
737
 
666
738
 
667
739
void
668
740
LastFmApplication::onRadioStateChanged( RadioState newState )
669
741
{
670
 
  #ifndef HIDE_RADIO
671
 
 
672
742
    switch ( newState )
673
743
    {
674
744
        case State_Handshaken:
737
807
        default:
738
808
            Q_ASSERT( !"Undefined state case reached in onRadioStateChanged!" );
739
809
    }
740
 
 
741
 
  #endif // HIDE_RADIO
742
810
}
743
811
 
744
812
 
778
846
                        LOGL( 2, "Failed to extract MBID for: " << m_currentTrack.path() );
779
847
                }
780
848
 
781
 
                if (m_container->isVisible())
782
 
                {
 
849
                if ( m_container->isVisible() )
783
850
                    fetchMetaData();
784
 
                }
785
851
 
786
852
                if ( QFile::exists( m_currentTrack.path() ) &&
787
853
                     The::settings().currentUser().fingerprintingEnabled() )
861
927
 
862
928
            Handshake* handshake = static_cast<Handshake*>(request);
863
929
            The::user().m_isSubscriber = handshake->isSubscriber();
864
 
 
865
 
            break;
866
930
        }
 
931
        break;
867
932
 
868
933
        case TypeArtistMetaData:
869
934
        {
886
951
 
887
952
            // if track has changed before we got this metadata, don't emit the event ;)
888
953
            emit event( Event::ArtistMetaDataAvailable, QVariant::fromValue( m_currentTrack ) );
889
 
 
890
 
            break;
891
954
        }
 
955
        break;
892
956
 
893
957
        case TypeTrackMetaData:
894
958
        {
918
982
 
919
983
            // if track has changed before we got this metadata, don't emit the event ;)
920
984
            emit event( Event::TrackMetaDataAvailable, QVariant::fromValue( m_currentTrack ) );
921
 
 
922
 
            break;
923
985
        }
 
986
        break;
924
987
 
925
988
        default:
926
989
            break;
927
990
    }
928
 
 
929
 
//TODO
930
 
//    request->abort();
931
 
//    request->deleteLater();
932
991
}
933
992
 
934
993
 
952
1011
    // some other program like the firefox extension.
953
1012
 
954
1013
    QTcpSocket* socket = m_control->nextPendingConnection();
955
 
    connect( socket, SIGNAL(readyRead()), SLOT(onControlRequest()) );
 
1014
    connect( socket, SIGNAL( readyRead() ), SLOT( onControlRequest() ) );
956
1015
}
957
1016
 
958
1017
 
991
1050
 
992
1051
    if ( request.contains( "container://show" ) )
993
1052
    {
994
 
        LOGL( 3, "Calling restoreWindow" );
995
1053
        m_container->restoreWindow();
996
1054
    }
997
1055
 
998
 
    if ( request.contains( "container://checkScrobblerCache" ) )
999
 
    {
1000
 
        QString const username = request.split( "checkScrobblerCache/" ).value( 1 );
1001
 
 
1002
 
        if (m_scrobbler->canScrobble( username ))
1003
 
        {
1004
 
            scrobbleMediaDeviceCache( username );
1005
 
        }
1006
 
        else {
1007
 
            Scrobbler::Init init;
1008
 
            init.username = username;
1009
 
            init.password = The::settings().user( username ).password();
1010
 
            init.client_version = The::settings().version();
1011
 
 
1012
 
            m_scrobbler->handshake( init );
1013
 
 
1014
 
            // we will submit the mediadevice cache once the scrobbler comes back to us
1015
 
        }
1016
 
    }
1017
 
 
1018
 
    if ( request.contains( "container://addMediaDevice" ) )
1019
 
    {
1020
 
        LOGL( 3, "Calling container for media device addition" );
1021
 
        addMediaDevice( request.split( "addMediaDevice/" ).at( 1 ) );
1022
 
    }
1023
 
}
1024
 
 
1025
 
 
1026
 
/** send to the already running instance of this Application */
1027
 
bool
1028
 
LastFmApplication::sendToInstance( const QString& data ) //static
1029
 
{
1030
 
    LOGL( 3, "sendToInstance (new instance): " << data );
1031
 
 
1032
 
    QTcpSocket socket;
1033
 
    socket.connectToHost( QHostAddress::LocalHost, The::settings().controlPort() );
1034
 
 
1035
 
    if ( socket.waitForConnected( 500 ) )
1036
 
    {
1037
 
        if ( data.length() > 0 )
1038
 
        {
1039
 
            QByteArray utf8Data = data.toUtf8();
1040
 
            socket.write( utf8Data, utf8Data.length() );
1041
 
            socket.flush();
1042
 
        }
1043
 
 
1044
 
        socket.close();
1045
 
        return true;
1046
 
    }
1047
 
    else
1048
 
    {
1049
 
        LOGL( 1, "sendToInstance failed" << data );
1050
 
    }
1051
 
 
1052
 
    return false;
1053
 
}
1054
 
 
1055
 
 
1056
 
void
1057
 
LastFmApplication::addMediaDevice( const QString& uid )
1058
 
{
1059
 
    ConfigWizard cw( NULL, ConfigWizard::MediaDevice, uid );
1060
 
    if ( !cw.isWizardRunning() )
1061
 
        cw.exec();
 
1056
  #ifndef Q_WS_X11
 
1057
    if ( request.contains( "container://SubmitScrobbleCache/Device" ) )
 
1058
    {
 
1059
        QStringList const params = request.split( "container://SubmitScrobbleCache/Device/" ).value( 1 ).split( "/" );
 
1060
        int const vid = params.value( 2 ).toInt(); //vendor id
 
1061
        int const pid = params.value( 3 ).toInt(); //product id
 
1062
        QString const uid = params.value( 0 ) + '/' + params.value( 1 );
 
1063
        QString username = The::settings().usernameForDeviceId( uid );
 
1064
 
 
1065
        if ( username.isEmpty() )
 
1066
        {
 
1067
            username = The::currentUsername();
 
1068
            The::settings().addMediaDevice( uid, username );
 
1069
        }
 
1070
        
 
1071
        if ( The::currentUsername() != username )
 
1072
        {
 
1073
            LastMessageBox::warning( tr("iPod Scrobbling"),
 
1074
                                     tr("<p>This iPod is associated with a different Last.fm account."
 
1075
                                        "<p>Please log in as <b>%1</b> to submit the scrobbles."
 
1076
                                        "<p>You can change the user association in the Options dialog.").arg( username ),
 
1077
                                     QMessageBox::Ok,
 
1078
                                     QMessageBox::Ok );
 
1079
        }
 
1080
        else
 
1081
        {
 
1082
            IPodScrobbler( username, m_container ).exec();
 
1083
        }
 
1084
    }
 
1085
 
 
1086
    if ( request.startsWith( "container://Notification" ) && !ConfigWizard::isActive() )
 
1087
    {
 
1088
        QString title, message;
 
1089
        QStringList keys = request.mid( 12 ).split( '/' );
 
1090
 
 
1091
        if (keys.value( 1 ) == "Twiddly")
 
1092
        {
 
1093
            title = tr("iPod Scrobbling");
 
1094
            QString key = keys.value( 2 );
 
1095
                        
 
1096
            // ask Toby if you wonder why we don't show any of these messages
 
1097
            // by default
 
1098
            if (The::currentUser().giveMoreIPodScrobblingFeedback())
 
1099
            {
 
1100
                if ( key == "Started" )
 
1101
                    message = tr( "Your iPod scrobbles are being determined. Please don't exit iTunes." );
 
1102
                
 
1103
                if ( key == "Error" )
 
1104
                {
 
1105
                    title = tr( "iPod Scrobbling Error" );
 
1106
                    message = keys.value( 3 ).replace( '_', ' ' );
 
1107
                }
 
1108
            
 
1109
                if ( key == "Finished" )
 
1110
                {
 
1111
                    int const n = keys.value( 3 ).toInt();
 
1112
                    if (n)
 
1113
                        message = tr( "Last.fm found %n scrobbles on your iPod.", "", n );
 
1114
                    else
 
1115
                        message = tr( "No scrobbles were found on your iPod." );
 
1116
                }
 
1117
            }
 
1118
            
 
1119
            if (key == "Bootstrap")
 
1120
            {
 
1121
                key = keys.value( 3 );
 
1122
                if (key == "Started")
 
1123
                    message = tr("Preparing for iPod scrobbling, please don't exit iTunes or sync your iPod.");
 
1124
                if (key == "Finished")
 
1125
                    message = tr("Last.fm is now ready for iPod scrobbling.");
 
1126
            }
 
1127
        }
 
1128
        else
 
1129
        {
 
1130
            QString key = keys.value( 2 );
 
1131
 
 
1132
            if ( key == "IPodDetected" )
 
1133
            {
 
1134
                title = tr( "iPod detected" );
 
1135
                message = tr( "Your iPod will be scrobbled to your Last.fm profile from now on." );
 
1136
            }
 
1137
        }
 
1138
        
 
1139
        if (!message.isEmpty())
 
1140
        {
 
1141
            m_container->showNotification( title, message );
 
1142
        }
 
1143
    }
 
1144
  #endif
1062
1145
}
1063
1146
 
1064
1147
 
1065
1148
void
1066
1149
LastFmApplication::onBootstrapReady( QString userName, QString pluginId )
1067
1150
{
 
1151
    #ifndef Q_WS_X11
1068
1152
    // Ignore the bootstrap if we're not currently logged in as the user who initiated it.
1069
1153
    // (The bootstrap file will be detected when the user who initiated it logs back in.)
1070
 
    if( userName != The::currentUsername() ) return;
 
1154
    if ( userName != The::currentUsername() )
 
1155
    {
 
1156
        LastMessageBox::warning( "Bootstrap detected",
 
1157
            QString( "Bootstrap information was detected for user %1.\n"
 
1158
            "Please log in as %1 to submit this." ).arg( userName ),
 
1159
            QMessageBox::Ok,
 
1160
            QMessageBox::Ok );
 
1161
        return;
 
1162
    }
1071
1163
 
1072
1164
    PluginBootstrapper* bootstrapper = new PluginBootstrapper( pluginId, this );
1073
 
 
1074
1165
    bootstrapper->submitBootstrap();
1075
 
 
 
1166
    #endif
1076
1167
}
1077
1168
 
1078
1169
 
1083
1174
    // a metadata request in-between requesting the fp and getting it.
1084
1175
 
1085
1176
    if ( m_currentTrack.path() != track.path() )
1086
 
    {
1087
1177
        return;
1088
 
    }
1089
1178
 
1090
1179
    m_currentTrack.setFpId( track.fpId() );
1091
1180