diff --git a/data/images/new-releases.png b/data/images/new-releases.png new file mode 100644 index 000000000..05252d24e Binary files /dev/null and b/data/images/new-releases.png differ diff --git a/data/images/share.png b/data/images/share.png index c6db17530..7cd3e0023 100644 Binary files a/data/images/share.png and b/data/images/share.png differ diff --git a/lang/tomahawk_bg.ts b/lang/tomahawk_bg.ts index e36a5aa44..84556d15f 100644 --- a/lang/tomahawk_bg.ts +++ b/lang/tomahawk_bg.ts @@ -296,36 +296,41 @@ Remember: Only allow peers to connect if you trust who they are and if you have + social + социален + + + love Харесай - + Time Продължителност - + Time Left Оставащо време - + Shuffle Разбъркано - + Repeat Повтори - + Low 0 - + High 100% @@ -680,6 +685,14 @@ Remember: Only allow peers to connect if you trust who they are and if you have Създай нов списък + + NewReleasesWidget + + + New Releases + + + PlaylistItemDelegate @@ -972,7 +985,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Failed to load: %1 - Неуспех при зареждане на %1 + Не мога да заредя %1 @@ -1052,6 +1065,49 @@ Remember: Only allow peers to connect if you trust who they are and if you have Най-изпълнявани песни, които нямаш в наличност + + SocialWidget + + + Form + Бланка + + + + Facebook + Facebook + + + + Twitter + Twitter + + + + Cover + Обложка + + + + TextLabel + Етикет + + + + Listening to "%1" by %2 and loving it! %4 + <3 "%1" от %2 %4 + + + + Listening to "%1" by %2 on "%3" and loving it! %4 + <3 "%1" от %2 от "%3" %4 + + + + %1 characters left + Остават още %1 символа + + SourceDelegate @@ -1265,6 +1321,11 @@ Remember: Only allow peers to connect if you trust who they are and if you have + New Releases + + + + Friends Приятели @@ -1408,8 +1469,8 @@ Spotify e TM на Spotify Group. - Allow web browsers to interact with Tomahawk - Позволи на web браузъри да работят заедно с Tomahawk. + Allow web browsers to interact with Tomahawk (recommended) + @@ -1513,7 +1574,7 @@ Spotify e TM на Spotify Group. Success - Успех! + Ура! @@ -1532,7 +1593,7 @@ Spotify e TM на Spotify Group. Tomahawk::Accounts::TwitterAccountFactory - + Connect to your Twitter followers. Свържи се с твоите приятели в Twitter @@ -1732,12 +1793,12 @@ You may wish to try re-authenticating. &Continue Playback after this Track - + &Продължи след тази песен &Stop Playback after this Track - + &Спри след тази песен @@ -1813,7 +1874,7 @@ Please change the filters or try again. Failed to generate preview with the desired filters - Неуспех при генериране на предварителен преглед на избраните филтри. + Не мога да генерирам предварителен преглед на избраните филтри. @@ -2123,7 +2184,7 @@ Try tweaking the filters for a new set of songs to play. about %n minute(s) long - около %n минути + около %n минутаоколо %n минути @@ -2166,7 +2227,7 @@ Try tweaking the filters for a new set of songs to play. Steer this station: - Задръж тази станция: + Настройки: @@ -2186,7 +2247,7 @@ Try tweaking the filters for a new set of songs to play. Keep at current - Задръж + Запази @@ -2270,22 +2331,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall Най-известни - + Artists Артисти - + Albums Албуми - + Tracks Песни @@ -2293,44 +2354,60 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + Tomahawk възпроизвежда "%1" от %2%3 - + on "%1" - + от "%1" Tomahawk::InfoSystem::LastFmPlugin - + Top Tracks Най-актуални песни - + Loved Tracks Харесвани песни - + Hyped Tracks Песни слушани най-често - + Top Artists Най-слушани артисти - + Hyped Artists Артисти слушани най-често + + Tomahawk::InfoSystem::NewReleasesPlugin + + + Albums + + + + + Tomahawk::InfoSystem::TwitterInfoPlugin + + + Listening to "%1" by %2 and loving it! %3 + <3 "%1" от %2 %3 + + Tomahawk::ItunesParser @@ -2349,7 +2426,7 @@ Try tweaking the filters for a new set of songs to play. Failed to save tracks - Неуспех при запаметяване на песни + Не мога да запазя избраните песни @@ -2480,7 +2557,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моята колекция @@ -2521,12 +2598,12 @@ enter the displayed PIN number here: Play - + Изпълни Pause - + Пауза @@ -2534,7 +2611,7 @@ enter the displayed PIN number here: Tomahawk - + Tomahawk @@ -2639,7 +2716,7 @@ enter the displayed PIN number here: Ctrl+M - + Ctrl+M @@ -2649,7 +2726,7 @@ enter the displayed PIN number here: Meta+Ctrl+Z - + Mod+Ctrl+Z @@ -2675,7 +2752,7 @@ enter the displayed PIN number here: Space - + Интервал @@ -2733,7 +2810,7 @@ enter the displayed PIN number here: Failed to save tracks - Неуспех при запазване на списък с песни + Не мога да запазя списъкът с песни @@ -3064,7 +3141,7 @@ enter the displayed PIN number here: Status: No saved credentials - + Статус: Няма запазени данни за вход @@ -3126,17 +3203,17 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + SuperCollection Супер колекция - + Combined libraries of all your online friends Комбинирани библиотеки от всичките ми приятели на линия - + All available albums Всички налични албуми @@ -3405,107 +3482,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Действие от потребителя - + Host is unknown Непознат адрес - + Item not found Обектът не е открит - + Authorization Error Грешка при даване на достъп - + Remote Stream Error Грешка в стриймът от отдалечената машина - + Remote Connection failed Отдалечената връзка е неуспешна - + Internal Server Error Вътрешна грешка на сървъра - + System shutdown Изключване на системата - + Conflict Конфликт - + Unknown - + No Compression Support Няма поддръжка на компресия - + No Encryption Support Няма поддръжка на криптиране - + No Authorization Support Няма поддръжка на удостоверяване - + No Supported Feature - + Неподдържана функция - + Add Friend Добави приятел - + Enter Xmpp ID: Въведи Xmpp ID: - + Add Friend... Добави приятел... - + XML Console... XML Конзола... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! Извинявай.. Аз съм режимът за автоматични отговори изпълзван от Tomahawk. ( http://gettomahawk.com ) Щом получаваш това съобщение, този с който се опитваш да се свържеш вероятно не е на линия. Моля, опитай отново по-късно. - + Authorize User Оправомощяване на потребител - + Do you want to grant <b>%1</b> access to your Collection? Искате ли да позволите на <b>%1</b> достъп до вашата колекция? diff --git a/lang/tomahawk_de.ts b/lang/tomahawk_de.ts index ef267e174..0fad17e4e 100644 --- a/lang/tomahawk_de.ts +++ b/lang/tomahawk_de.ts @@ -296,36 +296,41 @@ Denk dran: Erlaube das nur, wenn du dem Anderen vertraust und du die Rechte zum + social + + + + love Lieben - + Time Zeit - + Time Left Zeit verbleibend - + Shuffle Zufall - + Repeat Wiederholen - + Low Niedrig - + High Hoch @@ -679,6 +684,14 @@ Denk dran: Erlaube das nur, wenn du dem Anderen vertraust und du die Rechte zum Erstelle eine neue Playliste + + NewReleasesWidget + + + New Releases + + + PlaylistItemDelegate @@ -1051,6 +1064,49 @@ Denk dran: Erlaube das nur, wenn du dem Anderen vertraust und du die Rechte zum Meist gehörte Lieder die du nicht kennst + + SocialWidget + + + Form + + + + + Facebook + + + + + Twitter + + + + + Cover + + + + + TextLabel + + + + + Listening to "%1" by %2 and loving it! %4 + + + + + Listening to "%1" by %2 on "%3" and loving it! %4 + + + + + %1 characters left + + + SourceDelegate @@ -1264,6 +1320,11 @@ Denk dran: Erlaube das nur, wenn du dem Anderen vertraust und du die Rechte zum + New Releases + + + + Friends Freunde @@ -1403,8 +1464,8 @@ Denk dran: Erlaube das nur, wenn du dem Anderen vertraust und du die Rechte zum - Allow web browsers to interact with Tomahawk - Erlaube Webbrowsern mit Tomahawk zu interagieren + Allow web browsers to interact with Tomahawk (recommended) + @@ -1527,7 +1588,7 @@ Denk dran: Erlaube das nur, wenn du dem Anderen vertraust und du die Rechte zum Tomahawk::Accounts::TwitterAccountFactory - + Connect to your Twitter followers. Verbinde dich zu deinen Twitter-Followern @@ -2261,22 +2322,22 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall Top Allgemein - + Artists Künstler - + Albums Alben - + Tracks Stücke @@ -2284,12 +2345,12 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + on "%1" @@ -2297,31 +2358,47 @@ Versuch die Filter anzupassen für neue Lieder. Tomahawk::InfoSystem::LastFmPlugin - + Top Tracks Größte Hits - + Loved Tracks Lieblings Lieder - + Hyped Tracks Angesagte Stücke - + Top Artists - + Hyped Artists + + Tomahawk::InfoSystem::NewReleasesPlugin + + + Albums + + + + + Tomahawk::InfoSystem::TwitterInfoPlugin + + + Listening to "%1" by %2 and loving it! %3 + + + Tomahawk::ItunesParser @@ -2471,7 +2548,7 @@ Versuch die Filter anzupassen für neue Lieder. TomahawkApp - + My Collection Meine Sammlung @@ -3108,17 +3185,17 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + SuperCollection - + Combined libraries of all your online friends - + All available albums Alle verfügbaren Alben @@ -3383,107 +3460,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + No Compression Support - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend Freund hinzufügen... - + Enter Xmpp ID: XMPP-Benutzer: - + Add Friend... Freund hinzufügen... - + XML Console... XML-Konsole... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User - + Do you want to grant <b>%1</b> access to your Collection? Willst du <b>%1</b> Zugriff auf deine Sammlung gewähren? diff --git a/lang/tomahawk_en.ts b/lang/tomahawk_en.ts index f84149a1c..3eaf7eaea 100644 --- a/lang/tomahawk_en.ts +++ b/lang/tomahawk_en.ts @@ -296,36 +296,41 @@ Remember: Only allow peers to connect if you trust who they are and if you have + social + social + + + love love - + Time Time - + Time Left Time Left - + Shuffle Shuffle - + Repeat Repeat - + Low Low - + High High @@ -679,6 +684,14 @@ Remember: Only allow peers to connect if you trust who they are and if you have Create a new playlist + + NewReleasesWidget + + + New Releases + New Releases + + PlaylistItemDelegate @@ -1052,6 +1065,49 @@ Remember: Only allow peers to connect if you trust who they are and if you have Most Played Tracks You Don't Have + + SocialWidget + + + Form + Form + + + + Facebook + Facebook + + + + Twitter + Twitter + + + + Cover + Cover + + + + TextLabel + TextLabel + + + + Listening to "%1" by %2 and loving it! %4 + Listening to "%1" by %2 and loving it! %4 + + + + Listening to "%1" by %2 on "%3" and loving it! %4 + Listening to "%1" by %2 on "%3" and loving it! %4 + + + + %1 characters left + %1 characters left + + SourceDelegate @@ -1265,6 +1321,11 @@ Remember: Only allow peers to connect if you trust who they are and if you have + New Releases + New Releases + + + Friends Friends @@ -1407,8 +1468,8 @@ Remember: Only allow peers to connect if you trust who they are and if you have - Allow web browsers to interact with Tomahawk - Allow web browsers to interact with Tomahawk + Allow web browsers to interact with Tomahawk (recommended) + Allow web browsers to interact with Tomahawk (recommended) @@ -1531,7 +1592,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Tomahawk::Accounts::TwitterAccountFactory - + Connect to your Twitter followers. Connect to your Twitter followers. @@ -2268,22 +2329,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall Top Overall - + Artists Artists - + Albums Albums - + Tracks Tracks @@ -2291,12 +2352,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. Tomahawk is playing "%1" by %2%3. - + on "%1" on "%1" @@ -2304,31 +2365,47 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmPlugin - + Top Tracks Top Tracks - + Loved Tracks Loved Tracks - + Hyped Tracks Hyped Tracks - + Top Artists Top Artists - + Hyped Artists Hyped Artists + + Tomahawk::InfoSystem::NewReleasesPlugin + + + Albums + Albums + + + + Tomahawk::InfoSystem::TwitterInfoPlugin + + + Listening to "%1" by %2 and loving it! %3 + Listening to "%1" by %2 and loving it! %3 + + Tomahawk::ItunesParser @@ -2478,7 +2555,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection My Collection @@ -3120,17 +3197,17 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + SuperCollection SuperCollection - + Combined libraries of all your online friends Combined libraries of all your online friends - + All available albums All available albums @@ -3405,107 +3482,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction User Interaction - + Host is unknown Host is unknown - + Item not found Item not found - + Authorization Error Authorization Error - + Remote Stream Error Remote Stream Error - + Remote Connection failed Remote Connection failed - + Internal Server Error Internal Server Error - + System shutdown System shutdown - + Conflict Conflict - + Unknown Unknown - + No Compression Support No Compression Support - + No Encryption Support No Encryption Support - + No Authorization Support No Authorization Support - + No Supported Feature No Supported Feature - + Add Friend Add Friend - + Enter Xmpp ID: Enter Xmpp ID: - + Add Friend... Add Friend... - + XML Console... XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User Authorize User - + Do you want to grant <b>%1</b> access to your Collection? Do you want to grant <b>%1</b> access to your Collection? diff --git a/lang/tomahawk_es.ts b/lang/tomahawk_es.ts index 74aa6b86b..bf8599e69 100644 --- a/lang/tomahawk_es.ts +++ b/lang/tomahawk_es.ts @@ -294,36 +294,41 @@ Remember: Only allow peers to connect if you trust who they are and if you have + social + + + + love favorita - + Time Duración - + Time Left Tiempo restante - + Shuffle Aleatorio - + Repeat Repetir - + Low Bajar volumen - + High Subir volumen @@ -677,6 +682,14 @@ Remember: Only allow peers to connect if you trust who they are and if you have Crear una nueva lista de reproducción + + NewReleasesWidget + + + New Releases + + + PlaylistItemDelegate @@ -1049,6 +1062,49 @@ Remember: Only allow peers to connect if you trust who they are and if you have Pistas más reproducidas no disponibles + + SocialWidget + + + Form + + + + + Facebook + + + + + Twitter + + + + + Cover + + + + + TextLabel + + + + + Listening to "%1" by %2 and loving it! %4 + + + + + Listening to "%1" by %2 on "%3" and loving it! %4 + + + + + %1 characters left + + + SourceDelegate @@ -1262,6 +1318,11 @@ Remember: Only allow peers to connect if you trust who they are and if you have + New Releases + + + + Friends @@ -1404,7 +1465,7 @@ y estaciones basadas en sus gustos personales. - Allow web browsers to interact with Tomahawk + Allow web browsers to interact with Tomahawk (recommended) @@ -1528,7 +1589,7 @@ y estaciones basadas en sus gustos personales. Tomahawk::Accounts::TwitterAccountFactory - + Connect to your Twitter followers. @@ -2260,22 +2321,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall Top total - + Artists Artistas - + Albums Álbumes - + Tracks Pistas @@ -2283,12 +2344,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + on "%1" @@ -2296,31 +2357,47 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmPlugin - + Top Tracks Pistas más escuchadas - + Loved Tracks Pistas favoritas - + Hyped Tracks Pistas en alza - + Top Artists Artistas más escuchados - + Hyped Artists Artistas en alza + + Tomahawk::InfoSystem::NewReleasesPlugin + + + Albums + + + + + Tomahawk::InfoSystem::TwitterInfoPlugin + + + Listening to "%1" by %2 and loving it! %3 + + + Tomahawk::ItunesParser @@ -2470,7 +2547,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Mi colección @@ -3112,17 +3189,17 @@ Puede reenviar el mensaje de sincronización en cualquier momento simplemente en ViewManager - + SuperCollection - + Combined libraries of all your online friends - + All available albums Todos los álbumes disponibles @@ -3396,107 +3473,107 @@ Letras de "%1" por %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + No Compression Support - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User - + Do you want to grant <b>%1</b> access to your Collection? diff --git a/lang/tomahawk_fr.ts b/lang/tomahawk_fr.ts new file mode 100644 index 000000000..2912e70ee --- /dev/null +++ b/lang/tomahawk_fr.ts @@ -0,0 +1,3591 @@ + + + ACLRegistry + + + Connect to Peer? + Se connecter ? + + + + Another Tomahawk instance that claims to be owned by %1 is attempting to connect to you. Select whether to allow or deny this connection. + +Remember: Only allow peers to connect if you trust who they are and if you have the legal right for them to stream music from you. + Une autre instance de Tomahawk qui dit appartenir à %1 essaie de se connecter. Voulez vous accepter ou interdire cette connexion ? + +Note : N'autorisez à se connecter que les personnes en qui vous avez confiance et qui ont le droit de streamer votre musique. + + + + Deny + Interdire + + + + Allow + Accepter + + + + AccountFactoryWrapper + + + Dialog + Dialog + + + + Description goes here + ici la description + + + + Add Account + Ajouter un compte + + + + AccountFactoryWrapperDelegate + + + Online + En Ligne + + + + Connecting... + Connexion en cours... + + + + Offline + Hors ligne + + + + ActionCollection + + + &Listen Along + &Ecouter avec + + + + Stop &Listening Along + Arrêter d'&écouter avec + + + + &Follow in real-time + &Suivre en temps réel + + + + + &Listen Privately + &Ecouter en privé + + + + + &Listen Publicly + &Ecouter publiquement + + + + &Load Playlist + &Charger une liste de lecture + + + + &Rename Playlist + &Renommer la liste de lecture + + + + &Copy Playlist Link + &Copier le lien de la piste de lecture + + + + &Play + &Lire + + + + &Stop + &Stop + + + + &Previous Track + Piste &Précédente + + + + &Next Track + Piste &Suivante + + + + &Quit + &Quitter + + + + AlbumInfoWidget + + + Form + Form + + + + Other Albums by Artist + Tous les albums par cet artiste + + + + + Click to show Official Tracks + Cliquer pour afficher les pistes officielles + + + + + Click to show SuperCollection Tracks + Cliquer pour afficher les pistes de la SuperCollectio, + + + + + Click to show SuperCollection Albums + Cliquer pour afficher les albums de la SuperCollection + + + + Click to show Official Albums + Cliquer pour afficher les albums officiels + + + + Other Albums by %1 + Autres albums par %1 + + + + AlbumModel + + + Album + Album + + + + + All albums from %1 + Tous les albums de %1 + + + + All albums + Tous les albums + + + + AlbumView + + + After you have scanned your music collection you will find your latest album additions right here. + Après avoir scanné votre collection musicale, vous trouverez les derniers albums ajoutés ici. + + + + This collection doesn't have any recent albums. + Cette collection n'a pas d'albums récents + + + + ArtistInfoWidget + + + Form + Form + + + + Top Hits + Top Hits + + + + Related Artists + Artistes similaires + + + + Albums + Albums + + + + + Click to show SuperCollection Albums + Cliquer pour afficher les albums de la SuperCollection + + + + Click to show Official Albums + Cliquer pour afficher les albums officiels + + + + ArtistView + + + After you have scanned your music collection you will find your tracks right here. + Après avoir scanné votre collection musicale, vous trouverez vos pistes ici. + + + + This collection is currently empty. + La collection est vide actuellement. + + + + Sorry, your filter '%1' did not match any results. + Désolé, votre filtre '%1' ne correspond à aucun résultat + + + + AudioControls + + + Prev + Précédent + + + + Play + Lecture + + + + Pause + Pause + + + + Next + Suivant + + + + Artist + Artiste + + + + Album + Album + + + + Owner + Propriétaire + + + + social + + + + + love + love + + + + Time + Durée + + + + Time Left + Durée restante + + + + Shuffle + Lecture Aléatoire + + + + Repeat + Répéter + + + + Low + Bas + + + + High + Haut + + + + CategoryAddItem + + + + New Playlist + Nouvelle liste de lecture + + + + + + + New Station + Nouvelle Station + + + + + + %1 Station + Station %1 + + + + CategoryItem + + + Playlists + Listes de lecture + + + + Stations + Stations + + + + ClearButton + + + Clear + Vider + + + + CollectionFlatModel + + + My Collection + Ma Collection + + + + Collection of %1 + Collection de %1 + + + + CollectionView + + + This collection is empty. + La collection est vide. + + + + ContextWidget + + + InfoBar + Barre d'information + + + + + Show Footnotes + Afficher les notes + + + + Hide Footnotes + Masquer les notes + + + + CrashReporter + + + Tomahawk Crash Reporter + Tomahawk Crash Reporter + + + + <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> + <p><b>Désolé !</b>&nbsp;Tomahawk a planté. Les informations du plantage sont maintenant envoyés au siège de Tomahawk pour que nous puissions corriger le bug.</p> + + + + Abort + Abandonner + + + + You can disable sending crash reports in the configuration dialog. + Vous pouvez désactiver l'envoi des rapports de plantage dans la boite de dialogue de configuration. + + + + Uploaded %L1 of %L2 KB. + Chargement %L1 de %L2 ko. + + + + + Close + Fermer + + + + Sent! <b>Many thanks</b>. + Envoyé ! <b>Merci beaucoup</b>. + + + + Failed to send crash info. + Échec de l'envoi des informations de plantage + + + + DatabaseCommand_AllAlbums + + + Unknown + Inconnu + + + + DelegateConfigWrapper + + + Delete Account + Supprimer le compte + + + + DiagnosticsDialog + + + Tomahawk Diagnostics + Diagnostics de Tomahawk + + + + Update + Mettre à jour + + + + Copy to Clipboard + Copier dans le presse papier + + + + DropJob + + + No tracks found for given %1 + Aucune piste trouvée pour %1 + + + + GlobalSearchWidget + + + Form + Form + + + + IndexingJobItem + + + Indexing database + Indexation de la base de données + + + + InfoBar + + + InfoBar + Barre d'information + + + + Automatically update + + + + + Filter... + Filtre... + + + + JobStatusView + + + Searching For + Recherche de + + + + Pending + + + + + Idle + + + + + LastFmConfig + + + Form + Form + + + + Scrobble tracks to Last.fm + Scrobbler les pistes sur Last.fm + + + + Username: + Nom d'utilisateur : + + + + Password: + Mot de passe : + + + + Test Login + + + + + LastfmContext + + + Last.fm + Last.fm + + + + LatchedStatusItem + + + %1 is listening along to you! + %1 écoute avec vous + + + + LoadXSPF + + + Load XSPF + Charger XSPF + + + + Playlist URL + URL de la liste de lecture + + + + Enter URL... + Saisir une URL... + + + + ... + ... + + + + Automatically update + + + + + LoadXSPFDialog + + + Load XSPF File + Charger un fichier XSPF + + + + XSPF Files (*.xspf) + Fichiers XSPF (*.xspf) + + + + LocalCollection + + + Bookmarks + + + + + Saved tracks + Pistes sauvegardés + + + + NewPlaylistWidget + + + Enter a title for the new playlist: + Entrer un titre pour la nouvelle liste de lecture + + + + Tomahawk offers a variety of ways to help you create playlists and find music you enjoy! + Tomahawk offre plusieurs façons de créer des listes de lecture et de trouver la musique que vous aimerez ! + + + + Just enter a genre or tag name and Tomahawk will suggest a few songs to get you started with your new playlist: + Entrez un genre ou un tag et Tomahawk vous suggérera quelques morceaux pour commencer votre nouvelle liste de lecture : + + + + &Create Playlist + &Créer une liste de lecture + + + + Create a new playlist + Créer une nouvelle liste de lecture + + + + NewReleasesWidget + + + New Releases + + + + + PlaylistItemDelegate + + + played %1 by you + joué %1 par vous + + + + played %1 by %2 + joué %1 par %2 + + + + PlaylistLargeItemDelegate + + + played %1 by you + joué %1 par vous + + + + played %1 by %2 + joué %1 par %2 + + + + added %1 + + + + + PlaylistModel + + + A playlist by %1, created %2 + Une liste de lecture par %1, créée %2 + + + + you + vous + + + + All tracks by %1 on album %2 + Toutes les pistes par %1 sur l'album %2 + + + + All tracks by %1 + Toutes les pistes par %1 + + + + PlaylistTypeSelectorDlg + + + New Playlist + Nouvelle liste de lecture + + + + Just a regular old playlist... Give it a name, drag in some tracks, and go! + Juste une liste de lecture normale... Donnez lui un nom, faites glisser quelques pistes et c'est parti ! + + + + Don't know exactly what you want? Give Tomahawk a few pointers and let it build a playlist for you! + Vous ne savez pas ce que vous voulez exactement ? Donnez quelques idées à Tomahawk et laissez le créer une liste de lecture pour vous. + + + + Name: + Nom : + + + + New Playlist... + Nouvelle liste de lecture... + + + + Create Manual Playlist + Créer une liste de lecture manuellement + + + + Create Automatic Playlist + Créer une liste de lecture automatiquement + + + + PlaylistView + + + This playlist is currently empty. Add some tracks to it and enjoy the music! + La liste de lecture est vide. Ajoutez des pistes et profitez de la musique ! + + + + ProxyDialog + + + Proxy Settings + Paramètres de proxy + + + + Hostname of proxy server + Nom d'hôte du serveur proxy + + + + Host + Hôte + + + + Port + Port + + + + Proxy login + Login du proxy + + + + User + Utilisateur + + + + Password + Mot de passe + + + + Proxy password + Mot de passe du proxy + + + + Type + Type + + + + No Proxy Hosts: +(Overrides system proxy) + Pas de proxy pour : +(remplace les paramètres système) + + + + localhost *.example.com (space separated) + localhost *.example.com (séparé par espace) + + + + Use proxy for DNS lookups? + Utiliser le proxy pour les requêtes DNS ? + + + + QObject + + + %n year(s) ago + il y a %n anil y a %n ans + + + + %n year(s) + %n an%n ans + + + + %n month(s) ago + il y a %n moisil y a %n mois + + + + %n month(s) + %n mois%n mois + + + + %n week(s) ago + il y a %n semaineil y a %n semaines + + + + %n week(s) + %n semaine%n semaines + + + + %n day(s) ago + il y a %n jouril y a %n jours + + + + %n day(s) + %n jour%n jours + + + + %n hour(s) ago + il y a %n heureil y a %n heures + + + + %n hour(s) + %n heure%n heures + + + + %1 minutes ago + il y a %1 minutes + + + + %1 minutes + %1 minutes + + + + just now + à l'instant + + + + Friend Finders + + + + + Music Finders + + + + + Status Updaters + + + + + QuaZipFilePrivate + + + ZIP/UNZIP API error %1 + + + + + QueueView + + + InfoBar + Barre d'information + + + + + Show Queue + + + + + Hide Queue + + + + + RelatedArtistsContext + + + Related Artists + + + + + ResolverConfigDelegate + + + Not found: %1 + + + + + Failed to load: %1 + + + + + SearchLineEdit + + + Search + + + + + SearchWidget + + + Search: %1 + + + + + Results for '%1' + + + + + SettingsDialog + + + Collection + Collection + + + + Advanced + Avancés + + + + All + + + + + Services + + + + + Install resolver from file + + + + + Information + + + + + Changing this setting requires a restart of Tomahawk! + + + + + SocialPlaylistWidget + + + Popular New Albums From Your Friends + Nouveaux Albums Populaires de vos Amis + + + + Most Played Playlists + Liste de lecture les plus jouées + + + + Most Played Tracks You Don't Have + Pistes les plus joués que vous n'avez pas + + + + SocialWidget + + + Form + Form + + + + Facebook + Facebook + + + + Twitter + Twitter + + + + Cover + + + + + TextLabel + TextLabel + + + + Listening to "%1" by %2 and loving it! %4 + J'écoute "%1" par %2 et j'adore ! %4 + + + + Listening to "%1" by %2 on "%3" and loving it! %4 + J'écoute "%1" par %2 sur "%3" et j'adore ! %4 + + + + %1 characters left + %1 caractères restants + + + + SourceDelegate + + + Track + Piste + + + + Album + Album + + + + Artist + Artiste + + + + Local + Local + + + + Top 10 + Top 10 + + + + Offline + Hors ligne + + + + All available tracks + Toutes les pistes disponibles + + + + Online + En Ligne + + + + + Show + Afficher + + + + + Hide + Masquer + + + + SourceInfoWidget + + + Recent Albums + Derniers Albums + + + + Latest Additions + Derniers ajouts + + + + Recently Played Tracks + Dernières pistes jouées + + + + New Additions + Nouveaux ajouts + + + + My recent activity + Mon activité récente + + + + Recent activity from %1 + Activité récente de %1 + + + + SourceItem + + + Collection + Collection + + + + Latest Additions + Derniers ajouts + + + + Recently Played + Joués récemment + + + + Loved Tracks + + + + + SuperCollection + SuperCollection + + + + SourceTreeView + + + &Copy Link + &Copier le lien + + + + &Delete %1 + &Supprimer %1 + + + + &Export Playlist + &Exporter la liste de lecture + + + + Save XSPF + Enregistrer XSPF + + + + Playlists (*.xspf) + Listes de lecture (*.xspf) + + + + SourcesModel + + + Group + Groupe + + + + Collection + Collection + + + + Playlist + Liste de lecture + + + + Automatic Playlist + Liste de lecture automatique + + + + Station + Station + + + + Browse + Parcourir + + + + Search History + + + + + My Music + Ma Musique + + + + SuperCollection + SuperCollection + + + + Top Loved Tracks + + + + + Dashboard + Tableau de bord + + + + Recently Played + Joués récemment + + + + Charts + + + + + New Releases + + + + + Friends + Amis + + + + SpotifyConfig + + + Form + + + + + Configure your Spotify credentials + + + + + Username: + Nom d'utilisateur : + + + + placeholderUsername + placeholderUsername + + + + Password: + + + + + placeholderPw + + + + + High Quality Streaming + + + + + This product uses SPOTIFY(R) CORE but is not endorsed, certified or otherwise approved in any way by Spotify. Spotify is the registered trade mark of the Spotify Group. + + + + + StackedSettingsDialog + + + Tomahawk Settings + + + + + Local Music Information + + + + + Path to scan for music files: + Chemin à scanner pour des fichiers musicaux : + + + + The Echo Nest supports keeping track of your catalog metadata + and using it to craft personalized radios. Enabling this option + will allow you (and all your friends) to create automatic playlists + and stations based on your personal taste profile. + The Echo Nest peut garder les métadonnées de votre catalogue +et l'utiliser pour créer des radios personnalisées. En activant cette option +vous (et vos amis) pourrez créer des listes de lecture automatiquement +et des stations basés sur vos goûts. + + + + Upload collection list to The Echo Nest to enable user radio + Envoyer la collection à The Echo Nest pour activer la radio utilisateur + + + + Watch for changes + + + + + Time between scans, in seconds: + Période de scan, en secondes : + + + + Internet Services + + + + + Install from file... + + + + + Filter by capability: + + + + + Advanced Network Settings + Paramètres réseaux avancés + + + + If you're having difficulty connecting to peers, try setting this to your external IP address/host name and a port number (default 50210). Make sure to forward that port to this machine! + Si vous rencontrez des difficultés à vous connecter, essayez de saisir ici votre adresse IP externe et un port (défaut 50210). Vérifiez que vous redirigez ce port vers cette machine ! + + + + Static Host Name: + Nom d'hôte statique : + + + + Static Port: + + + + + Always use static host name/port? (Overrides UPnP discovery/port forwarding) + Toujours utiliser un nom d'hôte/port statique? (remplace la découverte UPnP/le transfer de port) + + + + Proxy Settings... + + + + + Send reports after Tomahawk crashed + + + + + Allow web browsers to interact with Tomahawk (recommended) + + + + + Use UPnP to establish port forward + + + + + Tomahawk::Accounts::AccountDelegate + + + Add Account + Ajouter un compte + + + + Remove Account + Supprimer le compte + + + + %1 downloads + + + + + Online + En Ligne + + + + Connecting... + Connexion en cours... + + + + Offline + Hors ligne + + + + Tomahawk::Accounts::GoogleWrapper + + + Configure this Google Account + Configurer le compte Google + + + + Google Address + Adresse Google + + + + Enter your Google login to connect with your friends using Tomahawk! + Entrer votre login Google pour vous connecter avec vos amis qui utilisent Tomahawk ! + + + + username@gmail.com + utilisateur@gmail.com + + + + Tomahawk::Accounts::GoogleWrapperFactory + + + Connect to Google Talk to find your friends + Connectez vous à Google Talk pour trouver vos amis + + + + Tomahawk::Accounts::GoogleWrapperSip + + + Add Friend + + + + + Enter Google Address: + + + + + Tomahawk::Accounts::LastFmAccountFactory + + + Scrobble your tracks to last.fm, and find freely downloadable tracks to play + Scrobbler vos écoutes sur Last.fm et trouver des morceaux téléchargeables gratuitement + + + + Tomahawk::Accounts::LastFmConfig + + + + Failed + Échec + + + + Success + Succès + + + + Could not contact server + Impossible de contacter le serveur + + + + Tomahawk::Accounts::SpotifyAccountFactory + + + Play music from and sync your playlists with Spotify Premium + Jouer la musique et synchroniser vos listes avec Spotify Premium + + + + Tomahawk::Accounts::TwitterAccountFactory + + + Connect to your Twitter followers. + + + + + Tomahawk::Accounts::TwitterConfigWidget + + + + + Tweet! + Tweet! + + + + + Status: No saved credentials + + + + + + + Authenticate + + + + + + Status: Credentials saved for %1 + + + + + + De-authenticate + + + + + + + + + + + Tweetin' Error + + + + + The credentials could not be verified. +You may wish to try re-authenticating. + + + + + Status: Error validating credentials + + + + + Global Tweet + Tweet Global + + + + Direct Message + + + + + Send Message! + + + + + @Mention + + + + + Send Mention! + + + + + You must enter a user name for this type of tweet. + Vous devez saisir un nom d'utilisateur pour ce type de tweet. + + + + Your saved credentials could not be loaded. +You may wish to try re-authenticating. + + + + + Your saved credentials could not be verified. +You may wish to try re-authenticating. + + + + + + There was an error posting your status -- sorry! + + + + + + Tweeted! + + + + + Your tweet has been posted! + + + + + There was an error posting your direct message -- sorry! + + + + + Your message has been posted! + + + + + Tomahawk::Accounts::XmppAccountFactory + + + Log on to your Jabber/XMPP account to connect to your friends + Connectez vous à votre compte Jabber/XMPP pour vous connecter avec vos amis + + + + Tomahawk::Accounts::ZeroconfFactory + + + Automatically connect to Tomahawks on the local network + Se connecter automatiquement aux Tomahawks sur le réseau local + + + + Tomahawk::ContextMenu + + + &Play + + + + + + + Add to &Queue + + + + + + &Love + + + + + &Copy Track Link + &Copier le lien de la piste + + + + Show &Album page + Afficher la page de l'&album + + + + Show &Artist page + + + + + Un-&Love + + + + + &Delete Items + + + + + &Continue Playback after this Track + &Continuer la lecture après cette piste + + + + &Stop Playback after this Track + &Stopper la lecture après cette piste + + + + &Delete Item + + + + + Tomahawk::CustomPlaylistView + + + Top Loved Tracks + + + + + Your loved tracks + + + + + %1's loved tracks + + + + + The most loved tracks from all your friends + + + + + All of your loved tracks + + + + + All of %1's loved tracks + + + + + Tomahawk::DropJobNotifier + + + Fetching %1 from database + + + + + Parsing %1 %2 + + + + + Tomahawk::DynamicControlList + + + Click to collapse + + + + + Tomahawk::DynamicModel + + + + Could not find a playable track. + +Please change the filters or try again. + + + + + Failed to generate preview with the desired filters + Échec de la génération de l'aperçu avec ces filtres + + + + Tomahawk::DynamicSetupWidget + + + Type: + + + + + Generate + Générer + + + + Tomahawk::DynamicView + + + Add some filters above to seed this station! + Ajoutez des filtres ci dessus pour commencer une station ! + + + + Press Generate to get started! + Appuyez sur Générer pour commencer ! + + + + Add some filters above, and press Generate to get started! + Ajoutez des filtres ci dessus et appuyez sur "Générer" pour commencer ! + + + + Tomahawk::DynamicWidget + + + Station ran out of tracks! + +Try tweaking the filters for a new set of songs to play. + + + + + Tomahawk::EchonestControl + + + + + + + + is + + + + + from user + + + + + + No users with Echo Nest Catalogs enabled. Try enabling option in Collection settings + Aucun utilisateur avec un Catalogue The Echo Nest actif. Essayez d'activer l'option dans les préférences de Collection. + + + + similar to + + + + + + + + + + + Less + Moins + + + + + + + + + + More + Plus + + + + 0 BPM + 0 BPM + + + + 500 BPM + 500 BPM + + + + 0 secs + 0 secs + + + + 3600 secs + 3600 secs + + + + -100 dB + -100 dB + + + + 100 dB + 100 dB + + + + Major + Majeur + + + + Minor + Mineur + + + + C + Do + + + + C Sharp + Do dièse + + + + D + + + + + E Flat + Mi bémol + + + + E + Mi + + + + F + Fa + + + + F Sharp + Fa dièse + + + + G + Sol + + + + A Flat + La bémol + + + + A + La + + + + B Flat + Si bémol + + + + B + Si + + + + Ascending + Croissant + + + + Descending + Décroissant + + + + Tempo + Tempo + + + + Duration + Durée + + + + Loudness + Intensité + + + + Artist Familiarity + + + + + Artist Hotttnesss + + + + + Song Hotttnesss + + + + + Latitude + Latitude + + + + Longitude + Longitude + + + + Mode + Mode + + + + Key + Tonalité + + + + Energy + Energie + + + + Danceability + Dansabilité + + + + only by ~%1 + + + + + similar to ~%1 + + + + + with genre ~%1 + + + + + + from no one + + + + + My Collection + Ma Collection + + + + from %1 radio + + + + + with %1 %2 + + + + + about %1 BPM + environ %1 BPM + + + + about %n minute(s) long + environ %n minuteenviron %n minutes + + + + about %1 dB + environ %1 dB + + + + at around %1%2 %3 + + + + + in %1 + + + + + in a %1 key + + + + + sorted in %1 %2 order + + + + + with a %1 mood + + + + + in a %1 style + + + + + Tomahawk::EchonestSteerer + + + Steer this station: + + + + + Much less + Beaucoup moins + + + + Less + Moins + + + + A bit less + Un peu moins + + + + Keep at current + Garder le même + + + + A bit more + Un peu plus + + + + More + Plus + + + + Much more + Beaucoup plus + + + + Tempo + Tempo + + + + Loudness + Intensité + + + + Danceability + Dansabilité + + + + Energy + Energie + + + + Song Hotttnesss + Hotttnesss du morceau + + + + Artist Hotttnesss + Hotttnesss de l'artiste + + + + Artist Familiarity + + + + + By Description + Par description + + + + Enter a description + Entrer une description + + + + Apply steering command + + + + + Reset all steering commands + + + + + Tomahawk::GroovesharkParser + + + Error fetching Grooveshark information from the network! + Échec du chargement des informations Grooveshark depuis le réseau! + + + + Tomahawk::InfoSystem::ChartsPlugin + + + Top Overall + + + + + Artists + + + + + Albums + Albums + + + + Tracks + Pistes + + + + Tomahawk::InfoSystem::FdoNotifyPlugin + + + Tomahawk is playing "%1" by %2%3. + Tomahawk joue "%1" par %2%3. + + + + on "%1" + + + + + Tomahawk::InfoSystem::LastFmPlugin + + + Top Tracks + + + + + Loved Tracks + + + + + Hyped Tracks + + + + + Top Artists + + + + + Hyped Artists + + + + + Tomahawk::InfoSystem::NewReleasesPlugin + + + Albums + + + + + Tomahawk::InfoSystem::TwitterInfoPlugin + + + Listening to "%1" by %2 and loving it! %3 + J'écoute "%1" par %2 et j'adore ! %3 + + + + Tomahawk::ItunesParser + + + Error fetching iTunes information from the network! + Échec du chargement des informations iTunes depuis le réseau! + + + + Tomahawk::JSPFLoader + + + New Playlist + Nouvelle liste de lecture + + + + Failed to save tracks + + + + + Some tracks in the playlist do not contain an artist and a title. They will be ignored. + Certaines pistes dans la liste de lecture ne contiennent pas d'artiste ou de titre. Elles seront ignorées. + + + + XSPF Error + Erreur XSPF + + + + This is not a valid XSPF playlist. + Ceci n'est pas une liste de lecture XSPF valide. + + + + Tomahawk::LatchManager + + + &Catch Up + + + + + + &Listen Along + &Ecouter avec + + + + Tomahawk::Query + + + and + et + + + + You + Vous + + + + you + vous + + + + and + et + + + + %n other(s) + %n autre%n autres + + + + %1 people + %1 personnes + + + + loved this track + + + + + Tomahawk::RdioParser + + + Error fetching Rdio information from the network! + Échec du chargement des informations Rdio depuis le réseau! + + + + Tomahawk::ShortenedLinkParser + + + Network error parsing shortened link! + Erreur réseau lors du décodage de l'URL courte! + + + + Tomahawk::Source + + + + Scanning (%L1 tracks) + Scan en cours (%L1 pistes) + + + + Scanning + Scan en cours + + + + Checking + + + + + Fetching + + + + + Parsing + + + + + Saving (%1%) + + + + + Tomahawk::SpotifyParser + + + Error fetching Spotify information from the network! + Échec du chargement des informations Spotify depuis le réseau! + + + + TomahawkApp + + + My Collection + Ma Collection + + + + TomahawkOAuthTwitter + + + Twitter PIN + + + + + After authenticating on Twitter's web site, +enter the displayed PIN number here: + Après vous être authentifier sur le site de Twitter, +saisissez le numéro PIN ici : + + + + TomahawkTrayIcon + + + + Hide Tomahawk Window + Masquer la fenêtre Tomahawk + + + + Show Tomahawk Window + Afficher la fenêtre Tomahawk + + + + Currently not playing. + Pas de lecture en cours + + + + Play + Lecture + + + + Pause + Pause + + + + TomahawkWindow + + + Tomahawk + Tomahawk + + + + &Settings + + + + + &Controls + &Contrôles + + + + &Network + &Réseau + + + + &Window + &Fenêtre + + + + &Help + &Aide + + + + &Quit + &Quitter + + + + Ctrl+Q + Ctrl+Q + + + + Go &Online + Se c&onnecter + + + + Add &Friend... + Ajouter un &ami... + + + + U&pdate Collection + Mettre à Jo&ur la Collection + + + + Update Collection + Mettre à Jour la Collection + + + + &Configure Tomahawk... + &Configurer Tomahawk... + + + + Load &XSPF... + Charger &XSPF + + + + Create &New Playlist... + Créer une &nouvelle liste de lecture... + + + + About &Tomahawk... + A propos de &Tomahawk + + + + Create New &Automatic Playlist + Créer une nouvelle liste de lecture automatique + + + + Create New &Station + Créer une nouvelle &Station + + + + Show Offline Sources + Afficher les sources hors ligne + + + + Hide Offline Sources + Masquer les sources hors ligne + + + + Minimize + Réduire + + + + Ctrl+M + Ctrl+M + + + + Zoom + Zoom + + + + Meta+Ctrl+Z + Meta+Ctrl+Z + + + + Diagnostics... + Diagnostics... + + + + Fully &Rescan Collection + &Rescanner la collection entièrement + + + + Fully Rescan Collection + Rescanner la collection entièrement + + + + + Play + Lecture + + + + Space + Espace + + + + Previous + Précédent + + + + Next + Suivant + + + + Global Search... + Recherche Globale... + + + + + Check For Updates... + Rechercher une mise à jour... + + + + + + Connect To Peer + + + + + Enter peer address: + + + + + Enter peer port: + + + + + Enter peer key: + + + + + XSPF Error + Erreur XSPF + + + + This is not a valid XSPF playlist. + Ceci n'est pas une liste de lecture XSPF valide. + + + + Failed to save tracks + Échec de la sauvegarde des pistes + + + + Some tracks in the playlist do not contain an artist and a title. They will be ignored. + Certaines pistes dans la liste de lecture ne contiennent pas d'artiste ou de titre. Elles seront ignorées. + + + + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. Make sure you have a suitable Phonon backend and required plugins installed. + Désolé, il y a un problème d'accès à votre matériel audio ou la piste en cours va être sauter. Vérifiez que vous avez un backend Phonon et les plugins requis installés. + + + + Sorry, there is a problem accessing your audio device or the desired track, current track will be skipped. + Désolé, il y a un problème d'accès à votre matériel audio ou la piste en cours va être sauter. + + + + Create New Station + Créer une nouvelle station + + + + Name: + Nom : + + + + New Station + Nouvelle station + + + + New Playlist + Nouvelle liste de lecture + + + + Pause + Pause + + + + Go &offline + Se &déconnecter + + + + Go &online + Se c&onnecter + + + + Authentication Error + Erreur d'authentification + + + + %1 by %2 + track, artist name + %1 par %2 + + + + %1 - %2 + current track, some window title + %1 - %2 + + + + <h2><b>Tomahawk %1<br/>(%2)</h2> + <h2><b>Tomahawk %1<br/>(%2)</h2> + + + + <h2><b>Tomahawk %1</h2> + <h2><b>Tomahawk %1</h2> + + + + Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson + Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson + + + + About Tomahawk + A propos de Tomahawk + + + + TopBar + + + Form + Form + + + + 0 Sources + 0 Source + + + + 0 Tracks + 0 piste + + + + 0 Artists + 0 Artist + + + + 0 Shown + 0 Affiché + + + + Tracks + Pistes + + + + Artists + Artistes + + + + Filter + Filtre + + + + Artist View + + + + + Flat View + + + + + Sources + + + + + Shown + Affiché + + + + TopTracksContext + + + Top Hits + + + + + TrackModel + + + Artist + Artiste + + + + Title + Titre + + + + Album + Album + + + + Track + Piste + + + + Duration + Durée + + + + Bitrate + + + + + Age + Age + + + + Year + Année + + + + Size + Taille + + + + Origin + Origine + + + + Score + Score + + + + Composer + Compositeur + + + + TrackView + + + Sorry, your filter '%1' did not match any results. + Désolé, votre filtre '%1' ne correspond à aucun résultat + + + + TransferStatusItem + + + from + + + + + to + + + + + TreeItemDelegate + + + Unknown + Inconnu + + + + TreeModel + + + Name + Nom + + + + Duration + + + + + Bitrate + + + + + Age + + + + + Year + + + + + Size + + + + + Origin + + + + + Composer + + + + + All Artists + + + + + + My Collection + Ma Collection + + + + + Collection of %1 + Collection de %1 + + + + TwitterConfigWidget + + + Configure this Twitter account + Configurer ce compte Twitter + + + + The Twitter plugin allows you to discover and play music from your Twitter friends running Tomahawk and post messages to your account. + Le plugin Twitter vous permet de découvrir et jouer de la musique de vos amis Twitter qui utilisent Tomahawk et de poster des messages sur votre compte. + + + + Status: No saved credentials + + + + + Authenticate with Twitter + + + + + Twitter Connections + + + + + +If you only want to post tweets, you're done. + +If you want to connect Tomahawk to your friends using Twitter, select the type of tweet and press the button below to send a sync message. You must both be following each other as Direct Messages are used. Then be (very) patient -- it can take several minutes! + +You can re-send a sync message at any time simply by sending another tweet using the button. + + + + + Select the kind of tweet you would like, then press the button to post it: + + + + + Global Tweet + Tweet Global + + + + @Mention + + + + + Direct Message + + + + + e.g. @tomahawk + + + + + Send Message + + + + + ViewManager + + + SuperCollection + SuperCollection + + + + Combined libraries of all your online friends + Collections regroupant toutes celles de vos amis en ligne + + + + All available albums + Tous les albums disponibles + + + + WelcomeWidget + + + Recent Additions + Derniers Ajouts + + + + Newest Stations & Playlists + Dernières stations & listes de lecture + + + + Recently Played Tracks + Joués récemment + + + + No recently created playlists in your network. + Pas de liste de lecture créée récemment sur votre réseau. + + + + Welcome to Tomahawk + + + + + WhatsHotWidget + + + Charts + + + + + WikipediaContext + + + Wikipedia + Wikipedia + + + + XMPPBot + + + +Terms for %1: + + + + + + No terms found, sorry. + + + + + +Hotttness for %1: %2 + + + + + + +Familiarity for %1: %2 + + + + + + +Lyrics for "%1" by %2: + +%3 + + + + + + XSPFLoader + + + Failed to parse contents of XSPF playlist + Échec du décodage de la liste de lecture XSPF + + + + Some playlist entries were found without artist and track name, they will be omitted + Certaines entrées de la liste de lecture n'ont pas d'artiste ou de titre, elles seront omises. + + + + Failed to fetch the desired playlist from the network, or the desired file does not exist + Échec du chargement de la liste de lecture depuis le réseau, ou le fichier n'existe pas + + + + New Playlist + Nouvelle liste de lecture + + + + XmlConsole + + + Xml stream console + + + + + + Filter + Filtre + + + + Save log + + + + + Disabled + + + + + By JID + + + + + By namespace uri + Par namespace uri + + + + By all attributes + + + + + Visible stanzas + + + + + Information query + + + + + Message + + + + + Presence + + + + + Custom + + + + + Close + + + + + Save XMPP log to file + + + + + OpenDocument Format (*.odf);;HTML file (*.html);;Plain text (*.txt) + Format OpenDocument (*.odf);;Fichiers HTML (*.html);;Texte (*.txt) + + + + XmppConfigWidget + + + Xmpp Configuration + Configuration XMPP + + + + Configure this Xmpp account + Configurer ce compte XMPP + + + + Enter your Xmpp login to connect with your friends using Tomahawk! + + + + + Login Information + + + + + Xmpp ID: + + + + + e.g. user@example.com + + + + + Password: + + + + + An account with this name already exists! + Un compte avec ce nom existe déjà ! + + + + Advanced Xmpp Settings + Paramètres XMPP avancés + + + + Server: + + + + + Port: + + + + + Lots of servers don't support this (e.g. GTalk, jabber.org) + + + + + Publish currently playing track + Publier la piste en cours de lecture + + + + Enforce secure connection + + + + + XmppSipPlugin + + + User Interaction + + + + + Host is unknown + + + + + Item not found + + + + + Authorization Error + + + + + Remote Stream Error + + + + + Remote Connection failed + + + + + Internal Server Error + + + + + System shutdown + + + + + Conflict + + + + + Unknown + Ajouter un &ami... + + + + No Compression Support + + + + + No Encryption Support + + + + + No Authorization Support + + + + + No Supported Feature + + + + + Add Friend + + + + + Enter Xmpp ID: + + + + + Add Friend... + + + + + XML Console... + + + + + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! + + + + + Authorize User + + + + + Do you want to grant <b>%1</b> access to your Collection? + Voulez vous donner accès à votre collection à %1 ? + + + + ZeroconfConfig + + + Form + + + + + Local Network configuration + Configuration réseau local + + + + This plugin will automatically find other users running Tomahawk on your local network + Ce plugin va automatiquement trouver les autres utilisateurs de Tomahawk sur votre réseau local + + + + Connect automatically when Tomahawk starts + + + + \ No newline at end of file diff --git a/lang/tomahawk_ja.ts b/lang/tomahawk_ja.ts index f3fdaa4dc..f35372285 100644 --- a/lang/tomahawk_ja.ts +++ b/lang/tomahawk_ja.ts @@ -294,36 +294,41 @@ Remember: Only allow peers to connect if you trust who they are and if you have + social + + + + love Love - + Time 時間 - + Time Left 残り時間 - + Shuffle シャッフル - + Repeat リピート - + Low - + High @@ -677,6 +682,14 @@ Remember: Only allow peers to connect if you trust who they are and if you have 新規プレイリストを作成 + + NewReleasesWidget + + + New Releases + + + PlaylistItemDelegate @@ -1051,6 +1064,49 @@ other: %n年前 + + SocialWidget + + + Form + + + + + Facebook + + + + + Twitter + + + + + Cover + + + + + TextLabel + + + + + Listening to "%1" by %2 and loving it! %4 + + + + + Listening to "%1" by %2 on "%3" and loving it! %4 + + + + + %1 characters left + + + SourceDelegate @@ -1264,6 +1320,11 @@ other: %n年前 + New Releases + + + + Friends @@ -1403,7 +1464,7 @@ other: %n年前 - Allow web browsers to interact with Tomahawk + Allow web browsers to interact with Tomahawk (recommended) @@ -1527,7 +1588,7 @@ other: %n年前 Tomahawk::Accounts::TwitterAccountFactory - + Connect to your Twitter followers. @@ -2257,22 +2318,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall - + Artists - + Albums - + Tracks @@ -2280,12 +2341,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + on "%1" @@ -2293,31 +2354,47 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmPlugin - + Top Tracks - + Loved Tracks - + Hyped Tracks - + Top Artists - + Hyped Artists + + Tomahawk::InfoSystem::NewReleasesPlugin + + + Albums + + + + + Tomahawk::InfoSystem::TwitterInfoPlugin + + + Listening to "%1" by %2 and loving it! %3 + + + Tomahawk::ItunesParser @@ -2467,7 +2544,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3103,17 +3180,17 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + SuperCollection - + Combined libraries of all your online friends - + All available albums @@ -3378,107 +3455,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + No Compression Support - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User - + Do you want to grant <b>%1</b> access to your Collection? diff --git a/lang/tomahawk_pl.ts b/lang/tomahawk_pl.ts index 36ad68d04..bfc1a3ab8 100644 --- a/lang/tomahawk_pl.ts +++ b/lang/tomahawk_pl.ts @@ -34,7 +34,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Description goes here - + Tutaj pojawi się opis @@ -75,7 +75,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have &Follow in real-time - + &Podążaj na bieżąco @@ -195,7 +195,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have After you have scanned your music collection you will find your latest album additions right here. - Po zeskanowaniu swojej kolekcji muzycznej znajdziesz ostatnio dodane albumy właśnie tutaj. + Po zeskanowaniu swojej kolekcji muzycznej w tym miejscu znajdziesz ostatnio dodane albumy. @@ -242,7 +242,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have After you have scanned your music collection you will find your tracks right here. - Po zeskanowaniu swojej kolekcji muzycznej znajdziesz utwory właśnie tutaj. + Po zeskanowaniu swojej kolekcji muzycznej utwory znajdziesz w tym miejscu. @@ -252,7 +252,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Sorry, your filter '%1' did not match any results. - Przepraszamy, twój filtr %1' nie pasuje do żadnych wyników. + Przepraszamy, twój filtr '%1' nie pasuje do żadnych wyników. @@ -294,36 +294,41 @@ Remember: Only allow peers to connect if you trust who they are and if you have + social + + + + love ulubione - + Time Czas - + Time Left Pozostały czas - + Shuffle Losowo - + Repeat Powtarzaj - + Low Nisko - + High Wysoko @@ -423,7 +428,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have <p><b>Sorry!</b>&nbsp;Tomahawk crashed. Information about the crash is now being sent to Tomahawk HQ so that we can fix the bug.</p> - <p><b>Przepraszamy!</b>&nbsp;Tomahawk uległ awarii. Informacja o incydencie zostanie teraz wysłana do kwatery głównej programu, abyśmy mogli poprawić błędy.</p> + <p><b>Przepraszamy!</b>&nbsp;Tomahawk uległ awarii. Abyśmy mogli poprawić błędy, informacja o incydencie zostanie wysłana do kwatery głównej programu.</p> @@ -659,12 +664,12 @@ Remember: Only allow peers to connect if you trust who they are and if you have Tomahawk offers a variety of ways to help you create playlists and find music you enjoy! - Tomahawk oferuje różne sposoby pomocy w tworzeniu list i poszukiwaniu muzyki, którą lubisz! + Tomahawk oferuje różne sposoby pomocy w tworzeniu list i poszukiwaniu twojej ulubionej muzyki! Just enter a genre or tag name and Tomahawk will suggest a few songs to get you started with your new playlist: - Podaj tylko tag lub gatunek, a Tomahawk zasugeruje kilka piosenek, żeby pomóc ci zacząć z nową listą: + Jeśli podasz chociaż jeden tag lub gatunek, Tomahawk postara się zasugerować ci kilka piosenek na dobry początek: @@ -677,6 +682,14 @@ Remember: Only allow peers to connect if you trust who they are and if you have Utwórz nową listę + + NewReleasesWidget + + + New Releases + Nowe Wydania + + PlaylistItemDelegate @@ -695,7 +708,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have played %1 by you - + odtworzone %1 przez ciebie @@ -705,7 +718,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have added %1 - + dodano %1 @@ -713,12 +726,12 @@ Remember: Only allow peers to connect if you trust who they are and if you have A playlist by %1, created %2 - Lista %1, utworzona %2 + %1 lista, utworzona %2 you - ty + Twoja @@ -741,7 +754,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Just a regular old playlist... Give it a name, drag in some tracks, and go! - Po prostu zwykła stara lista... Nazwij, upuść na nią trochę piosenek i gotowe! + Po prostu zwykła stara lista... Nazwij ją, upuść na nią trochę piosenek i gotowe! @@ -787,7 +800,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Hostname of proxy server - Nazwa hosta serwera proxy + Host serwera proxy @@ -948,7 +961,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Hide Queue - Schowaj Kolejkę + Ukryj Kolejkę @@ -964,7 +977,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Not found: %1 - Nie znaleziono %1 + Nie znaleziono: %1 @@ -1008,12 +1021,12 @@ Remember: Only allow peers to connect if you trust who they are and if you have All - + Wszystkie Services - + Usługi @@ -1049,6 +1062,49 @@ Remember: Only allow peers to connect if you trust who they are and if you have Najczęściej odtwarzane utwory, których nie masz + + SocialWidget + + + Form + + + + + Facebook + Facebook + + + + Twitter + Twitter + + + + Cover + Okładka + + + + TextLabel + + + + + Listening to "%1" by %2 and loving it! %4 + + + + + Listening to "%1" by %2 on "%3" and loving it! %4 + + + + + %1 characters left + pozostało znaków: %1 + + SourceDelegate @@ -1101,7 +1157,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Hide - Schowaj + Ukryj @@ -1142,12 +1198,12 @@ Remember: Only allow peers to connect if you trust who they are and if you have Collection - + Kolekcja Latest Additions - + Ostatnio Dodane @@ -1198,7 +1254,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Group - + Grupa @@ -1223,7 +1279,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Browse - + Przeglądaj @@ -1253,17 +1309,22 @@ Remember: Only allow peers to connect if you trust who they are and if you have Recently Played - + Ostatnio Odtworzone Charts - + Listy Przebojów + New Releases + Nowe Wydania + + + Friends - + Znajomi @@ -1301,7 +1362,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have High Quality Streaming - Strumieniowanie w wysokiej jakości + Muzyka w wysokiej jakości @@ -1319,7 +1380,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Local Music Information - Lokalne Informacje o Muzyce + Informacje o Lokalnej Muzyce @@ -1346,7 +1407,7 @@ indywidualnego profilu gustu. Watch for changes - Obserwuj pod kątem zmian + Obserwuj zmiany @@ -1405,7 +1466,7 @@ indywidualnego profilu gustu. - Allow web browsers to interact with Tomahawk + Allow web browsers to interact with Tomahawk (recommended) @@ -1429,7 +1490,7 @@ indywidualnego profilu gustu. %1 downloads - + pobrań: %1 @@ -1452,7 +1513,7 @@ indywidualnego profilu gustu. Configure this Google Account - + Konfiguruj to Konto Google @@ -1475,7 +1536,7 @@ indywidualnego profilu gustu. Connect to Google Talk to find your friends - + Połącz z Google Talk aby znaleźć znajomych @@ -1488,7 +1549,7 @@ indywidualnego profilu gustu. Enter Google Address: - + Podaj Adres Google @@ -1529,9 +1590,9 @@ indywidualnego profilu gustu. Tomahawk::Accounts::TwitterAccountFactory - + Connect to your Twitter followers. - + Łącz się z osobami śledzącymi cię na Twitterze. @@ -1657,7 +1718,7 @@ You may wish to try re-authenticating. Your message has been posted! - + Twoja wiadomość została wysłana! @@ -1688,13 +1749,13 @@ You may wish to try re-authenticating. Add to &Queue - Dodaj do &Kolejki + Dodaj do &kolejki &Love - + &Uwielbiam @@ -1724,12 +1785,12 @@ You may wish to try re-authenticating. &Continue Playback after this Track - + &Kontynuuj odtwarzanie po tym utworze &Stop Playback after this Track - + &Zatrzymaj odtwarzanie po tym utworze @@ -1827,17 +1888,17 @@ Proszę zmienić filtry lub spróbować ponownie. Add some filters above to seed this station! - Dodaj trochę filtrów powyżej, aby załączyć tą stację! + Dodaj powyżej trochę filtrów, a Tomahawk utworzy stację z podobną muzyką! Press Generate to get started! - Naciśnij Generuj, aby zacząć! + Naciśnij Generuj aby zacząć! Add some filters above, and press Generate to get started! - Dodaj trochę filtrów powyżej i naciśnij Generuj, aby zacząć! + Dodaj powyżej trochę filtrów i naciśnij Generuj aby zacząć! @@ -2261,22 +2322,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall Top Wszechczasów - + Artists Artyści - + Albums Albumy - + Tracks Utwory @@ -2284,44 +2345,60 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + on "%1" - + z "%1" Tomahawk::InfoSystem::LastFmPlugin - + Top Tracks - + Loved Tracks - + Hyped Tracks - + Top Artists - + Hyped Artists + + Tomahawk::InfoSystem::NewReleasesPlugin + + + Albums + Albumy + + + + Tomahawk::InfoSystem::TwitterInfoPlugin + + + Listening to "%1" by %2 and loving it! %3 + + + Tomahawk::ItunesParser @@ -2377,27 +2454,27 @@ Try tweaking the filters for a new set of songs to play. and - + i You - + Ty you - + ty and - + i %n other(s) - + %n inny%n inne%n innych @@ -2471,7 +2548,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Moja Kolekcja @@ -2481,7 +2558,7 @@ Try tweaking the filters for a new set of songs to play. Twitter PIN - PIN Twitter + Twitter PIN @@ -2806,7 +2883,7 @@ wprowadź pokazany numer PIN tutaj: Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Thanks to: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter and Steve Robertson - + Copyright 2010 - 2012<br/>Christian Muehlhaeuser &lt;muesli@tomahawk-player.org&gt;<br/><br/>Podziękowania dla: Leo Franchi, Jeff Mitchell, Dominik Schmidt, Jason Herskowitz, Alejandro Wainzinger, Hugo Lindstr&ouml;m, Michael Zanetti, Harald Sitter oraz Steve Robertson @@ -2945,7 +3022,7 @@ wprowadź pokazany numer PIN tutaj: Composer - + Kompozytor @@ -3017,7 +3094,7 @@ wprowadź pokazany numer PIN tutaj: Composer - + Kompozytor @@ -3042,12 +3119,12 @@ wprowadź pokazany numer PIN tutaj: Configure this Twitter account - Konfiguruj to konto Twitter + Konfiguruj konto na Twitter The Twitter plugin allows you to discover and play music from your Twitter friends running Tomahawk and post messages to your account. - Wtyczka Twittera pozwala ci odkrywać i odtwarzać muzykę znajomych z Twittera, którzy używają Tomahawka i wysyłać wiadomości ze swojego konta. + Wtyczka Twittera pozwala ci odkrywać i odtwarzać muzykę znajomych z Twittera, którzy używają Tomahawka oraz wysyłać wiadomości ze swojego konta. @@ -3075,14 +3152,14 @@ You can re-send a sync message at any time simply by sending another tweet using Jeśli chcesz jedynie wysyłać tweety, wszystko gotowe. -Jeśli chcesz, aby Tomahawk łączył się z twoimi znajomymi używając Twittera, wybierz rodzaj tweeta i naciśnij przycisk poniżej by wysłać wiadomość synchronizacyjną. Obie strony muszą śledzić się nawzajem, ponieważ używane są Wiadomości Prywatne. Teraz miej (dużo) cierpliwości - może to zająć kilka minut! +Jeśli chcesz, aby Tomahawk łączył się z twoimi znajomymi używając Twittera, wybierz rodzaj tweeta i naciśnij przycisk poniżej by wysłać wiadomość synchronizacyjną. Obie strony muszą śledzić się nawzajem, ponieważ używane są Wiadomości Prywatne. Miej (bardzo) dużo cierpliwości - może to zająć kilka minut! -Możesz wysłać wiadomość synchronizacyjną ponownie kiedykolwiek, po prostu wyślij kolejnego tweeta używając przycisku. +Zawsze możesz ponownie wysłać wiadomość synchronizacyjną - po prostu wyślij kolejnego tweeta używając przycisku. Select the kind of tweet you would like, then press the button to post it: - Wybierz rodzaj tweeta, który ci odpowiada, a następnie naciśnij przycisk, aby go wysłać: + Wybierz rodzaj tweeta który ci odpowiada, a następnie naciśnij przycisk aby go wysłać: @@ -3113,17 +3190,17 @@ Możesz wysłać wiadomość synchronizacyjną ponownie kiedykolwiek, po prostu ViewManager - + SuperCollection - + SuperKolekcja - + Combined libraries of all your online friends - + Połączone biblioteki wszystkich twoich znajomych online - + All available albums Wszystkie dostępne albumy @@ -3348,12 +3425,12 @@ Tekst dla "%1" wykonawcy %2: e.g. user@example.com - + n.p. uzytkownik@example.com Password: - + Hasło: @@ -3368,12 +3445,12 @@ Tekst dla "%1" wykonawcy %2: Server: - + Serwer: Port: - + Port: @@ -3388,115 +3465,115 @@ Tekst dla "%1" wykonawcy %2: Enforce secure connection - + Wymuś bezpieczne połączenie XmppSipPlugin - + User Interaction - + Host is unknown - + Nieznany Host - + Item not found - + Authorization Error - + Remote Stream Error - - - Remote Connection failed - - - - - Internal Server Error - - - - - System shutdown - - - - - Conflict - - - - - Unknown - - - - - No Compression Support - - + Remote Connection failed + Połączenie sieciowe się nie powiodło + + + + Internal Server Error + Wewnętrzny błąd serwera + + + + System shutdown + Wyłączenie systemu + + + + Conflict + Konflikt + + + + Unknown + Nieznany + + + + No Compression Support + Brak obsługi kompresji + + + No Encryption Support - + Brak obsługi szyfrowania - + No Authorization Support - + Brak obsługi autoryzacji - + No Supported Feature - + Brak obsługi danej funkcji - + Add Friend - + Dodaj Znajomego - + Enter Xmpp ID: - + Add Friend... - + Dodaj Znajomego... - + XML Console... - + Konsola XML... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User - + Autoryzuj Użytkownika - + Do you want to grant <b>%1</b> access to your Collection? - + Czy chcesz udzielić dostępu do swojej kolekcji <b>%1</b>? diff --git a/lang/tomahawk_pt_BR.ts b/lang/tomahawk_pt_BR.ts index 5075b0e7d..d5dec97d5 100644 --- a/lang/tomahawk_pt_BR.ts +++ b/lang/tomahawk_pt_BR.ts @@ -294,36 +294,41 @@ Remember: Only allow peers to connect if you trust who they are and if you have + social + + + + love Favorita - + Time Tempo - + Time Left Tempo restante - + Shuffle Embaralhar - + Repeat Repetir - + Low Diminuir - + High Aumentar @@ -677,6 +682,14 @@ Remember: Only allow peers to connect if you trust who they are and if you have Criar uma nova lista de reprodução + + NewReleasesWidget + + + New Releases + + + PlaylistItemDelegate @@ -1049,6 +1062,49 @@ Remember: Only allow peers to connect if you trust who they are and if you have Faixas mais reproduzidas que você não possui + + SocialWidget + + + Form + + + + + Facebook + + + + + Twitter + + + + + Cover + + + + + TextLabel + + + + + Listening to "%1" by %2 and loving it! %4 + + + + + Listening to "%1" by %2 on "%3" and loving it! %4 + + + + + %1 characters left + + + SourceDelegate @@ -1262,6 +1318,11 @@ Remember: Only allow peers to connect if you trust who they are and if you have + New Releases + + + + Friends Amigos @@ -1404,7 +1465,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have - Allow web browsers to interact with Tomahawk + Allow web browsers to interact with Tomahawk (recommended) @@ -1528,7 +1589,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Tomahawk::Accounts::TwitterAccountFactory - + Connect to your Twitter followers. @@ -2260,22 +2321,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall Classificação geral - + Artists Artistas - + Albums Álbuns - + Tracks Faixas @@ -2283,12 +2344,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + on "%1" @@ -2296,31 +2357,47 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmPlugin - + Top Tracks Faixas principais - + Loved Tracks Faixas favoritas - + Hyped Tracks Faixas mais populares - + Top Artists Artistas principais - + Hyped Artists Artistas mais populares + + Tomahawk::InfoSystem::NewReleasesPlugin + + + Albums + + + + + Tomahawk::InfoSystem::TwitterInfoPlugin + + + Listening to "%1" by %2 and loving it! %3 + + + Tomahawk::ItunesParser @@ -2470,7 +2547,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Minha biblioteca @@ -3112,17 +3189,17 @@ Você pode enviar uma outra mensagem de sincronia a qualquer momento simplesment ViewManager - + SuperCollection - + Combined libraries of all your online friends - + All available albums Todos os álbuns disponíveis @@ -3394,107 +3471,107 @@ Letras de "%1" por %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + No Compression Support - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User - + Do you want to grant <b>%1</b> access to your Collection? diff --git a/lang/tomahawk_ru.ts b/lang/tomahawk_ru.ts index 21b1eb9b0..f3a580596 100644 --- a/lang/tomahawk_ru.ts +++ b/lang/tomahawk_ru.ts @@ -294,36 +294,41 @@ Remember: Only allow peers to connect if you trust who they are and if you have + social + + + + love Любимая - + Time Прошло - + Time Left Осталось - + Shuffle Случаная - + Repeat Повторять - + Low Тише - + High Громче @@ -677,6 +682,14 @@ Remember: Only allow peers to connect if you trust who they are and if you have Создать Новый Плейлист + + NewReleasesWidget + + + New Releases + + + PlaylistItemDelegate @@ -1049,6 +1062,49 @@ Remember: Only allow peers to connect if you trust who they are and if you have Популярные проигрываемые песни которых у вас нет + + SocialWidget + + + Form + + + + + Facebook + + + + + Twitter + + + + + Cover + + + + + TextLabel + + + + + Listening to "%1" by %2 and loving it! %4 + + + + + Listening to "%1" by %2 on "%3" and loving it! %4 + + + + + %1 characters left + + + SourceDelegate @@ -1262,6 +1318,11 @@ Remember: Only allow peers to connect if you trust who they are and if you have + New Releases + + + + Friends Друзья @@ -1401,7 +1462,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have - Allow web browsers to interact with Tomahawk + Allow web browsers to interact with Tomahawk (recommended) @@ -1525,7 +1586,7 @@ Remember: Only allow peers to connect if you trust who they are and if you have Tomahawk::Accounts::TwitterAccountFactory - + Connect to your Twitter followers. @@ -2257,22 +2318,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall - + Artists Исполнители - + Albums Альбомы - + Tracks Песни @@ -2280,12 +2341,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + on "%1" @@ -2293,31 +2354,47 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmPlugin - + Top Tracks Топ песен - + Loved Tracks Любимые песни - + Hyped Tracks - + Top Artists Любимые исполнители - + Hyped Artists + + Tomahawk::InfoSystem::NewReleasesPlugin + + + Albums + + + + + Tomahawk::InfoSystem::TwitterInfoPlugin + + + Listening to "%1" by %2 and loving it! %3 + + + Tomahawk::ItunesParser @@ -2467,7 +2544,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection Моя коллекция @@ -3107,17 +3184,17 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + SuperCollection Общая коллекция - + Combined libraries of all your online friends Комбинированные библиотек всех ваших друзей онлайн - + All available albums Доступные альбомы @@ -3388,107 +3465,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction Взаимодействие с пользователем - + Host is unknown Неизвестный хост - + Item not found Песня не найдена - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + No Compression Support - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User - + Do you want to grant <b>%1</b> access to your Collection? diff --git a/lang/tomahawk_sv.ts b/lang/tomahawk_sv.ts index 3b3be3ab1..e1e3bddea 100644 --- a/lang/tomahawk_sv.ts +++ b/lang/tomahawk_sv.ts @@ -296,36 +296,41 @@ Kom ihåg: Tillåt endast anslutning från klienter du litar på, och som har la + social + + + + love älska - + Time Tid - + Time Left Tid kvar - + Shuffle Blanda - + Repeat Upprepa - + Low Låg - + High Hög @@ -679,6 +684,14 @@ Kom ihåg: Tillåt endast anslutning från klienter du litar på, och som har la Erstelle eine neue Playliste + + NewReleasesWidget + + + New Releases + + + PlaylistItemDelegate @@ -1052,6 +1065,49 @@ Kom ihåg: Tillåt endast anslutning från klienter du litar på, och som har la Mest spelade spår som du inte har + + SocialWidget + + + Form + + + + + Facebook + + + + + Twitter + + + + + Cover + + + + + TextLabel + + + + + Listening to "%1" by %2 and loving it! %4 + + + + + Listening to "%1" by %2 on "%3" and loving it! %4 + + + + + %1 characters left + + + SourceDelegate @@ -1265,6 +1321,11 @@ Kom ihåg: Tillåt endast anslutning från klienter du litar på, och som har la + New Releases + + + + Friends @@ -1404,7 +1465,7 @@ Kom ihåg: Tillåt endast anslutning från klienter du litar på, och som har la - Allow web browsers to interact with Tomahawk + Allow web browsers to interact with Tomahawk (recommended) @@ -1528,7 +1589,7 @@ Kom ihåg: Tillåt endast anslutning från klienter du litar på, och som har la Tomahawk::Accounts::TwitterAccountFactory - + Connect to your Twitter followers. @@ -2258,22 +2319,22 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::ChartsPlugin - + Top Overall - + Artists Artister - + Albums Album - + Tracks Spår @@ -2281,12 +2342,12 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::FdoNotifyPlugin - + Tomahawk is playing "%1" by %2%3. - + on "%1" @@ -2294,31 +2355,47 @@ Try tweaking the filters for a new set of songs to play. Tomahawk::InfoSystem::LastFmPlugin - + Top Tracks - + Loved Tracks - + Hyped Tracks - + Top Artists - + Hyped Artists + + Tomahawk::InfoSystem::NewReleasesPlugin + + + Albums + + + + + Tomahawk::InfoSystem::TwitterInfoPlugin + + + Listening to "%1" by %2 and loving it! %3 + + + Tomahawk::ItunesParser @@ -2468,7 +2545,7 @@ Try tweaking the filters for a new set of songs to play. TomahawkApp - + My Collection @@ -3104,17 +3181,17 @@ You can re-send a sync message at any time simply by sending another tweet using ViewManager - + SuperCollection - + Combined libraries of all your online friends - + All available albums Alla tillgängliga album @@ -3379,107 +3456,107 @@ Lyrics for "%1" by %2: XmppSipPlugin - + User Interaction - + Host is unknown - + Item not found - + Authorization Error - + Remote Stream Error - + Remote Connection failed - + Internal Server Error - + System shutdown - + Conflict - + Unknown - + No Compression Support - + No Encryption Support - + No Authorization Support - + No Supported Feature - + Add Friend - + Enter Xmpp ID: - + Add Friend... - + XML Console... - + I'm sorry -- I'm just an automatic presence used by Tomahawk Player (http://gettomahawk.com). If you are getting this message, the person you are trying to reach is probably not signed on, so please try again later! - + Authorize User - + Do you want to grant <b>%1</b> access to your Collection? diff --git a/resources.qrc b/resources.qrc index eed64bd59..602774165 100644 --- a/resources.qrc +++ b/resources.qrc @@ -87,6 +87,7 @@ data/images/station.png data/images/new-additions.png data/images/charts.png + data/images/new-releases.png data/images/loved_playlist.png data/images/dashboard.png data/images/artist-icon.png diff --git a/src/accounts/lastfm/LastFmAccount.cpp b/src/accounts/lastfm/LastFmAccount.cpp index 7e4ac519f..61de6bec7 100644 --- a/src/accounts/lastfm/LastFmAccount.cpp +++ b/src/accounts/lastfm/LastFmAccount.cpp @@ -54,8 +54,6 @@ LastFmAccountFactory::icon() const LastFmAccount::LastFmAccount( const QString& accountId ) : CustomAtticaAccount( accountId ) { - m_infoPlugin = QWeakPointer< LastFmPlugin >( new LastFmPlugin( this ) ); - setAccountFriendlyName( "Last.Fm" ); m_icon.load( RESPATH "images/lastfm-icon.png" ); @@ -70,12 +68,22 @@ LastFmAccount::LastFmAccount( const QString& accountId ) { hookupResolver(); } + + + if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() ) + { + infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() ); + Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() ); + QMetaObject::invokeMethod( infoPlugin().data(), "init", Qt::QueuedConnection ); + } } LastFmAccount::~LastFmAccount() { - delete m_infoPlugin.data(); + if ( m_infoPlugin ) + Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin() ); + delete m_resolver.data(); } @@ -155,10 +163,13 @@ LastFmAccount::icon() const } -InfoPlugin* +InfoPluginPtr LastFmAccount::infoPlugin() { - return m_infoPlugin.data(); + if ( m_infoPlugin.isNull() ) + m_infoPlugin = QWeakPointer< LastFmPlugin >( new LastFmPlugin( this ) ); + + return InfoPluginPtr( m_infoPlugin.data() ); } bool @@ -178,7 +189,8 @@ LastFmAccount::saveConfig() setScrobble( m_configWidget.data()->scrobble() ); } - m_infoPlugin.data()->settingsChanged(); + if ( m_infoPlugin ) + QTimer::singleShot( 0, m_infoPlugin.data(), SLOT( settingsChanged() ) ); } diff --git a/src/accounts/lastfm/LastFmAccount.h b/src/accounts/lastfm/LastFmAccount.h index 7988c37d6..e7fc5adf6 100644 --- a/src/accounts/lastfm/LastFmAccount.h +++ b/src/accounts/lastfm/LastFmAccount.h @@ -72,7 +72,7 @@ public: virtual void authenticate(); virtual SipPlugin* sipPlugin() { return 0; } - virtual Tomahawk::InfoSystem::InfoPlugin* infoPlugin(); + virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin(); virtual bool isAuthenticated() const; diff --git a/src/accounts/lastfm/lastfmplugin.cpp b/src/accounts/lastfm/lastfmplugin.cpp index 7e824fa53..3a9275323 100644 --- a/src/accounts/lastfm/lastfmplugin.cpp +++ b/src/accounts/lastfm/lastfmplugin.cpp @@ -46,17 +46,27 @@ LastFmPlugin::LastFmPlugin( LastFmAccount* account ) { m_supportedGetTypes << InfoAlbumCoverArt << InfoArtistImages << InfoArtistSimilars << InfoArtistSongs << InfoChart << InfoChartCapabilities; m_supportedPushTypes << InfoSubmitScrobble << InfoSubmitNowPlaying << InfoLove << InfoUnLove; +} + +void +LastFmPlugin::init() +{ + if ( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() && thread() != Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() ) + { + tDebug() << "Failure: move to the worker thread before running init"; + return; + } // Flush session key cache // TODO WHY FLUSH // m_account->setSessionKey( QByteArray() ); lastfm::ws::ApiKey = "7194b85b6d1f424fe1668173a78c0c4a"; lastfm::ws::SharedSecret = "ba80f1df6d27ae63e9cb1d33ccf2052f"; - lastfm::ws::Username = m_account->username(); + lastfm::ws::Username = m_account.data()->username(); lastfm::setNetworkAccessManager( TomahawkUtils::nam() ); - m_pw = m_account->password(); + m_pw = m_account.data()->password(); //HACK work around a bug in liblastfm---it doesn't create its config dir, so when it // tries to write the track cache, it fails silently. until we have a fixed version, do this @@ -707,23 +717,26 @@ LastFmPlugin::artistImagesReturned() void LastFmPlugin::settingsChanged() { - if ( !m_scrobbler && m_account->scrobble() ) + if ( m_account.isNull() ) + return; + + if ( !m_scrobbler && m_account.data()->scrobble() ) { // can simply create the scrobbler - lastfm::ws::Username = m_account->username(); - m_pw = m_account->password(); + lastfm::ws::Username = m_account.data()->username(); + m_pw = m_account.data()->password(); createScrobbler(); } - else if ( m_scrobbler && !m_account->scrobble() ) + else if ( m_scrobbler && !m_account.data()->scrobble() ) { delete m_scrobbler; m_scrobbler = 0; } - else if ( m_account->username() != lastfm::ws::Username || - m_account->password() != m_pw ) + else if ( m_account.data()->username() != lastfm::ws::Username || + m_account.data()->password() != m_pw ) { - lastfm::ws::Username = m_account->username(); - m_pw = m_account->password(); + lastfm::ws::Username = m_account.data()->username(); + m_pw = m_account.data()->password(); // credentials have changed, have to re-create scrobbler for them to take effect if ( m_scrobbler ) { @@ -740,12 +753,12 @@ void LastFmPlugin::onAuthenticated() { QNetworkReply* authJob = dynamic_cast( sender() ); - if ( !authJob ) + if ( !authJob || m_account.isNull() ) { tLog() << Q_FUNC_INFO << "Help! No longer got a last.fm auth job!"; return; } - + if ( authJob->error() == QNetworkReply::NoError ) { lastfm::XmlQuery lfm = lastfm::XmlQuery( authJob->readAll() ); @@ -753,16 +766,16 @@ LastFmPlugin::onAuthenticated() if ( lfm.children( "error" ).size() > 0 ) { tLog() << "Error from authenticating with Last.fm service:" << lfm.text(); - m_account->setSessionKey( QByteArray() ); + m_account.data()->setSessionKey( QByteArray() ); } else { lastfm::ws::SessionKey = lfm[ "session" ][ "key" ].text(); - m_account->setSessionKey( lastfm::ws::SessionKey.toLatin1() ); + m_account.data()->setSessionKey( lastfm::ws::SessionKey.toLatin1() ); // qDebug() << "Got session key from last.fm"; - if ( m_account->scrobble() ) + if ( m_account.data()->scrobble() ) m_scrobbler = new lastfm::Audioscrobbler( "thk" ); } } @@ -778,7 +791,10 @@ LastFmPlugin::onAuthenticated() void LastFmPlugin::createScrobbler() { - if ( m_account->sessionKey().isEmpty() ) // no session key, so get one + if ( m_account.isNull() ) + return; + + if ( m_account.data()->sessionKey().isEmpty() ) // no session key, so get one { qDebug() << "LastFmPlugin::createScrobbler Session key is empty"; QString authToken = TomahawkUtils::md5( ( lastfm::ws::Username.toLower() + TomahawkUtils::md5( m_pw.toUtf8() ) ).toUtf8() ); @@ -794,7 +810,7 @@ LastFmPlugin::createScrobbler() else { qDebug() << "LastFmPlugin::createScrobbler Already have session key"; - lastfm::ws::SessionKey = m_account->sessionKey(); + lastfm::ws::SessionKey = m_account.data()->sessionKey(); m_scrobbler = new lastfm::Audioscrobbler( "thk" ); } diff --git a/src/accounts/lastfm/lastfmplugin.h b/src/accounts/lastfm/lastfmplugin.h index c3c86bb96..4d9b7d530 100644 --- a/src/accounts/lastfm/lastfmplugin.h +++ b/src/accounts/lastfm/lastfmplugin.h @@ -49,6 +49,7 @@ public: virtual ~LastFmPlugin(); public slots: + void init(); void settingsChanged(); void onAuthenticated(); @@ -79,7 +80,7 @@ private: void dataError( Tomahawk::InfoSystem::InfoRequestData requestData ); - Accounts::LastFmAccount* m_account; + QWeakPointer< Accounts::LastFmAccount > m_account; QList parseTrackList( QNetworkReply * reply ); lastfm::MutableTrack m_track; diff --git a/src/accounts/spotify/SpotifyAccount.h b/src/accounts/spotify/SpotifyAccount.h index 4379e6502..d60f26086 100644 --- a/src/accounts/spotify/SpotifyAccount.h +++ b/src/accounts/spotify/SpotifyAccount.h @@ -84,7 +84,7 @@ public: virtual void saveConfig(); virtual QWidget* aclWidget() { return 0; } - virtual InfoSystem::InfoPlugin* infoPlugin() { return 0; } + virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() { return Tomahawk::InfoSystem::InfoPluginPtr(); } virtual SipPlugin* sipPlugin() { return 0; } QString sendMessage( const QVariantMap& msg, QObject* receiver = 0, const QString& slot = QString() ); diff --git a/src/accounts/twitter/CMakeLists.txt b/src/accounts/twitter/CMakeLists.txt index 253bd4e03..62bcf83b6 100644 --- a/src/accounts/twitter/CMakeLists.txt +++ b/src/accounts/twitter/CMakeLists.txt @@ -8,6 +8,7 @@ add_definitions( -DACCOUNTDLLEXPORT_PRO ) set( twitterAccountSources twitteraccount.cpp + twitterinfoplugin.cpp twitterconfigwidget.cpp tomahawkoauthtwitter.cpp sip/twittersip.cpp @@ -15,6 +16,7 @@ set( twitterAccountSources set( twitterAccountHeaders twitteraccount.h + twitterinfoplugin.h twitterconfigwidget.h tomahawkoauthtwitter.h sip/twittersip.h diff --git a/src/accounts/twitter/twitteraccount.cpp b/src/accounts/twitter/twitteraccount.cpp index 92eaab161..da1fb2467 100644 --- a/src/accounts/twitter/twitteraccount.cpp +++ b/src/accounts/twitter/twitteraccount.cpp @@ -21,6 +21,7 @@ #include "twitteraccount.h" #include "twitterconfigwidget.h" #include "accounts/twitter/tomahawkoauthtwitter.h" +#include "libtomahawk/infosystem/infosystem.h" #include "sip/SipPlugin.h" @@ -47,9 +48,10 @@ TwitterAccountFactory::createAccount( const QString& accountId ) TwitterAccount::TwitterAccount( const QString &accountId ) : Account( accountId ) , m_isAuthenticated( false ) + , m_isAuthenticating( false ) { setAccountServiceName( "Twitter" ); - setTypes( AccountTypes( InfoType | SipType ) ); + setTypes( AccountTypes( StatusPushType | SipType ) ); qDebug() << "Got cached peers:" << configuration() << configuration()[ "cachedpeers" ]; @@ -99,19 +101,57 @@ TwitterAccount::sipPlugin() } +Tomahawk::InfoSystem::InfoPluginPtr +TwitterAccount::infoPlugin() +{ + if ( m_twitterInfoPlugin.isNull() ) + m_twitterInfoPlugin = QWeakPointer< Tomahawk::InfoSystem::TwitterInfoPlugin >( new Tomahawk::InfoSystem::TwitterInfoPlugin( this ) ); + + return Tomahawk::InfoSystem::InfoPluginPtr( m_twitterInfoPlugin.data() ); +} + + void TwitterAccount::authenticate() { + // Since we need to have a chance for deletion (via the infosystem) to work on the info plugin, we put this on the event loop + tDebug() << Q_FUNC_INFO; + QTimer::singleShot( 0, this, SLOT( authenticateSlot() ) ); +} + + +void +TwitterAccount::authenticateSlot() +{ + tDebug() << Q_FUNC_INFO; + if ( m_twitterInfoPlugin.isNull() ) + { + if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() ) + { + infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() ); + Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() ); + QMetaObject::invokeMethod( infoPlugin().data(), "init", Qt::QueuedConnection ); + } + } + + if ( m_isAuthenticating ) + { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Already authenticating"; + return; + } + tDebug() << Q_FUNC_INFO << "credentials: " << credentials().keys(); if ( credentials()[ "oauthtoken" ].toString().isEmpty() || credentials()[ "oauthtokensecret" ].toString().isEmpty() ) { - qDebug() << "TwitterSipPlugin has empty Twitter credentials; not connecting"; + tDebug() << Q_FUNC_INFO << "TwitterSipPlugin has empty Twitter credentials; not connecting"; return; } if ( refreshTwitterAuth() ) { + m_isAuthenticating = true; + tDebug() << Q_FUNC_INFO << "Verifying credentials"; QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( m_twitterAuth.data(), this ); connect( credVerifier, SIGNAL( parsedUser( const QTweetUser & ) ), SLOT( connectAuthVerifyReply( const QTweetUser & ) ) ); credVerifier->verify(); @@ -122,10 +162,17 @@ TwitterAccount::authenticate() void TwitterAccount::deauthenticate() { - if ( sipPlugin() ) + tDebug() << Q_FUNC_INFO; + + if ( m_twitterSipPlugin ) sipPlugin()->disconnectPlugin(); + if ( m_twitterInfoPlugin ) + Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( m_twitterInfoPlugin.data() ); + m_isAuthenticated = false; + m_isAuthenticating = false; + emit nowDeauthenticated(); } @@ -139,7 +186,7 @@ TwitterAccount::refreshTwitterAuth() delete m_twitterAuth.data(); Q_ASSERT( TomahawkUtils::nam() != 0 ); - qDebug() << Q_FUNC_INFO << " with nam " << TomahawkUtils::nam(); + tDebug() << Q_FUNC_INFO << " with nam " << TomahawkUtils::nam(); m_twitterAuth = QWeakPointer< TomahawkOAuthTwitter >( new TomahawkOAuthTwitter( TomahawkUtils::nam(), this ) ); if( m_twitterAuth.isNull() ) @@ -155,6 +202,7 @@ TwitterAccount::refreshTwitterAuth() void TwitterAccount::connectAuthVerifyReply( const QTweetUser &user ) { + m_isAuthenticating = false; if ( user.id() == 0 ) { qDebug() << "TwitterAccount could not authenticate to Twitter"; @@ -174,6 +222,8 @@ TwitterAccount::connectAuthVerifyReply( const QTweetUser &user ) emit nowAuthenticated( m_twitterAuth, user ); } } + + QPixmap TwitterAccount::icon() const { return QPixmap( ":/twitter-icon.png" ); diff --git a/src/accounts/twitter/twitteraccount.h b/src/accounts/twitter/twitteraccount.h index 16dcf6242..2ec8e4079 100644 --- a/src/accounts/twitter/twitteraccount.h +++ b/src/accounts/twitter/twitteraccount.h @@ -25,6 +25,7 @@ #include "tomahawkoauthtwitter.h" #include "sip/twittersip.h" +#include "twitterinfoplugin.h" #include "accounts/accountdllmacro.h" #include "accounts/Account.h" @@ -49,7 +50,7 @@ public: QString factoryId() const { return "twitteraccount"; } QString description() const { return tr( "Connect to your Twitter followers." ); } QPixmap icon() const { return QPixmap( ":/twitter-icon.png" ); } - AccountTypes types() const { return AccountTypes( SipType ); }; + AccountTypes types() const { return AccountTypes( SipType | StatusPushType ); }; Account* createAccount( const QString& pluginId = QString() ); }; @@ -69,7 +70,7 @@ public: ConnectionState connectionState() const; - Tomahawk::InfoSystem::InfoPlugin* infoPlugin() { return 0; } + Tomahawk::InfoSystem::InfoPluginPtr infoPlugin(); SipPlugin* sipPlugin(); QWidget* configurationWidget() { return m_configWidget.data(); } @@ -83,15 +84,18 @@ signals: void nowDeauthenticated(); private slots: + void authenticateSlot(); void configDialogAuthedSignalSlot( bool authed ); void connectAuthVerifyReply( const QTweetUser &user ); private: QIcon m_icon; bool m_isAuthenticated; + bool m_isAuthenticating; QWeakPointer< TomahawkOAuthTwitter > m_twitterAuth; QWeakPointer< TwitterConfigWidget > m_configWidget; QWeakPointer< TwitterSipPlugin > m_twitterSipPlugin; + QWeakPointer< Tomahawk::InfoSystem::TwitterInfoPlugin > m_twitterInfoPlugin; // for settings access friend class TwitterConfigWidget; diff --git a/src/accounts/twitter/twitterinfoplugin.cpp b/src/accounts/twitter/twitterinfoplugin.cpp new file mode 100644 index 000000000..1de65a9ef --- /dev/null +++ b/src/accounts/twitter/twitterinfoplugin.cpp @@ -0,0 +1,174 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Dominik Schmidt + * Copyright 2012, Jeff Mitchell + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + + +#include "twitterinfoplugin.h" + +#include "accounts/twitter/twitteraccount.h" + +#include +#include + +#include "globalactionmanager.h" +#include "utils/logger.h" + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +TwitterInfoPlugin::TwitterInfoPlugin( Tomahawk::Accounts::TwitterAccount* account ) + : m_account( account ) +{ + m_supportedPushTypes << InfoLove; +} + + +void +TwitterInfoPlugin::init() +{ + if ( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() && thread() != Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() ) + { + tDebug() << "Failure: move to the worker thread before running init"; + return; + } + + QVariantHash credentials = m_account->credentials(); + if ( credentials[ "oauthtoken" ].toString().isEmpty() || credentials[ "oauthtokensecret" ].toString().isEmpty() ) + { + tDebug() << "TwitterInfoPlugin has empty Twitter credentials; not connecting"; + return; + } + + if ( refreshTwitterAuth() ) + { + QTweetAccountVerifyCredentials *credVerifier = new QTweetAccountVerifyCredentials( m_twitterAuth.data(), this ); + connect( credVerifier, SIGNAL( parsedUser( const QTweetUser & ) ), SLOT( connectAuthVerifyReply( const QTweetUser & ) ) ); + credVerifier->verify(); + } +} + + +TwitterInfoPlugin::~TwitterInfoPlugin() +{ + tDebug() << Q_FUNC_INFO; +} + + +bool +TwitterInfoPlugin::refreshTwitterAuth() +{ + tDebug() << Q_FUNC_INFO << " begin" << this; + if( !m_twitterAuth.isNull() ) + delete m_twitterAuth.data(); + + Q_ASSERT( TomahawkUtils::nam() != 0 ); + tDebug() << Q_FUNC_INFO << " with nam " << TomahawkUtils::nam() << this; + m_twitterAuth = QWeakPointer< TomahawkOAuthTwitter >( new TomahawkOAuthTwitter( TomahawkUtils::nam(), this ) ); + + if( m_twitterAuth.isNull() ) + return false; + + m_twitterAuth.data()->setOAuthToken( m_account->credentials()[ "oauthtoken" ].toString().toLatin1() ); + m_twitterAuth.data()->setOAuthTokenSecret( m_account->credentials()[ "oauthtokensecret" ].toString().toLatin1() ); + + return true; +} + + +void +TwitterInfoPlugin::connectAuthVerifyReply( const QTweetUser &user ) +{ + if ( user.id() == 0 ) + { + tDebug() << "TwitterInfoPlugin could not authenticate to Twitter" << this; + deleteLater(); + return; + } + else + { + tDebug() << "TwitterInfoPlugin successfully authenticated to Twitter" << this; + return; + } +} + + +void +TwitterInfoPlugin::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) +{ + tDebug() << Q_FUNC_INFO; + if ( !isValid() ) + { + tDebug() << Q_FUNC_INFO << "Plugin not valid, deleting and returning"; + deleteLater(); + return; + } + + Tomahawk::InfoSystem::PushInfoPair pushInfoPair = pushData.infoPair; + + if ( !pushInfoPair.second.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) + { + tDebug() << Q_FUNC_INFO << "Cannot convert input into an info string hash"; + return; + } + + Tomahawk::InfoSystem::InfoStringHash info = pushInfoPair.second.value< Tomahawk::InfoSystem::InfoStringHash >(); + + QString msg = tr( "Listening to \"%1\" by %2 and loving it! %3" ) + .arg( info[ "title" ] ) + .arg( info[ "artist" ] ) + .arg( pushInfoPair.first.contains( "shorturl" ) ? + pushInfoPair.first[ "shorturl" ].toUrl().toString() : + GlobalActionManager::instance()->openLink( info[ "title" ], info[ "artist" ], info[ "album" ] ).toString() ); + + QTweetStatusUpdate *statUpdate = new QTweetStatusUpdate( m_twitterAuth.data(), this ); + connect( statUpdate, SIGNAL( postedStatus(const QTweetStatus &) ), SLOT( postLovedStatusUpdateReply(const QTweetStatus &) ) ); + connect( statUpdate, SIGNAL( error(QTweetNetBase::ErrorCode, const QString&) ), SLOT( postLovedStatusUpdateError(QTweetNetBase::ErrorCode, const QString &) ) ); + tDebug() << Q_FUNC_INFO << "Posting message: " << msg; + statUpdate->post( msg ); +} + + +void +TwitterInfoPlugin::postLovedStatusUpdateReply( const QTweetStatus& status ) +{ + if ( status.id() == 0 ) + tDebug() << Q_FUNC_INFO << "Failed to post loved status"; + else + tDebug() << Q_FUNC_INFO << "Successfully posted loved status"; +} + + +void +TwitterInfoPlugin::postLovedStatusUpdateError( QTweetNetBase::ErrorCode code, const QString& errorMsg ) +{ + tDebug() << Q_FUNC_INFO << "Error posting love message, error code is " << code << ", error message is " << errorMsg; +} + + +bool +TwitterInfoPlugin::isValid() const +{ + return !m_twitterAuth.isNull(); +} + +} + +} \ No newline at end of file diff --git a/src/accounts/twitter/twitterinfoplugin.h b/src/accounts/twitter/twitterinfoplugin.h new file mode 100644 index 000000000..77242754d --- /dev/null +++ b/src/accounts/twitter/twitterinfoplugin.h @@ -0,0 +1,80 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Dominik Schmidt + * Copyright 2012, Jeff Mitchell + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef TWITTERINFOPLUGIN_H +#define TWITTERINFOPLUGIN_H + +#include "infosystem/infosystem.h" +#include "accounts/twitter/tomahawkoauthtwitter.h" + +#include +#include +#include + +namespace Tomahawk { + + namespace Accounts { + class TwitterAccount; + } + + namespace InfoSystem { + + class TwitterInfoPlugin : public InfoPlugin + { + Q_OBJECT + + public: + TwitterInfoPlugin( Tomahawk::Accounts::TwitterAccount* account ); + virtual ~TwitterInfoPlugin(); + + public slots: + void init(); + void notInCacheSlot( const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ) + { + Q_UNUSED( criteria ); + Q_UNUSED( requestData ); + } + + protected slots: + void pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ); + void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) + { + Q_UNUSED( requestData ); + } + + private slots: + void connectAuthVerifyReply( const QTweetUser &user ); + void postLovedStatusUpdateReply( const QTweetStatus& status ); + void postLovedStatusUpdateError( QTweetNetBase::ErrorCode code, const QString& errorMsg ); + + private: + bool refreshTwitterAuth(); + bool isValid() const; + + Tomahawk::Accounts::TwitterAccount* m_account; + QWeakPointer< TomahawkOAuthTwitter > m_twitterAuth; + }; + + } + +} + +#endif // TWITTERINFOPLUGIN_H + +struct A; diff --git a/src/accounts/xmpp/XmppInfoPlugin.cpp b/src/accounts/xmpp/XmppInfoPlugin.cpp index 728ba4cf5..b1d822a68 100644 --- a/src/accounts/xmpp/XmppInfoPlugin.cpp +++ b/src/accounts/xmpp/XmppInfoPlugin.cpp @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2012, Dominik Schmidt + * Copyright 2012, Jeff Mitchell * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,31 +24,18 @@ #include "sip/xmppsip.h" #include "utils/logger.h" -#include -#include -#include - -#include // remove now playing status after PAUSE_TIMEOUT seconds -static const int PAUSE_TIMEOUT = 60; +static const int PAUSE_TIMEOUT = 10; -Tomahawk::InfoSystem::XmppInfoPlugin::XmppInfoPlugin(XmppSipPlugin* sipPlugin) +Tomahawk::InfoSystem::XmppInfoPlugin::XmppInfoPlugin( XmppSipPlugin* sipPlugin ) : m_sipPlugin( sipPlugin ) - , m_pubSubManager( 0 ) , m_pauseTimer( this ) { Q_ASSERT( sipPlugin->m_client ); m_supportedPushTypes << InfoNowPlaying << InfoNowPaused << InfoNowResumed << InfoNowStopped; - m_pubSubManager = new Jreen::PubSub::Manager( sipPlugin->m_client ); - m_pubSubManager->addEntityType< Jreen::Tune >(); - - // Clear status - Jreen::Tune::Ptr tune( new Jreen::Tune() ); - m_pubSubManager->publishItems(QList() << tune, Jreen::JID()); - m_pauseTimer.setSingleShot( true ); connect( &m_pauseTimer, SIGNAL( timeout() ), this, SLOT( audioStopped() ) ); @@ -56,24 +44,28 @@ Tomahawk::InfoSystem::XmppInfoPlugin::XmppInfoPlugin(XmppSipPlugin* sipPlugin) Tomahawk::InfoSystem::XmppInfoPlugin::~XmppInfoPlugin() { - //Note: the next two lines don't currently work, because the deletion wipes out internally posted events, need to talk to euro about a fix - Jreen::Tune::Ptr tune( new Jreen::Tune() ); - m_pubSubManager->publishItems(QList() << tune, Jreen::JID()); - m_pubSubManager->deleteLater(); +} + + +void +Tomahawk::InfoSystem::XmppInfoPlugin::init() +{ + if ( QThread::currentThread() != Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() ) + { + QMetaObject::invokeMethod( this, "init", Qt::QueuedConnection ); + return; + } + + if ( m_sipPlugin.isNull() ) + return; + + connect( this, SIGNAL( publishTune( QUrl, Tomahawk::InfoSystem::InfoStringHash ) ), m_sipPlugin.data(), SLOT( publishTune( QUrl, Tomahawk::InfoSystem::InfoStringHash ) ), Qt::QueuedConnection ); } void Tomahawk::InfoSystem::XmppInfoPlugin::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) { - tDebug() << Q_FUNC_INFO << m_sipPlugin->m_client->jid().full(); - - if( m_sipPlugin->m_account->configuration().value("publishtracks").toBool() == false ) - { - tDebug() << Q_FUNC_INFO << m_sipPlugin->m_client->jid().full() << "Not publishing now playing info (disabled in account config)"; - return; - } - switch ( pushData.type ) { case InfoNowPlaying: @@ -108,8 +100,7 @@ Tomahawk::InfoSystem::XmppInfoPlugin::audioStarted( const Tomahawk::InfoSystem:: QVariantMap map = pushInfoPair.second.toMap(); if ( map.contains( "private" ) && map[ "private" ] == TomahawkSettings::FullyPrivate ) { - Jreen::Tune::Ptr tune( new Jreen::Tune() ); - m_pubSubManager->publishItems( QList() << tune, Jreen::JID() ); + emit publishTune( QUrl(), Tomahawk::InfoSystem::InfoStringHash() ); return; } @@ -120,42 +111,25 @@ Tomahawk::InfoSystem::XmppInfoPlugin::audioStarted( const Tomahawk::InfoSystem:: } Tomahawk::InfoSystem::InfoStringHash info = map[ "trackinfo" ].value< Tomahawk::InfoSystem::InfoStringHash >(); - tDebug() << Q_FUNC_INFO << m_sipPlugin->m_client->jid().full() << info; - - Jreen::Tune::Ptr tune( new Jreen::Tune() ); - tune->setTitle( info.value( "title" ) ); - tune->setArtist( info.value( "artist" ) ); - tune->setLength( info.value("duration").toInt() ); - tune->setTrack( info.value("albumpos") ); + QUrl url; if ( pushInfoPair.first.contains( "shorturl" ) ) - tune->setUri( pushInfoPair.first[ "shorturl" ].toUrl() ); + url = pushInfoPair.first[ "shorturl" ].toUrl(); else - tune->setUri( GlobalActionManager::instance()->openLink( info.value( "title" ), info.value( "artist" ), info.value( "album" ) ) ); + url = GlobalActionManager::instance()->openLink( info.value( "title" ), info.value( "artist" ), info.value( "album" ) ); - tDebug() << Q_FUNC_INFO << "Setting URI of " << tune->uri().toString(); - //TODO: provide a rating once available in Tomahawk - tune->setRating( 10 ); - - //TODO: it would be nice to set Spotify, Dilandau etc here, but not the jabber ids of friends - tune->setSource( "Tomahawk" ); - - m_pubSubManager->publishItems( QList() << tune, Jreen::JID() ); + emit publishTune( url, info ); } void Tomahawk::InfoSystem::XmppInfoPlugin::audioPaused() { - tDebug() << Q_FUNC_INFO << m_sipPlugin->m_client->jid().full(); } void Tomahawk::InfoSystem::XmppInfoPlugin::audioStopped() { - tDebug() << Q_FUNC_INFO << m_sipPlugin->m_client->jid().full(); - - Jreen::Tune::Ptr tune( new Jreen::Tune() ); - m_pubSubManager->publishItems(QList() << tune, Jreen::JID()); + emit publishTune( QUrl(), Tomahawk::InfoSystem::InfoStringHash() ); } diff --git a/src/accounts/xmpp/XmppInfoPlugin.h b/src/accounts/xmpp/XmppInfoPlugin.h index 223167524..c15f1571f 100644 --- a/src/accounts/xmpp/XmppInfoPlugin.h +++ b/src/accounts/xmpp/XmppInfoPlugin.h @@ -1,6 +1,7 @@ /* === This file is part of Tomahawk Player - === * * Copyright 2012, Dominik Schmidt + * Copyright 2012, Jeff Mitchell * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,12 +24,6 @@ #include -namespace Jreen { - namespace PubSub { - class Manager; - } -} - class XmppSipPlugin; namespace Tomahawk { @@ -43,7 +38,11 @@ namespace Tomahawk { XmppInfoPlugin(XmppSipPlugin* parent); virtual ~XmppInfoPlugin(); + signals: + void publishTune( QUrl url, Tomahawk::InfoSystem::InfoStringHash trackInfo ); + public slots: + void init(); void notInCacheSlot( const Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); protected slots: @@ -56,8 +55,7 @@ namespace Tomahawk { void audioPaused(); private: - XmppSipPlugin* m_sipPlugin; - Jreen::PubSub::Manager* m_pubSubManager; + QWeakPointer< XmppSipPlugin > m_sipPlugin; QTimer m_pauseTimer; }; diff --git a/src/accounts/xmpp/sip/xmppsip.cpp b/src/accounts/xmpp/sip/xmppsip.cpp index 10fb265b4..a36d3fb1e 100644 --- a/src/accounts/xmpp/sip/xmppsip.cpp +++ b/src/accounts/xmpp/sip/xmppsip.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -86,11 +87,11 @@ JreenMessageHandler(QtMsgType type, const char *msg) XmppSipPlugin::XmppSipPlugin( Account *account ) : SipPlugin( account ) - , m_infoPlugin( 0 ) , m_state( Account::Disconnected ) #ifndef ENABLE_HEADLESS , m_menu( 0 ) , m_xmlConsole( 0 ) + , m_pubSubManager( 0 ) #endif { Jreen::Logger::addHandler( JreenMessageHandler ); @@ -161,11 +162,22 @@ XmppSipPlugin::XmppSipPlugin( Account *account ) #ifndef ENABLE_HEADLESS connect(m_avatarManager, SIGNAL(newAvatar(QString)), SLOT(onNewAvatar(QString))); #endif + + m_pubSubManager = new Jreen::PubSub::Manager( m_client ); + m_pubSubManager->addEntityType< Jreen::Tune >(); + + // Clear status + Jreen::Tune::Ptr tune( new Jreen::Tune() ); + m_pubSubManager->publishItems(QList() << tune, Jreen::JID()); + } XmppSipPlugin::~XmppSipPlugin() { - delete m_infoPlugin; + //Note: the next two lines don't currently work, because the deletion wipes out internally posted events, need to talk to euro about a fix + Jreen::Tune::Ptr tune( new Jreen::Tune() ); + m_pubSubManager->publishItems(QList() << tune, Jreen::JID()); + delete m_pubSubManager; delete m_avatarManager; delete m_roster; #ifndef ENABLE_HEADLESS @@ -175,10 +187,13 @@ XmppSipPlugin::~XmppSipPlugin() } -InfoSystem::InfoPlugin* +InfoSystem::InfoPluginPtr XmppSipPlugin::infoPlugin() { - return m_infoPlugin; + if ( m_infoPlugin.isNull() ) + m_infoPlugin = QWeakPointer< Tomahawk::InfoSystem::XmppInfoPlugin >( new Tomahawk::InfoSystem::XmppInfoPlugin( this ) ); + + return InfoSystem::InfoPluginPtr( m_infoPlugin.data() ); } @@ -241,6 +256,8 @@ XmppSipPlugin::disconnectPlugin() m_peers.clear(); + publishTune( QUrl(), Tomahawk::InfoSystem::InfoStringHash() ); + m_client->disconnectFromServer( true ); m_state = Account::Disconnecting; emit stateChanged( m_state ); @@ -272,10 +289,11 @@ XmppSipPlugin::onConnect() m_roster->load(); // load XmppInfoPlugin - if( !m_infoPlugin ) + if ( infoPlugin() && Tomahawk::InfoSystem::InfoSystem::instance()->workerThread() ) { - m_infoPlugin = new Tomahawk::InfoSystem::XmppInfoPlugin( this ); - InfoSystem::InfoSystem::instance()->addInfoPlugin( m_infoPlugin ); + infoPlugin().data()->moveToThread( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().data() ); + Tomahawk::InfoSystem::InfoSystem::instance()->addInfoPlugin( infoPlugin() ); + QMetaObject::invokeMethod( infoPlugin().data(), "init", Qt::QueuedConnection ); } //FIXME: this implementation is totally broken atm, so it's disabled to avoid harm :P @@ -340,6 +358,9 @@ XmppSipPlugin::onDisconnect( Jreen::Client::DisconnectReason reason ) { handlePeerStatus(peer, Jreen::Presence::Unavailable); } + + if ( !m_infoPlugin.isNull() ) + Tomahawk::InfoSystem::InfoSystem::instance()->removeInfoPlugin( infoPlugin() ); } @@ -509,6 +530,41 @@ XmppSipPlugin::showAddFriendDialog() } +void +XmppSipPlugin::publishTune( const QUrl& url, const InfoSystem::InfoStringHash& trackInfo ) +{ + if( m_account->configuration().value("publishtracks").toBool() == false ) + { + tDebug() << Q_FUNC_INFO << m_client->jid().full() << "Not publishing now playing info (disabled in account config)"; + return; + } + + if ( trackInfo.isEmpty() ) + { + Jreen::Tune::Ptr tune( new Jreen::Tune() ); + m_pubSubManager->publishItems(QList() << tune, Jreen::JID()); + } + + Jreen::Tune::Ptr tune( new Jreen::Tune() ); + + tune->setTitle( trackInfo.value( "title" ) ); + tune->setArtist( trackInfo.value( "artist" ) ); + tune->setLength( trackInfo.value("duration").toInt() ); + tune->setTrack( trackInfo.value("albumpos") ); + + //TODO: provide a rating once available in Tomahawk + tune->setRating( 10 ); + + //TODO: it would be nice to set Spotify, Dilandau etc here, but not the jabber ids of friends + tune->setSource( "Tomahawk" ); + + tune->setUri( url ); + tDebug() << Q_FUNC_INFO << "Setting URI of " << tune->uri().toString(); + + m_pubSubManager->publishItems( QList() << tune, Jreen::JID() ); +} + + QString XmppSipPlugin::defaultSuffix() const { diff --git a/src/accounts/xmpp/sip/xmppsip.h b/src/accounts/xmpp/sip/xmppsip.h index 33b7b61dd..1e570928d 100644 --- a/src/accounts/xmpp/sip/xmppsip.h +++ b/src/accounts/xmpp/sip/xmppsip.h @@ -42,6 +42,7 @@ #include #include #include +#include #ifndef ENABLE_HEADLESS #include @@ -67,7 +68,7 @@ public: //FIXME: Make this more correct virtual bool isValid() const { return true; } - Tomahawk::InfoSystem::InfoPlugin* infoPlugin(); + Tomahawk::InfoSystem::InfoPluginPtr infoPlugin(); #ifndef ENABLE_HEADLESS virtual QMenu* menu(); @@ -92,6 +93,7 @@ public slots: void broadcastMsg( const QString &msg ); virtual void addContact( const QString &jid, const QString& msg = QString() ); void showAddFriendDialog(); + void publishTune( const QUrl &url, const Tomahawk::InfoSystem::InfoStringHash &trackInfo ); protected: virtual QString defaultSuffix() const; @@ -131,7 +133,7 @@ private: int m_currentPort; QString m_currentResource; - Tomahawk::InfoSystem::InfoPlugin* m_infoPlugin; + QWeakPointer< Tomahawk::InfoSystem::XmppInfoPlugin> m_infoPlugin; Tomahawk::Accounts::Account::ConnectionState m_state; // sort out @@ -147,6 +149,7 @@ private: #endif enum IqContext { NoContext, RequestDisco, RequestedDisco, SipMessageSent, RequestedVCard, RequestVersion, RequestedVersion }; AvatarManager *m_avatarManager; + Jreen::PubSub::Manager* m_pubSubManager; }; #endif diff --git a/src/accounts/xmpp/xmppaccount.cpp b/src/accounts/xmpp/xmppaccount.cpp index aafabd7f7..1d40ae397 100644 --- a/src/accounts/xmpp/xmppaccount.cpp +++ b/src/accounts/xmpp/xmppaccount.cpp @@ -91,13 +91,13 @@ XmppAccount::saveConfig() } -InfoSystem::InfoPlugin* +InfoSystem::InfoPluginPtr XmppAccount::infoPlugin() { if( !m_xmppSipPlugin.isNull() ) return m_xmppSipPlugin.data()->infoPlugin(); - return 0; + return InfoSystem::InfoPluginPtr(); } diff --git a/src/accounts/xmpp/xmppaccount.h b/src/accounts/xmpp/xmppaccount.h index 7dde11c49..03037aacc 100644 --- a/src/accounts/xmpp/xmppaccount.h +++ b/src/accounts/xmpp/xmppaccount.h @@ -51,7 +51,7 @@ public: QString description() const { return tr( "Log on to your Jabber/XMPP account to connect to your friends" ); } QString factoryId() const { return "xmppaccount"; } QPixmap icon() const { return QPixmap( ":/xmpp-icon.png" ); } - AccountTypes types() const { return AccountTypes( SipType ); }; + AccountTypes types() const { return AccountTypes( SipType | StatusPushType ); }; Account* createAccount( const QString& pluginId = QString() ); }; @@ -69,7 +69,7 @@ public: void deauthenticate(); bool isAuthenticated() const; - Tomahawk::InfoSystem::InfoPlugin* infoPlugin(); + Tomahawk::InfoSystem::InfoPluginPtr infoPlugin(); SipPlugin* sipPlugin(); diff --git a/src/accounts/zeroconf/zeroconfaccount.h b/src/accounts/zeroconf/zeroconfaccount.h index 29a6673fd..4733cf22b 100644 --- a/src/accounts/zeroconf/zeroconfaccount.h +++ b/src/accounts/zeroconf/zeroconfaccount.h @@ -64,7 +64,7 @@ public: bool isAuthenticated() const; ConnectionState connectionState() const; - Tomahawk::InfoSystem::InfoPlugin* infoPlugin() { return 0; } + virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() { return Tomahawk::InfoSystem::InfoPluginPtr(); } SipPlugin* sipPlugin(); QWidget* configurationWidget() { return 0; } diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index b634f8026..230d4a792 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -135,6 +135,7 @@ set( libGuiSources widgets/playlisttypeselectordlg.cpp widgets/welcomewidget.cpp widgets/whatshotwidget.cpp + widgets/newreleaseswidget.cpp widgets/ChartDataLoader.cpp widgets/RecentlyPlayedPlaylistsModel.cpp widgets/RecentPlaylistsModel.cpp @@ -252,6 +253,7 @@ set( libSources infosystem/infoplugins/generic/echonestplugin.cpp infosystem/infoplugins/generic/chartsplugin.cpp + infosystem/infoplugins/generic/newreleasesplugin.cpp infosystem/infoplugins/generic/spotifyPlugin.cpp infosystem/infoplugins/generic/hypemPlugin.cpp infosystem/infoplugins/generic/musixmatchplugin.cpp @@ -290,6 +292,7 @@ set( libSources utils/logger.cpp utils/qnr_iodevicestream.cpp utils/xspfloader.cpp + utils/tomahawkcache.cpp thirdparty/kdsingleapplicationguard/kdsingleapplicationguard.cpp thirdparty/kdsingleapplicationguard/kdsharedmemorylocker.cpp @@ -303,6 +306,7 @@ set( libUI ${libUI} widgets/searchwidget.ui widgets/welcomewidget.ui widgets/whatshotwidget.ui + widgets/newreleaseswidget.ui widgets/SocialPlaylistWidget.ui widgets/infowidgets/sourceinfowidget.ui widgets/infowidgets/ArtistInfoWidget.ui diff --git a/src/libtomahawk/accounts/Account.cpp b/src/libtomahawk/accounts/Account.cpp index eda98702f..b51d7eab1 100644 --- a/src/libtomahawk/accounts/Account.cpp +++ b/src/libtomahawk/accounts/Account.cpp @@ -37,7 +37,7 @@ accountTypeToString( AccountType type ) case InfoType: case StatusPushType: return QObject::tr( "Status Updaters" ); - default: + case NoType: return QString(); } diff --git a/src/libtomahawk/accounts/Account.h b/src/libtomahawk/accounts/Account.h index 43e98ff67..66349a724 100644 --- a/src/libtomahawk/accounts/Account.h +++ b/src/libtomahawk/accounts/Account.h @@ -31,16 +31,13 @@ #include "dllmacro.h" #include "tomahawksettings.h" +#include "libtomahawk/infosystem/infosystem.h" + class SipPlugin; namespace Tomahawk { -namespace InfoSystem -{ - class InfoPlugin; -} - namespace Accounts { @@ -100,7 +97,7 @@ public: virtual QString errorMessage() const { QMutexLocker locker( &m_mutex ); return m_cachedError; } - virtual Tomahawk::InfoSystem::InfoPlugin* infoPlugin() = 0; + virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() = 0; virtual SipPlugin* sipPlugin() = 0; AccountTypes types() const; diff --git a/src/libtomahawk/accounts/AccountManager.cpp b/src/libtomahawk/accounts/AccountManager.cpp index eb1f7b43d..7c3f68035 100644 --- a/src/libtomahawk/accounts/AccountManager.cpp +++ b/src/libtomahawk/accounts/AccountManager.cpp @@ -51,14 +51,7 @@ AccountManager::AccountManager( QObject *parent ) { s_instance = this; - connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( onSettingsChanged() ) ); - - loadPluginFactories( findPluginFactories() ); - - // We include the resolver factory manually, not in a plugin - ResolverAccountFactory* f = new ResolverAccountFactory(); - m_accountFactories[ f->factoryId() ] = f; - registerAccountFactoryForFilesystem( f ); + QTimer::singleShot( 0, this, SLOT( init() ) ); } @@ -72,6 +65,29 @@ AccountManager::~AccountManager() } +void +AccountManager::init() +{ + if ( Tomahawk::InfoSystem::InfoSystem::instance()->workerThread().isNull() ) + { + //We need the info system worker to be alive so that we can move info plugins into its thread + QTimer::singleShot( 0, this, SLOT( init() ) ); + return; + } + + connect( TomahawkSettings::instance(), SIGNAL( changed() ), SLOT( onSettingsChanged() ) ); + + loadPluginFactories( findPluginFactories() ); + + // We include the resolver factory manually, not in a plugin + ResolverAccountFactory* f = new ResolverAccountFactory(); + m_accountFactories[ f->factoryId() ] = f; + registerAccountFactoryForFilesystem( f ); + + emit ready(); +} + + QStringList AccountManager::findPluginFactories() { @@ -180,6 +196,7 @@ AccountManager::loadPluginFactory( const QString& path ) void AccountManager::enableAccount( Account* account ) { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; if ( account->enabled() ) return; @@ -195,6 +212,7 @@ AccountManager::enableAccount( Account* account ) void AccountManager::disableAccount( Account* account ) { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; if ( !account->enabled() ) return; @@ -209,6 +227,7 @@ AccountManager::disableAccount( Account* account ) void AccountManager::connectAll() { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; foreach( Account* acc, m_accounts ) { acc->authenticate(); @@ -222,6 +241,7 @@ AccountManager::connectAll() void AccountManager::disconnectAll() { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; foreach( Account* acc, m_enabledAccounts ) acc->deauthenticate(); @@ -234,6 +254,7 @@ AccountManager::disconnectAll() void AccountManager::toggleAccountsConnected() { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; if ( m_connected ) disconnectAll(); else @@ -297,9 +318,6 @@ AccountManager::addAccount( Account* account ) if ( account->types() & Accounts::StatusPushType ) m_accountsByAccountType[ Accounts::StatusPushType ].append( account ); - if ( account->infoPlugin() ) - InfoSystem::InfoSystem::instance()->addInfoPlugin( account->infoPlugin() ); - emit added( account ); } @@ -370,6 +388,7 @@ AccountManager::hookupAccount( Account* account ) const void AccountManager::hookupAndEnable( Account* account, bool startup ) { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; SipPlugin* p = account->sipPlugin(); if ( p ) SipHandler::instance()->hookUpPlugin( p ); diff --git a/src/libtomahawk/accounts/AccountManager.h b/src/libtomahawk/accounts/AccountManager.h index 16dcf72fc..1e1820476 100644 --- a/src/libtomahawk/accounts/AccountManager.h +++ b/src/libtomahawk/accounts/AccountManager.h @@ -43,7 +43,7 @@ public: explicit AccountManager( QObject *parent ); virtual ~AccountManager(); - + void loadFromConfig(); void initSIP(); @@ -84,6 +84,8 @@ public slots: void toggleAccountsConnected(); signals: + void ready(); + void added( Tomahawk::Accounts::Account* ); void removed( Tomahawk::Accounts::Account* ); @@ -94,6 +96,7 @@ signals: void stateChanged( Account* p, Accounts::Account::ConnectionState state ); private slots: + void init(); void onStateChanged( Tomahawk::Accounts::Account::ConnectionState state ); void onError( int code, const QString& msg ); diff --git a/src/libtomahawk/accounts/ResolverAccount.h b/src/libtomahawk/accounts/ResolverAccount.h index b115d8fc4..4846d0e59 100644 --- a/src/libtomahawk/accounts/ResolverAccount.h +++ b/src/libtomahawk/accounts/ResolverAccount.h @@ -80,7 +80,7 @@ public: // Not relevant virtual QPixmap icon() const { return QPixmap(); } virtual SipPlugin* sipPlugin() { return 0; } - virtual Tomahawk::InfoSystem::InfoPlugin* infoPlugin() { return 0; } + virtual Tomahawk::InfoSystem::InfoPluginPtr infoPlugin() { return Tomahawk::InfoSystem::InfoPluginPtr(); } virtual QWidget* aclWidget() { return 0; } private slots: diff --git a/src/libtomahawk/audio/audioengine.cpp b/src/libtomahawk/audio/audioengine.cpp index 36681e8ab..cd2bc6041 100644 --- a/src/libtomahawk/audio/audioengine.cpp +++ b/src/libtomahawk/audio/audioengine.cpp @@ -327,8 +327,8 @@ AudioEngine::sendNowPlayingNotification( const Tomahawk::InfoSystem::InfoType ty onNowPlayingInfoReady( type ); else { - _detail::Closure* closure = NewClosure( m_currentTrack->album().data(), SIGNAL( updated() ), const_cast< AudioEngine* >( this ), SLOT( onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType ) ), type ); - m_currentTrack->album()->cover( QSize( 0, 0 ) ); + NewClosure( m_currentTrack->album().data(), SIGNAL( updated() ), const_cast< AudioEngine* >( this ), SLOT( onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType ) ), type ); + m_currentTrack->album()->cover( QSize( 0, 0 ), true ); } #endif } @@ -337,14 +337,11 @@ AudioEngine::sendNowPlayingNotification( const Tomahawk::InfoSystem::InfoType ty void AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type ) { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << type; if ( m_currentTrack.isNull() || m_currentTrack->track().isNull() || m_currentTrack->artist().isNull() ) return; - - if ( !m_currentTrack->album().isNull() && sender() && m_currentTrack->album().data() != sender() ) - return; QVariantMap playInfo; @@ -353,26 +350,29 @@ AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type ) #ifndef ENABLE_HEADLESS QImage cover; cover = m_currentTrack->album()->cover( QSize( 0, 0 ) ).toImage(); - playInfo["cover"] = cover; - - QTemporaryFile coverTempFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + m_currentTrack->artist()->name() + "_" + m_currentTrack->album()->name() + "_tomahawk_cover.png" ) ); - if ( !coverTempFile.open() ) + if ( !cover.isNull() ) { - tDebug() << "WARNING: could not write temporary file for cover art!"; - } + playInfo["cover"] = cover; - // Finally, save the image to the new temp file - if ( cover.save( &coverTempFile, "PNG" ) ) - { - tDebug( LOGVERBOSE ) << "Saving cover image to:" << QFileInfo( coverTempFile ).absoluteFilePath(); - coverTempFile.close(); - playInfo["coveruri"] = QFileInfo( coverTempFile ).absoluteFilePath(); + QTemporaryFile* coverTempFile = new QTemporaryFile( QDir::toNativeSeparators( QDir::tempPath() + "/" + m_currentTrack->artist()->name() + "_" + m_currentTrack->album()->name() + "_tomahawk_cover.png" ) ); + if ( !coverTempFile->open() ) + tDebug() << Q_FUNC_INFO << "WARNING: could not write temporary file for cover art!"; + else + { + // Finally, save the image to the new temp file + coverTempFile->setAutoRemove( false ); + if ( cover.save( coverTempFile, "PNG" ) ) + { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Saving cover image to:" << QFileInfo( *coverTempFile ).absoluteFilePath(); + playInfo["coveruri"] = QFileInfo( *coverTempFile ).absoluteFilePath(); + } + else + tDebug() << Q_FUNC_INFO << "failed to save cover image!"; + } + delete coverTempFile; } else - { - tDebug() << Q_FUNC_INFO << "failed to save cover image!"; - coverTempFile.close(); - } + tDebug() << Q_FUNC_INFO << "Cover from album is null!"; #endif } @@ -388,6 +388,7 @@ AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type ) Tomahawk::InfoSystem::InfoPushData pushData ( s_aeInfoIdentifier, type, playInfo, Tomahawk::InfoSystem::PushShortUrlFlag ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "pushing data with type " << type; Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( pushData ); } diff --git a/src/libtomahawk/globalactionmanager.cpp b/src/libtomahawk/globalactionmanager.cpp index 0c5ecc0f0..ffeb4dbc9 100644 --- a/src/libtomahawk/globalactionmanager.cpp +++ b/src/libtomahawk/globalactionmanager.cpp @@ -125,6 +125,7 @@ GlobalActionManager::openLink( const QString& title, const QString& artist, cons void GlobalActionManager::shortenLink( const QUrl& url, const QVariant& callbackObj ) { + tDebug() << Q_FUNC_INFO << "callbackObj is valid: " << ( callbackObj.isValid() ? "true" : "false" ); if ( QThread::currentThread() != thread() ) { qDebug() << "Reinvoking in correct thread:" << Q_FUNC_INFO; @@ -136,7 +137,7 @@ GlobalActionManager::shortenLink( const QUrl& url, const QVariant& callbackObj ) request.setUrl( url ); QNetworkReply *reply = TomahawkUtils::nam()->get( request ); - if ( !callbackObj.isValid() ) + if ( callbackObj.isValid() ) reply->setProperty( "callbackobj", callbackObj ); connect( reply, SIGNAL( finished() ), SLOT( shortenLinkRequestFinished() ) ); connect( reply, SIGNAL( error( QNetworkReply::NetworkError ) ), SLOT( shortenLinkRequestError( QNetworkReply::NetworkError ) ) ); @@ -1013,7 +1014,7 @@ GlobalActionManager::shortenLinkRequestFinished() } QVariant callbackObj; - if ( reply->property( "callbackobj" ).canConvert< QVariant >() && reply->property( "callbackobj" ).isValid() ) + if ( reply->property( "callbackobj" ).isValid() ) callbackObj = reply->property( "callbackobj" ); // Check for the redirect attribute, as this should be the shortened link diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp index cf330b695..56e005997 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.cpp @@ -1,5 +1,6 @@ /* === This file is part of Tomahawk Player - === * + * Copyright 2012, Casey Link * Copyright 2010-2011, Hugo Lindström * Copyright 2011, Leo Franchi * Copyright 2010-2011, Jeff Mitchell @@ -32,6 +33,7 @@ #include "tomahawksettings.h" #include "utils/tomahawkutils.h" #include "utils/logger.h" +#include "utils/tomahawkcache.h" #define CHART_URL "http://charts.tomahawk-player.org/" //#define CHART_URL "http://localhost:8080/" @@ -45,10 +47,17 @@ ChartsPlugin::ChartsPlugin() : InfoPlugin() , m_chartsFetchJobs( 0 ) { - /// Add resources here - m_chartResources << "billboard" << "itunes" << "rdio" << "wearehunted" << "ex.fm" << "soundcloudwall"; /// If you add resource, update version aswell - m_chartVersion = "2.1"; + m_chartVersion = "2.3"; + QVariantList source_qvarlist = TomahawkUtils::Cache::instance()->getData( "ChartsPlugin", "chart_sources" ).toList(); + foreach( const QVariant & source, source_qvarlist ) { + m_chartResources.append( source.toString() ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "fetched source from cache" << source.toString(); + + } + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "total sources" << m_chartResources.size() << source_qvarlist.size(); + if( m_chartResources.size() == 0 ) + fetchChartSourcesList( true ); m_supportedGetTypes << InfoChart << InfoChartCapabilities; } @@ -93,22 +102,24 @@ ChartsPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) { if( resource == hash["chart_source"] ) { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "get source" << resource; foundSource = true; } } if( !foundSource ) { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "no such source" << hash["chart_source"] << "(" << m_chartResources.size() << " total sources)"; dataError( requestData ); break; } } - fetchChart( requestData ); + fetchChartFromCache( requestData ); break; case InfoChartCapabilities: - fetchChartCapabilities( requestData ); + fetchChartCapabilitiesFromCache( requestData ); break; default: dataError( requestData ); @@ -117,11 +128,12 @@ ChartsPlugin::getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ) void -ChartsPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) +ChartsPlugin::fetchChartFromCache( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Could not convert requestData to InfoStringHash!"; dataError( requestData ); return; } @@ -140,12 +152,14 @@ ChartsPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ) /// Set the criterias for current chart criteria["chart_id"] = hash["chart_id"]; criteria["chart_source"] = hash["chart_source"]; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Checking cache for " << hash["chart_id"] << " from " << hash["chart_source"]; + emit getCachedInfo( criteria, 86400000, requestData ); } void -ChartsPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ) +ChartsPlugin::fetchChartCapabilitiesFromCache( Tomahawk::InfoSystem::InfoRequestData requestData ) { if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { @@ -157,6 +171,7 @@ ChartsPlugin::fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requ Tomahawk::InfoSystem::InfoStringHash criteria; criteria[ "InfoChartCapabilities" ] = "chartsplugin"; criteria[ "InfoChartVersion" ] = m_chartVersion; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Checking cache for " << "InfoChartCapabilities" << m_chartVersion; emit getCachedInfo( criteria, 864000000, requestData ); } @@ -168,53 +183,17 @@ ChartsPlugin::notInCacheSlot( QHash criteria, Tomahawk::InfoSy case InfoChart: { tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChart not in cache! Fetching..."; - - /// Fetch the chart, we need source and id - QUrl url = QUrl( QString( CHART_URL "source/%1/chart/%2" ).arg( criteria["chart_source"] ).arg( criteria["chart_id"] ) ); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Getting chart url" << url; - - QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) ); - reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); - - connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); + fetchChart( requestData, criteria["chart_source"], criteria["chart_id"] ); return; } case InfoChartCapabilities: { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChartCapabilities not in cache! Fetching..."; - // we never need to re-fetch - //if ( !m_allChartsMap.isEmpty() ) - // return; + fetchChartSourcesList( false ); + m_cachedRequests.append( requestData ); - /// Then get each chart from resource - - if ( !m_chartResources.isEmpty() && m_allChartsMap.isEmpty() ) - { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChart fetching possible resources"; - foreach ( QString resource, m_chartResources ) - { - QUrl url = QUrl( QString( CHART_URL "source/%1" ).arg( resource ) ); - QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) ); - reply->setProperty( "chart_resource", resource); - - tDebug() << "fetching:" << url; - connect( reply, SIGNAL( finished() ), SLOT( chartTypes() ) ); - - m_chartsFetchJobs++; - } - } - - if ( m_chartsFetchJobs > 0 ) - { - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChartCapabilities still fetching!"; - m_cachedRequests.append( requestData ); - return; - } - - emit info( requestData, m_allChartsMap ); return; } @@ -227,21 +206,94 @@ ChartsPlugin::notInCacheSlot( QHash criteria, Tomahawk::InfoSy } } +void +ChartsPlugin::fetchChartSourcesList( bool fetchOnlySourceList ) +{ + QUrl url = QUrl( QString( CHART_URL "charts" ) ); + QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) ); + reply->setProperty( "only_source_list", fetchOnlySourceList ); + + + tDebug() << "fetching:" << url; + connect( reply, SIGNAL( finished() ), SLOT( chartSourcesList() ) ); + +} void -ChartsPlugin::chartTypes() +ChartsPlugin::chartSourcesList() { - /// Get possible chart type for specificChartsPlugin: InfoChart types returned chart source - tDebug( LOGVERBOSE ) << "Got chart type result"; + tDebug( LOGVERBOSE ) << "Got chart sources list"; + QNetworkReply* reply = qobject_cast( sender() ); + + if ( reply->error() == QNetworkReply::NoError ) + { + QJson::Parser p; + bool ok; + const QVariantMap res = p.parse( reply, &ok ).toMap(); + const QVariantList sources = res.value( "sources" ).toList(); + + if ( !ok ) + { + tLog() << "Failed to parse sources" << p.errorString() << "On line" << p.errorLine(); + return; + } + + m_chartResources.clear(); + foreach(const QVariant &source, sources) { + m_chartResources << source.toString(); + + } + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "storing sources in cache" << m_chartResources; + TomahawkUtils::Cache::instance()->putData( "ChartsPlugin", 172800000 /* 2 days */, "chart_sources", m_chartResources ); + if( !reply->property("only_source_list" ).toBool() ) + fetchAllChartSources(); + } +} + +void ChartsPlugin::fetchAllChartSources() +{ + if ( !m_chartResources.isEmpty() && m_allChartsMap.isEmpty() ) + { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChart fetching source data"; + foreach ( QString source, m_chartResources ) + { + QUrl url = QUrl( QString( CHART_URL "charts/%1" ).arg( source ) ); + QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) ); + reply->setProperty( "chart_source", source); + + tDebug() << "fetching:" << url; + connect( reply, SIGNAL( finished() ), SLOT( chartsList() ) ); + + m_chartsFetchJobs++; + } + } +} + +void ChartsPlugin::fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData, const QString& source, const QString& chart_id) +{ + /// Fetch the chart, we need source and id + QUrl url = QUrl( QString( CHART_URL "charts/%1/%2" ).arg( source ).arg( chart_id ) ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "fetching: " << url; + + QNetworkReply* reply = TomahawkUtils::nam()->get( QNetworkRequest( url ) ); + reply->setProperty( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData >( requestData ) ); + + connect( reply, SIGNAL( finished() ), SLOT( chartReturned() ) ); +} + + + +void +ChartsPlugin::chartsList() +{ + tDebug( LOGVERBOSE ) << "Got chart list result"; QNetworkReply* reply = qobject_cast( sender() ); - if ( reply->error() == QNetworkReply::NoError ) { QJson::Parser p; bool ok; const QVariantMap res = p.parse( reply, &ok ).toMap(); - const QVariantMap chartObjs = res.value( "charts" ).toMap(); if ( !ok ) { @@ -251,7 +303,7 @@ ChartsPlugin::chartTypes() } /// Got types, append! - const QString source = res.value( "source" ).toString(); + const QString source = reply->property("chart_source").toString(); // We'll populate charts with the data from the server QVariantMap charts; @@ -267,7 +319,7 @@ ChartsPlugin::chartTypes() // WeAreHunted - Type - Artists - Chart Type // - Tracks - Chart Type QHash< QString, QVariantMap > extraType; - foreach( const QVariant& chartObj, chartObjs.values() ) + foreach( const QVariant& chartObj, res.values() ) { if( !chartObj.toMap().isEmpty() ) { @@ -352,7 +404,7 @@ ChartsPlugin::chartTypes() QList< InfoStringHash > trackCharts; QList< InfoStringHash > artistCharts; - foreach( const QVariant& chartObj, chartObjs.values() ) + foreach( const QVariant& chartObj, res.values() ) { if( !chartObj.toMap().isEmpty() ){ const QVariantMap chart = chartObj.toMap(); @@ -407,7 +459,7 @@ ChartsPlugin::chartTypes() } /// Add the possible charts and its types to breadcrumb -// tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "ADDING CHART TYPE TO CHARTS:" << chartName; + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "ADDING CHART TO CHARTS:" << chartName; QVariantMap defaultMap = m_allChartsMap.value( "defaults" ).value< QVariantMap >(); defaultMap[ source ] = defaultChain; m_allChartsMap[ "defaults" ] = defaultMap; @@ -427,6 +479,7 @@ ChartsPlugin::chartTypes() { emit info( request, m_allChartsMap ); // update cache + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Updating cache with " << m_allChartsMap.size() << "charts"; Tomahawk::InfoSystem::InfoStringHash criteria; criteria[ "InfoChartCapabilities" ] = "chartsplugin"; criteria[ "InfoChartVersion" ] = m_chartVersion; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h index 0bebb4629..352ac2a3a 100644 --- a/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/generic/chartsplugin.h @@ -52,10 +52,6 @@ public: void setChartType( ChartType type ) { m_chartType = type; } ChartType chartType() const { return m_chartType; } -public slots: - void chartReturned(); - void chartTypes(); - protected slots: virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); @@ -65,9 +61,39 @@ protected slots: Q_UNUSED( pushData ); } + /** + * Parses a QNetworkReply of a list of chart sources. + */ + void chartSourcesList(); + + /** + * Parses a QNetworkReply of a list of charts for a particular source + */ + void chartsList(); + + /** + * Parses a QNetworkReply for the chart data for a particular chart + */ + void chartReturned(); + private: - void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData ); - void fetchChartCapabilities( Tomahawk::InfoSystem::InfoRequestData requestData ); + /** + * Fetch list of chart sources (e.g., itunes, billboard) + * Populates the m_chartResources member. + */ + void fetchChartSourcesList( bool fetchOnlySourceList ); + /** + * Requests charts list for each chart source in m_chartResources + */ + void fetchAllChartSources(); + /** + * Fetches a specific chart from a particular source. + * Updates the cache. + */ + void fetchChart( Tomahawk::InfoSystem::InfoRequestData requestData, const QString& source, const QString& chart_id ); + + void fetchChartFromCache( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchChartCapabilitiesFromCache( Tomahawk::InfoSystem::InfoRequestData requestData ); void dataError( Tomahawk::InfoSystem::InfoRequestData requestData ); QStringList m_chartResources; diff --git a/src/libtomahawk/infosystem/infoplugins/generic/newreleasesplugin.cpp b/src/libtomahawk/infosystem/infoplugins/generic/newreleasesplugin.cpp new file mode 100644 index 000000000..0a4cb9338 --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/newreleasesplugin.cpp @@ -0,0 +1,356 @@ +#include "newreleasesplugin.h" + +#include +#include +#include +#include + +#include "album.h" +#include "chartsplugin_data_p.h" +#include "typedefs.h" +#include "audio/audioengine.h" +#include "tomahawksettings.h" +#include "utils/tomahawkutils.h" +#include "utils/logger.h" +#include "utils/tomahawkcache.h" + +#include +#include + +#define CHART_URL "http://charts.tomahawk-player.org/" +//#define CHART_URL "http://localhost:8080/" + +using namespace Tomahawk::InfoSystem; + +NewReleasesPlugin::NewReleasesPlugin() + : InfoPlugin() + , m_nrFetchJobs ( 0 ) +{ + m_nrVersion = "0"; + m_supportedGetTypes << InfoNewReleaseCapabilities << InfoNewRelease; + QVariantList source_qvarlist = TomahawkUtils::Cache::instance()->getData( "NewReleasesPlugin", "nr_sources" ).toList(); + foreach( const QVariant & source, source_qvarlist ) { + m_nrSources.append( source.toString() ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "fetched source from cache" << source.toString(); + + } + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "total sources" << m_nrSources.size() << source_qvarlist.size(); + if( m_nrSources.size() == 0 ) + fetchNRSourcesList( true ); +} + +NewReleasesPlugin::~NewReleasesPlugin() +{ + tDebug ( LOGVERBOSE ) << Q_FUNC_INFO; +} + +void NewReleasesPlugin::dataError ( InfoRequestData requestData ) +{ + emit info ( requestData, QVariant() ); + return; +} + +void NewReleasesPlugin::getInfo ( InfoRequestData requestData ) +{ +//qDebug() << Q_FUNC_INFO << requestData.caller; + //qDebug() << Q_FUNC_INFO << requestData.customData; + + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + bool foundSource = false; + + switch ( requestData.type ) { + case InfoNewRelease: + /// We need something to check if the request is actually ment to go to this plugin + if ( !hash.contains ( "nr_source" ) ) { + tDebug ( LOGVERBOSE ) << Q_FUNC_INFO << "Hash did not contain required param!"; + dataError ( requestData ); + break; + } else { + foreach ( QString resource, m_nrSources ) { + if ( resource == hash["nr_source"] ) { + foundSource = true; + } + } + + if ( !foundSource ) { + dataError ( requestData ); + break; + } + + } + fetchNRFromCache ( requestData ); + break; + + case InfoNewReleaseCapabilities: + fetchNRCapabilitiesFromCache ( requestData ); + break; + default: + dataError ( requestData ); + } +} + +void NewReleasesPlugin::fetchNRFromCache ( InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { + dataError ( requestData ); + return; + } + + InfoStringHash hash = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + Tomahawk::InfoSystem::InfoStringHash criteria; + + /// Each request needs to contain both a id and source + if ( !hash.contains ( "nr_id" ) && !hash.contains ( "nr_source" ) ) { + tDebug ( LOGVERBOSE ) << Q_FUNC_INFO << "Hash did not contain required params!"; + dataError ( requestData ); + return; + + } + /// Set the criterias for current chart + criteria["nr_id"] = hash["nr_id"]; + criteria["nr_source"] = hash["nr_source"]; + + emit getCachedInfo ( criteria, 86400000, requestData ); +} + +void NewReleasesPlugin::fetchNRCapabilitiesFromCache ( InfoRequestData requestData ) +{ + if ( !requestData.input.canConvert< Tomahawk::InfoSystem::InfoStringHash >() ) { + tDebug ( LOGVERBOSE ) << Q_FUNC_INFO << "Could not convert requestData to InfoStringHash!"; + dataError ( requestData ); + return; + } + + Tomahawk::InfoSystem::InfoStringHash criteria; + criteria[ "InfoNewReleaseCapabilities" ] = "newreleasesplugin"; + criteria[ "InfoNewReleaseVersion" ] = m_nrVersion; + emit getCachedInfo ( criteria, 864000000, requestData ); +} + +void NewReleasesPlugin::notInCacheSlot ( InfoStringHash criteria, InfoRequestData requestData ) +{ + switch ( requestData.type ) { + case InfoNewRelease: { + tDebug ( LOGVERBOSE ) << Q_FUNC_INFO << "InfoNewRelease not in cache! Fetching..."; + fetchNR ( requestData, criteria["nr_source"], criteria["nr_id"] ); + return; + + } + + case InfoNewReleaseCapabilities: { + tDebug ( LOGVERBOSE ) << Q_FUNC_INFO << "InfoChartCapabilities not in cache! Fetching..."; + fetchNRSourcesList( false ); + m_cachedRequests.append ( requestData ); + + return; + } + + default: { + tLog() << Q_FUNC_INFO << "Couldn't figure out what to do with this type of request after cache miss"; + emit info ( requestData, QVariant() ); + return; + } + } +} + +void NewReleasesPlugin::fetchNRSourcesList( bool fetchOnlySourcesList ) +{ + QUrl url = QUrl ( QString ( CHART_URL "newreleases" ) ); + QNetworkReply* reply = TomahawkUtils::nam()->get ( QNetworkRequest ( url ) ); + reply->setProperty( "only_source_list", fetchOnlySourcesList ); + + + tDebug() << "fetching:" << url; + connect ( reply, SIGNAL ( finished() ), SLOT ( nrSourcesList() ) ); + +} + +void NewReleasesPlugin::nrSourcesList() +{ + tDebug ( LOGVERBOSE ) << "Got newreleases sources list"; + QNetworkReply* reply = qobject_cast ( sender() ); + + if ( reply->error() == QNetworkReply::NoError ) { + QJson::Parser p; + bool ok; + const QVariantMap res = p.parse ( reply, &ok ).toMap(); + const QVariantList sources = res.value ( "sources" ).toList(); + + if ( !ok ) { + tLog() << "Failed to parse sources" << p.errorString() << "On line" << p.errorLine(); + return; + } + + m_nrSources.clear(); + foreach ( const QVariant &source, sources ) { + m_nrSources << source.toString(); + } + TomahawkUtils::Cache::instance()->putData( "NewReleasesPlugin", 172800000 /* 2 days */, "nr_sources", m_nrSources ); + if( !reply->property( "only_source_list" ).toBool() ) + fetchAllNRSources(); + } +} + +void NewReleasesPlugin::fetchAllNRSources() +{ + if ( !m_nrSources.isEmpty() && m_allNRsMap.isEmpty() ) { + tDebug ( LOGVERBOSE ) << Q_FUNC_INFO << "InfoNewRelease fetching source data"; + foreach ( QString source, m_nrSources ) { + QUrl url = QUrl ( QString ( CHART_URL "newreleases/%1" ).arg ( source ) ); + QNetworkReply* reply = TomahawkUtils::nam()->get ( QNetworkRequest ( url ) ); + reply->setProperty ( "nr_source", source ); + + tDebug() << "fetching:" << url; + connect ( reply, SIGNAL ( finished() ), SLOT ( nrList() ) ); + + m_nrFetchJobs++; + } + } +} + +void NewReleasesPlugin::fetchNR ( InfoRequestData requestData, const QString& source, const QString& nr_id ) +{ + /// Fetch the chart, we need source and id + QUrl url = QUrl ( QString ( CHART_URL "newreleases/%1/%2" ).arg ( source ).arg ( nr_id ) ); + tDebug ( LOGVERBOSE ) << Q_FUNC_INFO << "fetching: " << url; + + QNetworkReply* reply = TomahawkUtils::nam()->get ( QNetworkRequest ( url ) ); + reply->setProperty ( "requestData", QVariant::fromValue< Tomahawk::InfoSystem::InfoRequestData > ( requestData ) ); + + connect ( reply, SIGNAL ( finished() ), SLOT ( nrReturned() ) ); +} + +void NewReleasesPlugin::nrList() +{ + tDebug ( LOGVERBOSE ) << "Got newreleases list result"; + QNetworkReply* reply = qobject_cast ( sender() ); + + if ( reply->error() == QNetworkReply::NoError ) { + QJson::Parser p; + bool ok; + const QVariantMap res = p.parse ( reply, &ok ).toMap(); + + if ( !ok ) { + tLog() << "Failed to parse resources" << p.errorString() << "On line" << p.errorLine(); + + return; + } + + /// Got types, append! + const QString source = reply->property ( "nr_source" ).toString(); + + // We'll populate newreleases with the data from the server + QVariantMap newreleases; + QString nrName; + + // Building: + // [Source] - New Release + QList< InfoStringHash > albumNRs; + + foreach ( const QVariant &nrObj, res.values() ) { + if ( !nrObj.toMap().isEmpty() ) { + const QVariantMap nrMap = nrObj.toMap(); + const QString type = nrMap.value ( "type" ).toString(); + + InfoStringHash nr; + nr["id"] = nrMap.value ( "id" ).toString(); + nr["label"] = nrMap.value ( "name" ).toString(); + nr["date"] = nrMap.value ( "date" ).toString(); + + if ( type == "Album" ) { + nr[ "type" ] = "album"; + albumNRs.append ( nr ); + } else { + tLog() << "Unknown newrelease type " << type; + continue; + } + + } + } + if ( !albumNRs.isEmpty() ) + newreleases.insert ( tr ( "Albums" ), QVariant::fromValue< QList< Tomahawk::InfoSystem::InfoStringHash > > ( albumNRs ) ); + + /// @note For displaying purposes, upper the first letter + /// @note Remeber to lower it when fetching this! + nrName = source; + nrName[0] = nrName[0].toUpper(); + + tDebug ( LOGVERBOSE ) << Q_FUNC_INFO << "ADDING newrelease TO NRS:" << nrName; + QVariantMap defaultMap = m_allNRsMap.value ( "defaults" ).value< QVariantMap >(); + m_allNRsMap.insert ( nrName, QVariant::fromValue< QVariantMap > ( newreleases ) ); + + } else { + tLog() << "Error fetching charts:" << reply->errorString(); + } + + m_nrFetchJobs--; + if ( !m_cachedRequests.isEmpty() && m_nrFetchJobs == 0 ) { + foreach ( InfoRequestData request, m_cachedRequests ) { + emit info ( request, m_allNRsMap ); + // update cache + Tomahawk::InfoSystem::InfoStringHash criteria; + criteria[ "InfoNewReleaseCapabilities" ] = "newreleasesplugin"; + criteria[ "InfoNewReleaseVersion" ] = m_nrVersion; + emit updateCache ( criteria, 864000000, request.type, m_allNRsMap ); + } + m_cachedRequests.clear(); + } +} + +void NewReleasesPlugin::nrReturned() +{ + /// Chart request returned something! Woho + QNetworkReply* reply = qobject_cast ( sender() ); + QVariantMap returnedData; + + if ( reply->error() == QNetworkReply::NoError ) { + QJson::Parser p; + bool ok; + QVariantMap res = p.parse ( reply, &ok ).toMap(); + + if ( !ok ) { + tLog() << "Failed to parse json from chart lookup:" << p.errorString() << "On line" << p.errorLine(); + return; + } + + /// SO we have a result, parse it! + QVariantList albumList = res.value ( "list" ).toList(); + QList< Tomahawk::InfoSystem::InfoStringHash > newreleases; + + foreach( const QVariant & albumObj, albumList ) { + QVariantMap albumMap = albumObj.toMap(); + if(!albumMap.isEmpty()) { + const QString album = albumMap.value("album").toString(); + const QString artist = albumMap.value("artist").toString(); + const QString date = albumMap.value("date").toString(); + + Tomahawk::InfoSystem::InfoStringHash pair; + pair["artist"] = artist; + pair["album"] = album; + newreleases.append( pair ); + } + } + + tDebug() << "NewReleasesPlugin:" << "\tgot " << newreleases.size() << " albums"; + returnedData[ "albums" ] = QVariant::fromValue< QList< Tomahawk::InfoSystem::InfoStringHash > >( newreleases ); + returnedData[ "type" ] = "albums"; + Tomahawk::InfoSystem::InfoRequestData requestData = reply->property( "requestData" ).value< Tomahawk::InfoSystem::InfoRequestData >(); + + emit info( requestData, returnedData ); + // update cache + Tomahawk::InfoSystem::InfoStringHash criteria; + Tomahawk::InfoSystem::InfoStringHash origData = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >(); + criteria[ "nr_id" ] = origData[ "nr_id" ]; + criteria[ "nr_source" ] = origData[ "nr_source" ]; + emit updateCache( criteria, 86400000, requestData.type, returnedData ); + } else + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Network error in fetching newrelease:" << reply->url().toString(); +} + + + + + + + + diff --git a/src/libtomahawk/infosystem/infoplugins/generic/newreleasesplugin.h b/src/libtomahawk/infosystem/infoplugins/generic/newreleasesplugin.h new file mode 100644 index 000000000..7fcedbddc --- /dev/null +++ b/src/libtomahawk/infosystem/infoplugins/generic/newreleasesplugin.h @@ -0,0 +1,100 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Casey Link + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef NEWRELEASESPLUGIN_H +#define NEWRELEASESPLUGIN_H + +#include "infosystem/infosystem.h" +#include "infosystem/infosystemworker.h" +#include +#include + +class QNetworkReply; + +namespace Tomahawk +{ + +namespace InfoSystem +{ + +class NewReleasesPlugin : public InfoPlugin +{ + Q_OBJECT + +public: + NewReleasesPlugin(); + virtual ~NewReleasesPlugin(); + +protected slots: + virtual void getInfo( Tomahawk::InfoSystem::InfoRequestData requestData ); + virtual void notInCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, Tomahawk::InfoSystem::InfoRequestData requestData ); + + virtual void pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) + { + Q_UNUSED( pushData ); + } + + /** + * Parses a QNetworkReply of a list of newreleases sources. + */ + void nrSourcesList(); + + /** + * Parses a QNetworkReply of a list of newreleases from a particular source + */ + void nrList(); + + /** + * Parses a QNetworkReply for the newreleases data for a particular newrelease + */ + void nrReturned(); + +private: + /** + * Fetch list of newlreeases sources (e.g., rovi) + * Populates the m_nrSources member. + */ + void fetchNRSourcesList( bool fetchOnlySourcesList ); + /** + * Requests newrelease list for each source in m_chartSources + */ + void fetchAllNRSources(); + /** + * Fetches a specific newrelease from a particular source. + * Updates the cache. + */ + void fetchNR( Tomahawk::InfoSystem::InfoRequestData requestData, const QString& source, const QString& nr_id ); + void fetchNRFromCache( Tomahawk::InfoSystem::InfoRequestData requestData ); + void fetchNRCapabilitiesFromCache( Tomahawk::InfoSystem::InfoRequestData requestData ); + void dataError( Tomahawk::InfoSystem::InfoRequestData requestData ); + + QStringList m_nrSources; + QString m_nrVersion; + QList< InfoStringHash > m_newreleases; + //ChartType m_chartType; + QVariantMap m_allNRsMap; + uint m_nrFetchJobs; + QList< InfoRequestData > m_cachedRequests; + QHash< QString, QString > m_cachedCountries; + QWeakPointer< QNetworkAccessManager > m_nam; +}; + +} +} + +#endif // NEWRELEASESPLUGIN_H diff --git a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.cpp b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.cpp index 4cc973270..5c252f820 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.cpp +++ b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.cpp @@ -49,9 +49,9 @@ using namespace Tomahawk::InfoSystem; - FdoNotifyPlugin::FdoNotifyPlugin() : InfoPlugin() + , m_nowPlayingId( 0 ) { qDebug() << Q_FUNC_INFO; m_supportedPushTypes << InfoNotifyUser << InfoNowPlaying << InfoTrackUnresolved << InfoNowStopped; @@ -119,6 +119,7 @@ FdoNotifyPlugin::notifyUser( const QString &messageText ) void FdoNotifyPlugin::nowPlaying( const QVariant &input ) { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO; if ( !input.canConvert< QVariantMap >() ) return; @@ -135,23 +136,29 @@ FdoNotifyPlugin::nowPlaying( const QVariant &input ) .arg( hash[ "title" ] ) .arg( hash[ "artist" ] ) .arg( hash[ "album" ].isEmpty() ? QString() : QString( " %1" ).arg( tr( "on \"%1\"" ).arg( hash[ "album" ] ) ) ); - + + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "sending message" << messageText; + QDBusMessage message = QDBusMessage::createMethodCall( "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "Notify" ); QList arguments; arguments << QString( "Tomahawk" ); //app_name - arguments << quint32( 0 ); //notification_id + arguments << m_nowPlayingId; //notification_id arguments << QString(); //app_icon arguments << QString( "Tomahawk" ); //summary arguments << messageText; //body arguments << QStringList(); //actions QVariantMap dict; dict["desktop-entry"] = QString( "tomahawk" ); - if ( map.contains( "cover" ) && map[ "cover" ].canConvert< QImage >() ) - dict[ "image_data" ] = ImageConverter::variantForImage( map[ "cover" ].value< QImage >() ); + if ( map.contains( "coveruri" ) && map[ "coveruri" ].canConvert< QString >() ) + dict[ "image_data" ] = ImageConverter::variantForImage( QImage( map[ "coveruri" ].toString(), "PNG" ) ); else dict[ "image_data" ] = ImageConverter::variantForImage( QImage( RESPATH "icons/tomahawk-icon-128x128.png" ) ); arguments << dict; //hints arguments << qint32( -1 ); //expire_timeout message.setArguments( arguments ); - QDBusConnection::sessionBus().send( message ); + + const QDBusMessage &reply = QDBusConnection::sessionBus().call( message ); + const QVariantList &list = reply.arguments(); + if ( list.count() > 0 ) + m_nowPlayingId = list.at( 0 ).toInt(); } diff --git a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h index c12621ff3..bba7bc304 100644 --- a/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h +++ b/src/libtomahawk/infosystem/infoplugins/unix/fdonotifyplugin.h @@ -54,6 +54,8 @@ private: void notifyUser( const QString &messageText ); void nowPlaying( const QVariant &input ); + + quint32 m_nowPlayingId; }; } diff --git a/src/libtomahawk/infosystem/infosystem.cpp b/src/libtomahawk/infosystem/infosystem.cpp index c1f6c020b..2dca67a95 100644 --- a/src/libtomahawk/infosystem/infosystem.cpp +++ b/src/libtomahawk/infosystem/infosystem.cpp @@ -137,7 +137,7 @@ InfoSystem::init() bool InfoSystem::getInfo( const InfoRequestData &requestData ) { - qDebug() << Q_FUNC_INFO; + //qDebug() << Q_FUNC_INFO; if ( !m_inited || !m_infoSystemWorkerThreadController->worker() ) { init(); @@ -210,15 +210,67 @@ InfoSystem::pushInfo( const QString &caller, const InfoTypeMap &input, const Pus void -InfoSystem::addInfoPlugin( InfoPlugin* plugin ) +InfoSystem::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ) +{ + // Init is not complete (waiting for worker thread to start and create worker object) so keep trying till then + if ( !m_inited || !m_infoSystemWorkerThreadController->worker() ) + { + QMetaObject::invokeMethod( this, "addInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPluginPtr, plugin ) ); + return; + } + + if ( plugin.isNull() ) + { + tDebug() << Q_FUNC_INFO << "Given plugin is null!"; + return; + } + + if ( plugin.data()->thread() != m_infoSystemWorkerThreadController->worker()->thread() ) + { + Q_ASSERT( false ); + tDebug() << Q_FUNC_INFO << "The object must be moved to the worker thread first, see InfoSystem::workerThread()"; + return; + } + + tDebug() << Q_FUNC_INFO << plugin.data(); + QMetaObject::invokeMethod( m_infoSystemWorkerThreadController->worker(), "addInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPluginPtr, plugin ) ); +} + + +void +InfoSystem::removeInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ) { // Init is not complete (waiting for worker th read to start and create worker object) so keep trying till then if ( !m_inited || !m_infoSystemWorkerThreadController->worker() ) { - QMetaObject::invokeMethod( this, "addInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPlugin*, plugin ) ); + QMetaObject::invokeMethod( this, "removeInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPluginPtr, plugin ) ); return; } - QMetaObject::invokeMethod( m_infoSystemWorkerThreadController->worker(), "addInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPlugin*, plugin ) ); + + if ( plugin.isNull() ) + { + tDebug() << Q_FUNC_INFO << "Given plugin is null!"; + return; + } + + if ( plugin.data()->thread() != m_infoSystemWorkerThreadController->worker()->thread() ) + { + tDebug() << Q_FUNC_INFO << "The object must be moved to the worker thread first, see InfoSystem::workerThread()"; + return; + } + + tDebug() << Q_FUNC_INFO << plugin.data(); + QMetaObject::invokeMethod( m_infoSystemWorkerThreadController->worker(), "removeInfoPlugin", Qt::QueuedConnection, Q_ARG( Tomahawk::InfoSystem::InfoPluginPtr, plugin ) ); +} + + +QWeakPointer< QThread > +InfoSystem::workerThread() const +{ + if ( m_infoSystemWorkerThreadController->isRunning() && m_infoSystemWorkerThreadController->worker() ) + return QWeakPointer< QThread >( m_infoSystemWorkerThreadController->worker()->thread() ); + + return QWeakPointer< QThread >(); } diff --git a/src/libtomahawk/infosystem/infosystem.h b/src/libtomahawk/infosystem/infosystem.h index 9f0d8a811..bb0716525 100644 --- a/src/libtomahawk/infosystem/infosystem.h +++ b/src/libtomahawk/infosystem/infosystem.h @@ -112,6 +112,9 @@ enum InfoType { // as items are saved in cache, mark them here to not change the */ InfoChart = 51, + InfoNewReleaseCapabilities = 52, + InfoNewRelease = 53, + InfoMiscTopHotttness = 60, InfoMiscTopTerms = 61, @@ -195,7 +198,7 @@ struct InfoPushData { , pushFlags( pflags ) , infoPair( Tomahawk::InfoSystem::PushInfoPair( QVariantMap(), QVariant() ) ) {} - + }; @@ -282,9 +285,12 @@ public: bool pushInfo( InfoPushData pushData ); bool pushInfo( const QString &caller, const InfoTypeMap &input, const PushInfoFlags pushFlags ); + QWeakPointer< QThread > workerThread() const; + public slots: // InfoSystem takes ownership of InfoPlugins - void addInfoPlugin( Tomahawk::InfoSystem::InfoPlugin* plugin ); + void addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ); + void removeInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ); signals: void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); @@ -307,7 +313,6 @@ private: } - inline uint qHash( Tomahawk::InfoSystem::InfoStringHash hash ) { QCryptographicHash md5( QCryptographicHash::Md5 ); @@ -329,6 +334,7 @@ inline uint qHash( Tomahawk::InfoSystem::InfoStringHash hash ) return returnval; } + Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoRequestData ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoPushData ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoStringHash ); @@ -337,6 +343,7 @@ Q_DECLARE_METATYPE( Tomahawk::InfoSystem::PushInfoFlags ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoType ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoSystemCache* ); Q_DECLARE_METATYPE( QList< Tomahawk::InfoSystem::InfoStringHash > ); +Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoPluginPtr ); Q_DECLARE_METATYPE( Tomahawk::InfoSystem::InfoPlugin* ); #endif // TOMAHAWK_INFOSYSTEM_H diff --git a/src/libtomahawk/infosystem/infosystemcache.cpp b/src/libtomahawk/infosystem/infosystemcache.cpp index ac626ecd0..5a2503046 100644 --- a/src/libtomahawk/infosystem/infosystemcache.cpp +++ b/src/libtomahawk/infosystem/infosystemcache.cpp @@ -159,7 +159,7 @@ InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash criteri if ( !fileLocationHash.isEmpty() ) { //We already know of some values, so no need to re-read the directory again as it's already happened - qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash empty"; + //qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash empty"; notInCache( sendingObj, criteria, requestData ); return; } @@ -169,7 +169,7 @@ InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash criteri if ( !dir.exists() ) { //Dir doesn't exist so clearly not in cache - qDebug() << Q_FUNC_INFO << "notInCache -- dir doesn't exist"; + //qDebug() << Q_FUNC_INFO << "notInCache -- dir doesn't exist"; notInCache( sendingObj, criteria, requestData ); return; } @@ -186,7 +186,7 @@ InfoSystemCache::getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash criteri if ( !fileLocationHash.contains( criteriaHashVal ) ) { //Still didn't find it? It's really not in the cache then - qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash doesn't contain criteria val"; + //qDebug() << Q_FUNC_INFO << "notInCache -- filelocationhash doesn't contain criteria val"; notInCache( sendingObj, criteria, requestData ); return; } @@ -250,7 +250,7 @@ InfoSystemCache::notInCache( QObject *receiver, Tomahawk::InfoSystem::InfoString void InfoSystemCache::updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash criteria, qint64 maxAge, Tomahawk::InfoSystem::InfoType type, QVariant output ) { - qDebug() << Q_FUNC_INFO; + //qDebug() << Q_FUNC_INFO; const QString criteriaHashVal = criteriaMd5( criteria ); const QString criteriaHashValWithType = criteriaMd5( criteria, type ); const QString cacheDir = m_cacheBaseDir + QString::number( (int)type ); diff --git a/src/libtomahawk/infosystem/infosystemcache.h b/src/libtomahawk/infosystem/infosystemcache.h index aa2544816..591f21efb 100644 --- a/src/libtomahawk/infosystem/infosystemcache.h +++ b/src/libtomahawk/infosystem/infosystemcache.h @@ -58,7 +58,7 @@ private: void doUpgrade( uint oldVersion, uint newVersion ); void performWipe( QString directory ); const QString criteriaMd5( const Tomahawk::InfoSystem::InfoStringHash &criteria, Tomahawk::InfoSystem::InfoType type = Tomahawk::InfoSystem::InfoNoInfo ) const; - + QString m_cacheBaseDir; QHash< InfoType, QHash< QString, QString > > m_fileLocationCache; QTimer m_pruneTimer; diff --git a/src/libtomahawk/infosystem/infosystemworker.cpp b/src/libtomahawk/infosystem/infosystemworker.cpp index be7fb4f2b..cdefc1095 100644 --- a/src/libtomahawk/infosystem/infosystemworker.cpp +++ b/src/libtomahawk/infosystem/infosystemworker.cpp @@ -28,6 +28,7 @@ #include "infoplugins/generic/echonestplugin.h" #include "infoplugins/generic/musixmatchplugin.h" #include "infoplugins/generic/chartsplugin.h" +#include "infoplugins/generic/newreleasesplugin.h" #include "infoplugins/generic/spotifyPlugin.h" #include "infoplugins/generic/musicbrainzPlugin.h" #include "infoplugins/generic/hypemPlugin.h" @@ -82,36 +83,52 @@ InfoSystemWorker::init( Tomahawk::InfoSystem::InfoSystemCache* cache ) m_shortLinksWaiting = 0; m_cache = cache; #ifndef ENABLE_HEADLESS - addInfoPlugin( new EchoNestPlugin() ); - addInfoPlugin( new MusixMatchPlugin() ); - addInfoPlugin( new MusicBrainzPlugin() ); - addInfoPlugin( new ChartsPlugin() ); - addInfoPlugin( new RoviPlugin() ); - addInfoPlugin( new SpotifyPlugin() ); - addInfoPlugin( new hypemPlugin() ); + addInfoPlugin( InfoPluginPtr( new EchoNestPlugin() ) ); + addInfoPlugin( InfoPluginPtr( new MusixMatchPlugin() ) ); + addInfoPlugin( InfoPluginPtr( new MusicBrainzPlugin() ) ); + addInfoPlugin( InfoPluginPtr( new ChartsPlugin() ) ); + addInfoPlugin( InfoPluginPtr( new NewReleasesPlugin() ) ); + addInfoPlugin( InfoPluginPtr( new RoviPlugin() ) ); + addInfoPlugin( InfoPluginPtr( new SpotifyPlugin() ) ); + addInfoPlugin( InfoPluginPtr( new hypemPlugin() ) ); #endif #ifdef Q_WS_MAC - addInfoPlugin( new AdiumPlugin() ); + addInfoPlugin( InfoPluginPtr( new AdiumPlugin() ) ); #endif #ifndef ENABLE_HEADLESS #ifdef Q_WS_X11 - addInfoPlugin( new FdoNotifyPlugin() ); - addInfoPlugin( new MprisPlugin() ); + addInfoPlugin( InfoPluginPtr( new FdoNotifyPlugin() ) ); + addInfoPlugin( InfoPluginPtr( new MprisPlugin() ) ); #endif #endif } void -InfoSystemWorker::addInfoPlugin( InfoPlugin* plugin ) +InfoSystemWorker::addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ) { - InfoPluginPtr weakptr( plugin ); - m_plugins.append( weakptr ); - registerInfoTypes( weakptr, weakptr.data()->supportedGetTypes(), weakptr.data()->supportedPushTypes() ); + tDebug() << Q_FUNC_INFO << plugin; + foreach ( InfoPluginPtr ptr, m_plugins ) + { + if ( ptr == plugin ) + { + tDebug() << Q_FUNC_INFO << "This plugin is already added to the infosystem."; + return; + } + } + + if ( plugin.isNull() ) + { + tDebug() << Q_FUNC_INFO << "passed-in plugin is null"; + return; + } + + m_plugins.append( plugin ); + registerInfoTypes( plugin, plugin.data()->supportedGetTypes(), plugin.data()->supportedPushTypes() ); connect( - plugin, + plugin.data(), SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), this, SLOT( infoSlot( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), @@ -119,14 +136,14 @@ InfoSystemWorker::addInfoPlugin( InfoPlugin* plugin ) ); connect( - plugin, + plugin.data(), SIGNAL( getCachedInfo( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ), m_cache, SLOT( getCachedInfoSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoRequestData ) ), Qt::QueuedConnection ); connect( - plugin, + plugin.data(), SIGNAL( updateCache( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ), m_cache, SLOT( updateCacheSlot( Tomahawk::InfoSystem::InfoStringHash, qint64, Tomahawk::InfoSystem::InfoType, QVariant ) ), @@ -135,6 +152,32 @@ InfoSystemWorker::addInfoPlugin( InfoPlugin* plugin ) } +void +InfoSystemWorker::removeInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ) +{ + tDebug() << Q_FUNC_INFO << plugin; + + if ( plugin.isNull() ) + { + tDebug() << Q_FUNC_INFO << "passed-in plugin is null"; + return; + } + + foreach ( InfoPluginPtr ptr, m_plugins ) + { + if ( ptr == plugin ) + break; + + tDebug() << Q_FUNC_INFO << "This plugin does not exist in the infosystem."; + return; + } + + m_plugins.removeOne( plugin ); + deregisterInfoTypes( plugin, plugin.data()->supportedGetTypes(), plugin.data()->supportedPushTypes() ); + delete plugin.data(); +} + + void InfoSystemWorker::registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType >& getTypes, const QSet< InfoType >& pushTypes ) { @@ -145,6 +188,16 @@ InfoSystemWorker::registerInfoTypes( const InfoPluginPtr &plugin, const QSet< In } +void +InfoSystemWorker::deregisterInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType >& getTypes, const QSet< InfoType >& pushTypes ) +{ + Q_FOREACH( InfoType type, getTypes ) + m_infoGetMap[type].removeOne( plugin ); + Q_FOREACH( InfoType type, pushTypes ) + m_infoPushMap[type].removeOne( plugin ); +} + + QList< InfoPluginPtr > InfoSystemWorker::determineOrderedMatches( const InfoType type ) const { @@ -223,7 +276,7 @@ void InfoSystemWorker::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) { tDebug() << Q_FUNC_INFO << "type is " << pushData.type; - + if ( pushData.pushFlags != PushNoFlag ) { if ( pushData.pushFlags & PushShortUrlFlag ) @@ -234,6 +287,8 @@ InfoSystemWorker::pushInfo( Tomahawk::InfoSystem::InfoPushData pushData ) } } + tDebug() << Q_FUNC_INFO << "number of matching plugins: " << m_infoPushMap[ pushData.type ].size(); + Q_FOREACH( InfoPluginPtr ptr, m_infoPushMap[ pushData.type ] ) { if( ptr ) @@ -281,7 +336,7 @@ InfoSystemWorker::shortLinkReady( QUrl longUrl, QUrl shortUrl, QVariant callback m_shortLinksWaiting--; if ( !m_shortLinksWaiting ) disconnect( GlobalActionManager::instance(), SIGNAL( shortLinkReady( QUrl, QUrl, QVariant ) ) ); - + if ( !callbackObj.isValid() ) { tDebug() << Q_FUNC_INFO << "callback object was not valid, cannot continue"; diff --git a/src/libtomahawk/infosystem/infosystemworker.h b/src/libtomahawk/infosystem/infosystemworker.h index 6a51299b5..e949329ec 100644 --- a/src/libtomahawk/infosystem/infosystemworker.h +++ b/src/libtomahawk/infosystem/infosystemworker.h @@ -49,8 +49,6 @@ public: InfoSystemWorker(); ~InfoSystemWorker(); - void registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &getTypes, const QSet< InfoType > &pushTypes ); - signals: void info( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); void finished( QString target ); @@ -64,7 +62,8 @@ public slots: void infoSlot( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); - void addInfoPlugin( Tomahawk::InfoSystem::InfoPlugin* plugin ); + void addInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ); + void removeInfoPlugin( Tomahawk::InfoSystem::InfoPluginPtr plugin ); void getShortUrl( Tomahawk::InfoSystem::InfoPushData data ); void shortLinkReady( QUrl longUrl, QUrl shortUrl, QVariant callbackObj ); @@ -73,6 +72,8 @@ private slots: void checkTimeoutsTimerFired(); private: + void registerInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &getTypes, const QSet< InfoType > &pushTypes ); + void deregisterInfoTypes( const InfoPluginPtr &plugin, const QSet< InfoType > &getTypes, const QSet< InfoType > &pushTypes ); void checkFinished( const Tomahawk::InfoSystem::InfoRequestData &target ); QList< InfoPluginPtr > determineOrderedMatches( const InfoType type ) const; diff --git a/src/libtomahawk/playlist/artistview.cpp b/src/libtomahawk/playlist/artistview.cpp index 351c76d48..45f55ca19 100644 --- a/src/libtomahawk/playlist/artistview.cpp +++ b/src/libtomahawk/playlist/artistview.cpp @@ -384,7 +384,7 @@ ArtistView::onCustomContextMenu( const QPoint& pos ) m_contextMenu->setArtists( artists ); m_contextMenu->setAlbums( albums ); - m_contextMenu->exec( mapToGlobal( pos ) ); + m_contextMenu->exec( viewport()->mapToGlobal( pos ) ); } diff --git a/src/libtomahawk/playlist/trackview.cpp b/src/libtomahawk/playlist/trackview.cpp index 35ed48376..7939596b1 100644 --- a/src/libtomahawk/playlist/trackview.cpp +++ b/src/libtomahawk/playlist/trackview.cpp @@ -567,7 +567,7 @@ TrackView::onCustomContextMenu( const QPoint& pos ) } m_contextMenu->setQueries( queries ); - m_contextMenu->exec( mapToGlobal( pos ) ); + m_contextMenu->exec( viewport()->mapToGlobal( pos ) ); } diff --git a/src/libtomahawk/query.cpp b/src/libtomahawk/query.cpp index 0702cad40..2b9cabc86 100644 --- a/src/libtomahawk/query.cpp +++ b/src/libtomahawk/query.cpp @@ -588,9 +588,9 @@ Query::setLoved( bool loved ) trackInfo["album"] = album(); Tomahawk::InfoSystem::InfoPushData pushData ( id(), - Tomahawk::InfoSystem::InfoLove, + ( loved ? Tomahawk::InfoSystem::InfoLove : Tomahawk::InfoSystem::InfoUnLove ), QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( trackInfo ), - Tomahawk::InfoSystem::PushNoFlag ); + Tomahawk::InfoSystem::PushShortUrlFlag ); Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( pushData ); diff --git a/src/libtomahawk/utils/tomahawkcache.cpp b/src/libtomahawk/utils/tomahawkcache.cpp new file mode 100644 index 000000000..ac509a194 --- /dev/null +++ b/src/libtomahawk/utils/tomahawkcache.cpp @@ -0,0 +1,152 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Casey Link + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "tomahawkcache.h" + +#include "tomahawksettings.h" +#include "utils/logger.h" + +#include +#include +#include + +using namespace TomahawkUtils; + +Cache*Cache::s_instance = 0; + +Cache* Cache::instance() +{ + if ( !s_instance ) + s_instance = new Cache(); + + return s_instance; +} + +Cache::Cache() + : QObject ( 0 ) + , m_cacheBaseDir ( TomahawkSettings::instance()->storageCacheLocation() + "/GenericCache/" ) + , m_cacheManifest ( m_cacheBaseDir + "cachemanifest.ini", QSettings::IniFormat ) +{ + m_pruneTimer.setInterval ( 300000 ); + m_pruneTimer.setSingleShot ( false ); + connect ( &m_pruneTimer, SIGNAL ( timeout() ), SLOT ( pruneTimerFired() ) ); + m_pruneTimer.start(); +} + +Cache::~Cache() +{ + +} + +void Cache::pruneTimerFired() +{ + QMutexLocker mutex_locker( &m_mutex ); + + qDebug() << Q_FUNC_INFO << "Pruning tomahawkcache"; + qlonglong currentMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); + + QVariantList clients = m_cacheManifest.value ( "clients" ).toList(); + foreach ( const QVariant &client, clients ) { + const QString client_identifier = client.toString(); + const QString cache_dir = m_cacheBaseDir + client_identifier; + + QSettings cached_settings ( cache_dir, QSettings::IniFormat ); + const QStringList keys = cached_settings.allKeys(); + foreach ( const QString &key, keys ) { + CacheData data = cached_settings.value ( key ).value(); + if ( data.maxAge < currentMSecsSinceEpoch ) { + cached_settings.remove ( key ); + tLog() << Q_FUNC_INFO << "Removed stale entry: " << client_identifier << key; + } + } + cached_settings.sync(); + if ( cached_settings.allKeys().size() == 0 ) + removeClient ( client_identifier ); + } +} + + +QVariant Cache::getData ( const QString& identifier, const QString& key ) +{ + QMutexLocker mutex_locker( &m_mutex ); + + const QString cacheDir = m_cacheBaseDir + identifier; + QSettings cached_settings ( cacheDir, QSettings::IniFormat ); + + if ( cached_settings.contains ( key ) ) { + CacheData data = cached_settings.value ( key ).value(); + + if ( data.maxAge < QDateTime::currentMSecsSinceEpoch() ) { + cached_settings.remove ( key ); + tLog() << Q_FUNC_INFO << "Removed stale entry: " << identifier << key; + return QVariant(); + } + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Fetched data for" << identifier << key; + return data.data; + + } + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "No such client" << identifier; + return QVariant(); +} + +void Cache::putData ( const QString& identifier, qint64 maxAge, const QString& key, const QVariant& value ) +{ + QMutexLocker mutex_locker( &m_mutex ); + + const QString cacheDir = m_cacheBaseDir + identifier; + addClient ( identifier ); + QSettings cached_settings ( cacheDir, QSettings::IniFormat ); + cached_settings.setValue ( key, QVariant::fromValue ( CacheData ( QDateTime::currentMSecsSinceEpoch() + maxAge, value ) ) ); + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Storing from client " << identifier << maxAge << key << value; +} + +void Cache::addClient ( const QString& identifier ) +{ + QVariantList clients = m_cacheManifest.value ( "clients" ).toList(); + foreach ( const QVariant &client, clients ) { + const QString client_identifier = client.toString(); + if ( identifier == client_identifier ) return; + } + + tLog() << Q_FUNC_INFO << "adding client" << identifier; + clients.append ( identifier ); + m_cacheManifest.setValue ( "clients", clients ); + m_cacheManifest.sync(); +} + +void Cache::removeClient ( const QString& identifier ) +{ + QVariantList clients = m_cacheManifest.value ( "clients" ).toList(); + QVariantList::iterator it = clients.begin(); + while ( it != clients.end() ) { + const QString client_identifier = it->toString(); + if ( identifier == client_identifier ) { + tLog() << Q_FUNC_INFO << "removing client" << identifier; + clients.erase ( it ); + break; + } + ++it; + } + m_cacheManifest.setValue ( "clients", clients ); + m_cacheManifest.sync(); +} + + + + + diff --git a/src/libtomahawk/utils/tomahawkcache.h b/src/libtomahawk/utils/tomahawkcache.h new file mode 100644 index 000000000..3bd17885f --- /dev/null +++ b/src/libtomahawk/utils/tomahawkcache.h @@ -0,0 +1,122 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Casey Link + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef TOMAHAWKCACHE_H +#define TOMAHAWKCACHE_H + +#include "dllmacro.h" +#include "utils/tomahawkutils.h" + +#include +#include +#include +#include + +namespace TomahawkUtils { + +/** + * Internal data structure. Don't use. + */ +struct CacheData { + CacheData(){} + CacheData( qint64 maxAg, QVariant dat ) + : maxAge( maxAg ) + , data( dat ) + {} + + qint64 maxAge; //!< milliseconds + QVariant data; +}; + +/** + * A simple generic cache for anyone to use. + * + * Data is segmented according to clients, which specify + * a client identifier. + * + * Structure is a basic key-value store with associated max lifetime in + * milliseconds. + */ +class DLLEXPORT Cache : public QObject +{ +Q_OBJECT + +public: + static Cache* instance(); + virtual ~Cache(); + + /** + * Store data in the cache. + * @param identifier your unique identifier, used to segment your data. + * @param maxAge lifetime of data in milliseconds (e.g, 3600000 = 1 hour) + * @param key the key to store the data + * @param value the data to store + */ + void putData( const QString &identifier, qint64 maxAge, const QString &key, const QVariant& value ); + + /** + * Retrieve data from the cache. + * @param identifier your unique identifier, used to segment your data. + * @param key the key to store the data + * @return the data, if found, if not found an invalid QVariant is returned. + */ + QVariant getData( const QString &identifier, const QString &key ); + +private slots: + void pruneTimerFired(); + +private: + Cache(); + static Cache* s_instance; + + /** + * Adds a client to the manifest. + * Does not lock the mutex. + */ + void addClient( const QString &identifier ); + + /** + * Removes a client to the manifest. + * Does not lock the mutex. + */ + void removeClient( const QString &identifier ); + + QString m_cacheBaseDir; + QSettings m_cacheManifest; + QTimer m_pruneTimer; + QMutex m_mutex; +}; + +} + +inline QDataStream& operator<< ( QDataStream& in, const TomahawkUtils::CacheData& data ) +{ + in << data.data << data.maxAge; + return in; +} + +inline QDataStream& operator>> ( QDataStream& out, TomahawkUtils::CacheData& data ) +{ + out >> data.data; + out >> data.maxAge; + return out; +} + +Q_DECLARE_METATYPE( TomahawkUtils::CacheData ); + +#endif // TOMAHAWKCACHE_H diff --git a/src/libtomahawk/viewmanager.cpp b/src/libtomahawk/viewmanager.cpp index f9c939e45..3835cbed6 100644 --- a/src/libtomahawk/viewmanager.cpp +++ b/src/libtomahawk/viewmanager.cpp @@ -75,6 +75,7 @@ ViewManager::ViewManager( QObject* parent ) , m_widget( new QWidget() ) , m_welcomeWidget( new WelcomeWidget() ) , m_whatsHotWidget( new WhatsHotWidget() ) + , m_newReleasesWidget( new NewReleasesWidget() ) , m_topLovedWidget( 0 ) , m_recentPlaysWidget( 0 ) , m_currentMode( PlaylistInterface::Tree ) @@ -118,6 +119,7 @@ ViewManager::ViewManager( QObject* parent ) connect( m_infobar, SIGNAL( filterTextChanged( QString ) ), SLOT( setFilter( QString ) ) ); connect( this, SIGNAL( tomahawkLoaded() ), m_whatsHotWidget, SLOT( fetchData() ) ); + connect( this, SIGNAL( tomahawkLoaded() ), m_newReleasesWidget, SLOT( fetchData() ) ); connect( this, SIGNAL( tomahawkLoaded() ), m_welcomeWidget, SLOT( loadData() ) ); /* connect( m_infobar, SIGNAL( flatMode() ), SLOT( setTableMode() ) ); @@ -130,6 +132,7 @@ ViewManager::~ViewManager() { saveCurrentPlaylistSettings(); delete m_whatsHotWidget; + delete m_newReleasesWidget; delete m_welcomeWidget; delete m_topLovedWidget; delete m_recentPlaysWidget; @@ -437,6 +440,13 @@ ViewManager::showWhatsHotPage() } +Tomahawk::ViewPage* +ViewManager::showNewReleasesPage() +{ + return show( m_newReleasesWidget ); +} + + Tomahawk::ViewPage* ViewManager::showTopLovedPage() { diff --git a/src/libtomahawk/viewmanager.h b/src/libtomahawk/viewmanager.h index a62f18f74..fc37724cd 100644 --- a/src/libtomahawk/viewmanager.h +++ b/src/libtomahawk/viewmanager.h @@ -31,6 +31,7 @@ #include "viewpage.h" #include "widgets/welcomewidget.h" #include "widgets/whatshotwidget.h" +#include "widgets/newreleaseswidget.h" #include "dllmacro.h" @@ -91,6 +92,7 @@ public: Tomahawk::ViewPage* welcomeWidget() const { return m_welcomeWidget; } Tomahawk::ViewPage* whatsHotWidget() const { return m_whatsHotWidget; } + Tomahawk::ViewPage* newReleasesWidget() const { return m_newReleasesWidget; } Tomahawk::ViewPage* topLovedWidget() const { return m_topLovedWidget; } Tomahawk::ViewPage* recentPlaysWidget() const { return m_recentPlaysWidget; } ArtistView* superCollectionView() const { return m_superCollectionView; } @@ -139,6 +141,7 @@ public slots: Tomahawk::ViewPage* showSuperCollection(); Tomahawk::ViewPage* showWelcomePage(); Tomahawk::ViewPage* showWhatsHotPage(); + Tomahawk::ViewPage* showNewReleasesPage(); Tomahawk::ViewPage* showTopLovedPage(); Tomahawk::ViewPage* showRecentPlaysPage(); void showCurrentTrack(); @@ -202,6 +205,7 @@ private: QueueView* m_queue; WelcomeWidget* m_welcomeWidget; WhatsHotWidget* m_whatsHotWidget; + NewReleasesWidget* m_newReleasesWidget; Tomahawk::ViewPage* m_topLovedWidget; Tomahawk::ViewPage* m_recentPlaysWidget; diff --git a/src/libtomahawk/widgets/Breadcrumb.cpp b/src/libtomahawk/widgets/Breadcrumb.cpp index 975a5c2b0..fdccebd46 100644 --- a/src/libtomahawk/widgets/Breadcrumb.cpp +++ b/src/libtomahawk/widgets/Breadcrumb.cpp @@ -21,6 +21,7 @@ #include "BreadcrumbButton.h" #include "utils/stylehelper.h" #include "utils/logger.h" +#include "utils/tomahawkutilsgui.h" #include #include @@ -35,8 +36,7 @@ Breadcrumb::Breadcrumb( QWidget* parent, Qt::WindowFlags f ) , m_model( 0 ) , m_buttonlayout( new QHBoxLayout( this ) ) { - m_buttonlayout->setSpacing( 0 ); - m_buttonlayout->setMargin( 0 ); + TomahawkUtils::unmarginLayout( m_buttonlayout ); m_buttonlayout->setAlignment( Qt::AlignLeft ); setAutoFillBackground( true ); diff --git a/src/libtomahawk/widgets/BreadcrumbButton.cpp b/src/libtomahawk/widgets/BreadcrumbButton.cpp index 4b479c21e..12127538b 100644 --- a/src/libtomahawk/widgets/BreadcrumbButton.cpp +++ b/src/libtomahawk/widgets/BreadcrumbButton.cpp @@ -31,21 +31,67 @@ using namespace Tomahawk; +class BreadcrumbArrow : public QWidget +{ +public: + BreadcrumbArrow(QWidget* parent) : QWidget(parent) {} + +protected: + virtual void paintEvent( QPaintEvent* ) { + QPainter p( this ); + QStyleOption opt; + opt.initFrom( this ); + QRect r = rect(); + + const bool reverse = opt.direction == Qt::RightToLeft; + const int menuButtonWidth = 12; + const int rightSpacing = 10; + const int right = !reverse ? r.right() - rightSpacing : r.left() + menuButtonWidth; + const int height = r.height(); + + QLine l1( 1, 0, right, height / 2 ); + QLine l2( 1, height, right, height / 2 ); + + p.setRenderHint( QPainter::Antialiasing, true ); + + // Draw the shadow + QColor shadow( 0, 0, 0, 100 ); + p.translate( 0, 1 ); + p.setPen( shadow ); + p.drawLine( l1 ); + p.drawLine( l2 ); + + // Draw the main arrow + QColor foreGround( "#747474" ); + p.translate( 0, -1 ); + p.setPen( foreGround ); + p.drawLine( l1 ); + p.drawLine( l2 ); + } + virtual QSize sizeHint() const { + return QSize( 20, TomahawkUtils::headerHeight() ); + + } + +}; + BreadcrumbButton::BreadcrumbButton( Breadcrumb* parent, QAbstractItemModel* model ) : QWidget( parent ) , m_breadcrumb( parent ) , m_model( model ) , m_combo( new ComboBox( this ) ) + , m_arrow( new BreadcrumbArrow( this ) ) { setLayout( new QHBoxLayout ); - layout()->setContentsMargins( 0, 0, 0, 0 ); - layout()->setSpacing( 0 ); + + TomahawkUtils::unmarginLayout( layout() ); layout()->addWidget( m_combo ); + layout()->addWidget( m_arrow ); setFixedHeight( TomahawkUtils::headerHeight() ); m_combo->setSizeAdjustPolicy( QComboBox::AdjustToContents ); - setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding ); + setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Expanding ); connect( m_combo, SIGNAL( activated( int ) ), SLOT( comboboxActivated( int ) ) ); } @@ -55,44 +101,9 @@ void BreadcrumbButton::paintEvent( QPaintEvent* ) { QPainter p( this ); - QStyleOption opt; - opt.initFrom( this ); - QRect r = rect(); - StyleHelper::horizontalHeader( &p, r ); // draw the background - - if ( !hasChildren() ) - return; - - bool reverse = opt.direction == Qt::RightToLeft; - int menuButtonWidth = 12; - int rightSpacing = 10; - int left = !reverse ? r.right() - rightSpacing - menuButtonWidth : r.left(); - int right = !reverse ? r.right() - rightSpacing : r.left() + menuButtonWidth; - int height = r.height(); - QRect arrowRect( ( left + right ) / 2 + ( reverse ? 6 : -6 ), 0, height, height ); - - QStyleOption arrowOpt = opt; - arrowOpt.rect = arrowRect; - - QLine l1( left, 0, right, height / 2 ); - QLine l2( left, height, right, height / 2 ); - - p.setRenderHint( QPainter::Antialiasing, true ); - - // Draw the shadow - QColor shadow( 0, 0, 0, 100 ); - p.translate( 0, -1 ); - p.setPen( shadow ); - p.drawLine( l1 ); - p.drawLine( l2 ); - - // Draw the main arrow - QColor foreGround( "#747474" ); - p.translate( 0, 1 ); - p.setPen( foreGround ); - p.drawLine( l1 ); - p.drawLine( l2 ); + StyleHelper::horizontalHeader( &p, rect() ); // draw the background + m_arrow->setVisible( hasChildren() ); } diff --git a/src/libtomahawk/widgets/BreadcrumbButton.h b/src/libtomahawk/widgets/BreadcrumbButton.h index 9735bfff7..cf424e7e1 100644 --- a/src/libtomahawk/widgets/BreadcrumbButton.h +++ b/src/libtomahawk/widgets/BreadcrumbButton.h @@ -23,6 +23,7 @@ #include class ComboBox; +class BreadcrumbArrow; class QPaintEvent; namespace Tomahawk { @@ -63,6 +64,7 @@ private: QPersistentModelIndex m_parentIndex; QPersistentModelIndex m_curIndex; ComboBox* m_combo; + BreadcrumbArrow* m_arrow; }; } diff --git a/src/libtomahawk/widgets/headerbreadcrumb.cpp b/src/libtomahawk/widgets/headerbreadcrumb.cpp deleted file mode 100644 index a638164d0..000000000 --- a/src/libtomahawk/widgets/headerbreadcrumb.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2011, Casey Link - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "headerbreadcrumb.h" - -#include "utils/stylehelper.h" - -#include -#include -#include - -HeaderBreadCrumb::HeaderBreadCrumb( BreadcrumbButtonFactory* buttonFactory, QWidget* parent ) - : BreadcrumbBar( buttonFactory, parent ) -{ -} - - -HeaderBreadCrumb::HeaderBreadCrumb( QWidget* parent ) - : BreadcrumbBar( parent ) -{ -} - - -HeaderBreadCrumb::~HeaderBreadCrumb() -{ -} - - -void HeaderBreadCrumb::paintEvent( QPaintEvent* /* event */ ) -{ - QStylePainter p( this ); - StyleHelper::horizontalHeader( &p, rect() ); -} diff --git a/src/libtomahawk/widgets/headerbreadcrumb.h b/src/libtomahawk/widgets/headerbreadcrumb.h deleted file mode 100644 index 8b15ca5a8..000000000 --- a/src/libtomahawk/widgets/headerbreadcrumb.h +++ /dev/null @@ -1,41 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2011, Casey Link - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef HEADERBREADCRUMB_H -#define HEADERBREADCRUMB_H - -#include "breadcrumbbar.h" - -class QPaintEvent; - -/** - * \brief a bread crumb widget with Tomahawk's distinctive header style - */ -class HeaderBreadCrumb : public BreadcrumbBar -{ - Q_OBJECT - public: - HeaderBreadCrumb( BreadcrumbButtonFactory* buttonFactory, QWidget* parent = 0 ); - HeaderBreadCrumb( QWidget* parent = 0 ); - ~HeaderBreadCrumb(); - - protected: - virtual void paintEvent( QPaintEvent* event ); -}; - -#endif diff --git a/src/libtomahawk/widgets/newreleaseswidget.cpp b/src/libtomahawk/widgets/newreleaseswidget.cpp new file mode 100644 index 000000000..5d77d7404 --- /dev/null +++ b/src/libtomahawk/widgets/newreleaseswidget.cpp @@ -0,0 +1,368 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Casey Link + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi + * Copyright 2011, Jeff Mitchell + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "newreleaseswidget.h" +#include "whatshotwidget_p.h" +#include "ui_newreleaseswidget.h" + +#include +#include +#include + +#include "viewmanager.h" +#include "sourcelist.h" +#include "tomahawksettings.h" +#include "RecentPlaylistsModel.h" +#include "ChartDataLoader.h" + +#include "audio/audioengine.h" +#include "dynamic/GeneratorInterface.h" +#include "playlist/playlistmodel.h" +#include "playlist/treeproxymodel.h" +#include "playlist/PlaylistChartItemDelegate.h" +#include "widgets/overlaywidget.h" +#include "utils/tomahawkutils.h" +#include "utils/logger.h" +#include "pipeline.h" + +#define HISTORY_TRACK_ITEMS 25 +#define HISTORY_PLAYLIST_ITEMS 10 +#define HISTORY_RESOLVING_TIMEOUT 2500 + +using namespace Tomahawk; + +static QString s_newReleasesIdentifier = QString( "NewReleasesWidget" ); + + +NewReleasesWidget::NewReleasesWidget( QWidget* parent ) + : QWidget( parent ) + , ui( new Ui::NewReleasesWidget ) + , m_sortedProxy( 0 ) + , m_workerThread( 0 ) +{ + ui->setupUi( this ); + + ui->albumsView->setFrameShape( QFrame::NoFrame ); + ui->albumsView->setAttribute( Qt::WA_MacShowFocusRect, 0 ); + + TomahawkUtils::unmarginLayout( layout() ); + TomahawkUtils::unmarginLayout( ui->verticalLayout_2 ); + TomahawkUtils::unmarginLayout( ui->breadCrumbLeft->layout() ); + + m_crumbModelLeft = new QStandardItemModel( this ); + m_sortedProxy = new QSortFilterProxyModel( this ); + m_sortedProxy->setDynamicSortFilter( true ); + m_sortedProxy->setFilterCaseSensitivity( Qt::CaseInsensitive ); + + ui->breadCrumbLeft->setRootIcon( QPixmap( RESPATH "images/new-releases.png" ) ); + + connect( ui->breadCrumbLeft, SIGNAL( activateIndex( QModelIndex ) ), SLOT( leftCrumbIndexChanged(QModelIndex) ) ); + + + //m_playlistInterface = Tomahawk::playlistinterface_ptr( new ChartsPlaylistInterface( this ) ); + + m_workerThread = new QThread( this ); + m_workerThread->start(); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), + SIGNAL( info( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ), + SLOT( infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData, QVariant ) ) ); + + connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) ); +} + + +NewReleasesWidget::~NewReleasesWidget() +{ + qDeleteAll( m_workers ); + m_workers.clear(); + m_workerThread->exit(0); + m_playlistInterface.clear(); + delete ui; +} + + +Tomahawk::playlistinterface_ptr +NewReleasesWidget::playlistInterface() const +{ + return m_playlistInterface; +} + + +bool +NewReleasesWidget::isBeingPlayed() const +{ + return false; +} + + +bool +NewReleasesWidget::jumpToCurrentTrack() +{ + return false; +} + + +void +NewReleasesWidget::fetchData() +{ + Tomahawk::InfoSystem::InfoStringHash artistInfo; + + Tomahawk::InfoSystem::InfoRequestData requestData; + requestData.caller = s_newReleasesIdentifier; + requestData.customData = QVariantMap(); + requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( artistInfo ); + requestData.type = Tomahawk::InfoSystem::InfoNewReleaseCapabilities; + requestData.timeoutMillis = 20000; + requestData.allSources = true; + Tomahawk::InfoSystem::InfoSystem::instance()->getInfo( requestData ); + + tDebug( LOGVERBOSE ) << "NewReleases: requested InfoNewReleaseCapabilities"; +} + + +void +NewReleasesWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ) +{ + if ( requestData.caller != s_newReleasesIdentifier ) + return; + + if ( !output.canConvert< QVariantMap >() ) + { + tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "NewReleases: Could not parse output"; + return; + } + + QVariantMap returnedData = output.toMap(); + switch ( requestData.type ) + { + case InfoSystem::InfoNewReleaseCapabilities: + { + tLog() << "NewReleases: got InfoNewReleaseCapabilities"; + QStandardItem *rootItem= m_crumbModelLeft->invisibleRootItem(); + + foreach ( const QString label, returnedData.keys() ) + { + QStandardItem *childItem = parseNode( rootItem, label, returnedData[label] ); + rootItem->appendRow(childItem); + tLog() << "NewReleases: " << label; + } + + m_sortedProxy->setSourceModel( m_crumbModelLeft ); + m_sortedProxy->sort( 0, Qt::AscendingOrder ); + ui->breadCrumbLeft->setModel( m_sortedProxy ); + break; + } + + case InfoSystem::InfoNewRelease: + { + if( !returnedData.contains("type") ) + break; + const QString type = returnedData["type"].toString(); + if( !returnedData.contains(type) ) + break; + const QString side = requestData.customData["whatshot_side"].toString(); + const QString releaseId = requestData.input.value< Tomahawk::InfoSystem::InfoStringHash >().value( "nr_id" ); + + m_queuedFetches.remove( releaseId ); + + ChartDataLoader* loader = new ChartDataLoader(); + loader->setProperty( "nrid", releaseId ); + loader->moveToThread( m_workerThread ); + + if ( type == "albums" ) + { + + loader->setType( ChartDataLoader::Album ); + loader->setData( returnedData[ "albums" ].value< QList< Tomahawk::InfoSystem::InfoStringHash > >() ); + + connect( loader, SIGNAL( albums( Tomahawk::ChartDataLoader*, QList< Tomahawk::album_ptr > ) ), this, SLOT( newReleasesLoaded( Tomahawk::ChartDataLoader*, QList ) ) ); + + AlbumModel* albumModel = new AlbumModel( ui->albumsView ); + + m_albumModels[ releaseId ] = albumModel; + + if ( m_queueItemToShow == releaseId ) + setLeftViewAlbums( albumModel ); + } + else + { + // intentionally unhandled + } + + QMetaObject::invokeMethod( loader, "go", Qt::QueuedConnection ); + + break; + } + + default: + return; + } +} + + +void +NewReleasesWidget::infoSystemFinished( QString target ) +{ + Q_UNUSED( target ); +} + + +void +NewReleasesWidget::leftCrumbIndexChanged( QModelIndex index ) +{ + tDebug( LOGVERBOSE ) << "NewReleases:: left crumb changed" << index.data(); + QStandardItem* item = m_crumbModelLeft->itemFromIndex( m_sortedProxy->mapToSource( index ) ); + if( !item ) + return; + if( !item->data( Breadcrumb::ChartIdRole ).isValid() ) + return; + + + QList indexes; + while ( index.parent().isValid() ) + { + indexes.prepend(index); + index = index.parent(); + } + + + const QString nrId = item->data( Breadcrumb::ChartIdRole ).toString(); + + if ( m_albumModels.contains( nrId ) ) + { + setLeftViewAlbums( m_albumModels[ nrId ] ); + return; + } + + if ( m_queuedFetches.contains( nrId ) ) + { + return; + } + + Tomahawk::InfoSystem::InfoStringHash criteria; + criteria.insert( "nr_id", nrId ); + /// Remember to lower the source! + criteria.insert( "nr_source", index.data().toString().toLower() ); + + Tomahawk::InfoSystem::InfoRequestData requestData; + QVariantMap customData; + customData.insert( "newrelease_side", "left" ); + requestData.caller = s_newReleasesIdentifier; + requestData.customData = customData; + requestData.input = QVariant::fromValue< Tomahawk::InfoSystem::InfoStringHash >( criteria ); + requestData.type = Tomahawk::InfoSystem::InfoNewRelease; + requestData.timeoutMillis = 20000; + requestData.allSources = true; + + qDebug() << "Making infosystem request for chart of type:" <getInfo( requestData ); + + m_queuedFetches.insert( nrId ); + m_queueItemToShow = nrId; +} + + +void +NewReleasesWidget::changeEvent( QEvent* e ) +{ + QWidget::changeEvent( e ); + switch ( e->type() ) + { + case QEvent::LanguageChange: + ui->retranslateUi( this ); + break; + + default: + break; + } +} + + +QStandardItem* +NewReleasesWidget::parseNode( QStandardItem* parentItem, const QString &label, const QVariant &data ) +{ + Q_UNUSED( parentItem ); +// tDebug( LOGVERBOSE ) << "NewReleases:: parsing " << label; + + QStandardItem *sourceItem = new QStandardItem(label); + + if ( data.canConvert< QList< Tomahawk::InfoSystem::InfoStringHash > >() ) + { + QList< Tomahawk::InfoSystem::InfoStringHash > charts = data.value< QList< Tomahawk::InfoSystem::InfoStringHash > >(); + foreach ( Tomahawk::InfoSystem::InfoStringHash chart, charts ) + { + QStandardItem *childItem= new QStandardItem( chart[ "label" ] ); + childItem->setData( chart[ "id" ], Breadcrumb::ChartIdRole ); + if ( chart.value( "default", "" ) == "true") + { + childItem->setData( true, Breadcrumb::DefaultRole ); + } + sourceItem->appendRow( childItem ); + } + } + else if ( data.canConvert() ) + { + QVariantMap dataMap = data.toMap(); + foreach ( const QString childLabel,dataMap.keys() ) + { + QStandardItem *childItem = parseNode( sourceItem, childLabel, dataMap[childLabel] ); + sourceItem->appendRow( childItem ); + } + } + else if ( data.canConvert() ) + { + QVariantList dataList = data.toList(); + + foreach ( const QVariant value, dataList ) + { + QStandardItem *childItem= new QStandardItem(value.toString()); + sourceItem->appendRow(childItem); + } + } + else + { + QStandardItem *childItem= new QStandardItem( data.toString() ); + sourceItem->appendRow( childItem ); + } + return sourceItem; +} + + +void +NewReleasesWidget::setLeftViewAlbums( AlbumModel* model ) +{ + ui->albumsView->setAlbumModel( model ); + ui->albumsView->proxyModel()->sort( -1 ); // disable sorting, must be called after artistsViewLeft->setTreeModel +} + +void +NewReleasesWidget::newReleasesLoaded( ChartDataLoader* loader, const QList< album_ptr >& albums ) +{ + QString chartId = loader->property( "nrid" ).toString(); + Q_ASSERT( m_albumModels.contains( chartId ) ); + + if ( m_albumModels.contains( chartId ) ) + m_albumModels[ chartId ]->addAlbums( albums ); + + m_workers.remove( loader ); + loader->deleteLater(); +} diff --git a/src/libtomahawk/widgets/newreleaseswidget.h b/src/libtomahawk/widgets/newreleaseswidget.h new file mode 100644 index 000000000..8223f7482 --- /dev/null +++ b/src/libtomahawk/widgets/newreleaseswidget.h @@ -0,0 +1,126 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012, Casey Link + * Copyright 2010-2011, Christian Muehlhaeuser + * Copyright 2011, Leo Franchi + * Copyright 2011, Jeff Mitchell + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#ifndef NEWRELEASESWIDGET_H +#define NEWRELEASESWIDGET_H + +#include +#include +#include + +#include "playlistinterface.h" +#include "infosystem/infosystem.h" +#include "viewpage.h" + +#include "utils/tomahawkutils.h" + +#include "dllmacro.h" + +class QSortFilterProxyModel; +class QStandardItemModel; +class QStandardItem; +class TreeModel; +class PlaylistModel; +class OverlayWidget; +class TreeProxyModel; +class AlbumModel; + +namespace Ui +{ + class NewReleasesWidget; +} + +namespace Tomahawk +{ + class ChartDataLoader; + class ChartsPlaylistInterface; + class ChartDataLoader; +} + +/** + * \class + * \brief The tomahawk page that shows music charts. + */ +class DLLEXPORT NewReleasesWidget : public QWidget, public Tomahawk::ViewPage +{ +Q_OBJECT + +public: + NewReleasesWidget( QWidget* parent = 0 ); + ~NewReleasesWidget(); + + virtual QWidget* widget() { return this; } + virtual Tomahawk::playlistinterface_ptr playlistInterface() const; + + virtual QString title() const { return tr( "New Releases" ); } + virtual QString description() const { return QString(); } + + virtual bool showStatsBar() const { return false; } + virtual bool showInfoBar() const { return false; } + + virtual bool jumpToCurrentTrack(); + virtual bool isBeingPlayed() const; + +protected: + void changeEvent( QEvent* e ); + +signals: + void destroyed( QWidget* widget ); + +public slots: + void fetchData(); + +private slots: + void infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output ); + void infoSystemFinished( QString target ); + void leftCrumbIndexChanged( QModelIndex ); + + void newReleasesLoaded( Tomahawk::ChartDataLoader*, const QList< Tomahawk::album_ptr >& ); + +private: + void setLeftViewArtists( TreeModel* artistModel ); + void setLeftViewAlbums( AlbumModel* albumModel ); + void setLeftViewTracks( PlaylistModel* trackModel ); + + + QStandardItem* parseNode( QStandardItem* parentItem, const QString &label, const QVariant &data ); + Ui::NewReleasesWidget *ui; + Tomahawk::playlistinterface_ptr m_playlistInterface; + + QStandardItemModel* m_crumbModelLeft; + QSortFilterProxyModel* m_sortedProxy; + + // Load artist, album, and track objects in a thread + // {Artist,Album,Track}::get() calls are all synchronous db calls + // and we don't want to lock up out UI in case the db is busy (e.g. on startup) + QThread* m_workerThread; + QSet< Tomahawk::ChartDataLoader* > m_workers; + + // Cache our model data + QHash< QString, AlbumModel* > m_albumModels; + QString m_queueItemToShow; + QSet< QString > m_queuedFetches; + QTimer* m_timer; + + friend class Tomahawk::ChartsPlaylistInterface; +}; + +#endif // NEWRELEASESWIDGET_H diff --git a/src/libtomahawk/widgets/newreleaseswidget.ui b/src/libtomahawk/widgets/newreleaseswidget.ui new file mode 100644 index 000000000..d6122d622 --- /dev/null +++ b/src/libtomahawk/widgets/newreleaseswidget.ui @@ -0,0 +1,45 @@ + + + NewReleasesWidget + + + + 0 + 0 + 875 + 513 + + + + + + + + + + + true + + + QAbstractItemView::ExtendedSelection + + + + + + + + AlbumView + QListView +
playlist/albumview.h
+
+ + Tomahawk::Breadcrumb + QWidget +
widgets/Breadcrumb.h
+ 1 +
+
+ + +
diff --git a/src/musicscanner.cpp b/src/musicscanner.cpp index 0edd230a4..06a0cf2c6 100644 --- a/src/musicscanner.cpp +++ b/src/musicscanner.cpp @@ -174,7 +174,6 @@ MusicScanner::scan() SLOT( commitBatch( QVariantList, QVariantList ) ), Qt::DirectConnection ); m_dirListerThreadController = new QThread( this ); - m_dirListerThreadController->setPriority( QThread::IdlePriority ); m_dirLister = QWeakPointer< DirLister >( new DirLister( m_dirs ) ); m_dirLister.data()->moveToThread( m_dirListerThreadController ); @@ -186,7 +185,7 @@ MusicScanner::scan() connect( m_dirLister.data(), SIGNAL( finished() ), SLOT( listerFinished() ), Qt::QueuedConnection ); - m_dirListerThreadController->start(); + m_dirListerThreadController->start( QThread::IdlePriority ); QMetaObject::invokeMethod( m_dirLister.data(), "go" ); } diff --git a/src/scanmanager.cpp b/src/scanmanager.cpp index c82438481..cf3c8a7bd 100644 --- a/src/scanmanager.cpp +++ b/src/scanmanager.cpp @@ -196,7 +196,6 @@ ScanManager::runDirScan() { m_scanTimer->stop(); m_musicScannerThreadController = new QThread( this ); - m_musicScannerThreadController->setPriority( QThread::IdlePriority ); m_scanner = QWeakPointer< MusicScanner >( new MusicScanner( paths ) ); m_scanner.data()->moveToThread( m_musicScannerThreadController ); connect( m_scanner.data(), SIGNAL( finished() ), SLOT( scannerFinished() ) ); diff --git a/src/sourcetree/sourcesmodel.cpp b/src/sourcetree/sourcesmodel.cpp index 7ee3b8b0c..5b0d96ce9 100644 --- a/src/sourcetree/sourcesmodel.cpp +++ b/src/sourcetree/sourcesmodel.cpp @@ -297,6 +297,11 @@ SourcesModel::appendGroups() boost::bind( &ViewManager::whatsHotWidget, ViewManager::instance() ) ); hot->setSortValue( 4 ); + GenericPageItem* newReleases = new GenericPageItem( this, browse, tr( "New Releases" ), QIcon( RESPATH "images/new-releases.png" ), + boost::bind( &ViewManager::showNewReleasesPage, ViewManager::instance() ), + boost::bind( &ViewManager::newReleasesWidget, ViewManager::instance() ) ); + newReleases->setSortValue( 5 ); + m_collectionsGroup = new GroupItem( this, m_rootItem, tr( "Friends" ), 4 ); endInsertRows(); @@ -320,7 +325,7 @@ SourcesModel::appendItem( const Tomahawk::source_ptr& source ) beginInsertRows( idx, rowCount( idx ), rowCount( idx ) ); new SourceItem( this, parent, source ); endInsertRows(); - + parent->checkExpandedState(); } diff --git a/src/stackedsettingsdialog.ui b/src/stackedsettingsdialog.ui index 63e3674a1..0500d69c6 100644 --- a/src/stackedsettingsdialog.ui +++ b/src/stackedsettingsdialog.ui @@ -393,7 +393,7 @@ Qt::RightToLeft - Allow web browsers to interact with Tomahawk + Allow web browsers to interact with Tomahawk (recommended) true diff --git a/src/tomahawkapp.cpp b/src/tomahawkapp.cpp index e4ad0d9a5..ec729071a 100644 --- a/src/tomahawkapp.cpp +++ b/src/tomahawkapp.cpp @@ -35,6 +35,8 @@ #include "collection.h" #include "infosystem/infosystem.h" #include "accounts/AccountManager.h" +#include "accounts/spotify/SpotifyAccount.h" +#include "accounts/lastfm/LastFmAccount.h" #include "database/database.h" #include "database/databasecollection.h" #include "database/databasecommand_collectionstats.h" @@ -64,6 +66,7 @@ #include "accounts/lastfm/LastFmAccount.h" #include "accounts/spotify/SpotifyAccount.h" #include "accounts/spotify/SpotifyPlaylistUpdater.h" +#include "utils/tomahawkcache.h" #include "config.h" @@ -229,15 +232,7 @@ TomahawkApp::init() tDebug() << "Init AccountManager."; m_accountManager = QWeakPointer< Tomahawk::Accounts::AccountManager >( new Tomahawk::Accounts::AccountManager( this ) ); - - Tomahawk::Accounts::LastFmAccountFactory* lastfmFactory = new Tomahawk::Accounts::LastFmAccountFactory(); - m_accountManager.data()->addAccountFactory( lastfmFactory ); - - Tomahawk::Accounts::SpotifyAccountFactory* spotifyFactory = new Tomahawk::Accounts::SpotifyAccountFactory; - m_accountManager.data()->addAccountFactory( spotifyFactory ); - m_accountManager.data()->registerAccountFactoryForFilesystem( spotifyFactory ); - - Tomahawk::Accounts::AccountManager::instance()->loadFromConfig(); + connect( m_accountManager.data(), SIGNAL( ready() ), SLOT( accountManagerReady() ) ); Echonest::Config::instance()->setNetworkAccessManager( TomahawkUtils::nam() ); #ifndef ENABLE_HEADLESS @@ -331,6 +326,7 @@ TomahawkApp::~TomahawkApp() delete m_audioEngine.data(); delete Tomahawk::Accounts::AccountManager::instance(); + delete TomahawkUtils::Cache::instance(); #ifndef ENABLE_HEADLESS delete m_mainwindow; @@ -452,6 +448,7 @@ TomahawkApp::registerMetaTypes() qRegisterMetaType< Tomahawk::InfoSystem::InfoRequestData >( "Tomahawk::InfoSystem::InfoRequestData" ); qRegisterMetaType< Tomahawk::InfoSystem::InfoPushData >( "Tomahawk::InfoSystem::InfoPushData" ); qRegisterMetaType< Tomahawk::InfoSystem::InfoSystemCache* >( "Tomahawk::InfoSystem::InfoSystemCache*" ); + qRegisterMetaType< Tomahawk::InfoSystem::InfoPluginPtr >( "Tomahawk::InfoSystem::InfoPluginPtr" ); qRegisterMetaType< Tomahawk::InfoSystem::InfoPlugin* >( "Tomahawk::InfoSystem::InfoPlugin*" ); qRegisterMetaType< QList< Tomahawk::InfoSystem::InfoStringHash > >("QList< Tomahawk::InfoSystem::InfoStringHash > "); @@ -461,6 +458,9 @@ TomahawkApp::registerMetaTypes() qRegisterMetaType< QPersistentModelIndex >( "QPersistentModelIndex" ); qRegisterMetaType< Tomahawk::PlaylistInterface::LatchMode >( "Tomahawk::PlaylistInterface::LatchMode" ); + + qRegisterMetaType< TomahawkUtils::CacheData >( "TomahawkUtils::CacheData" ); + qRegisterMetaTypeStreamOperators< TomahawkUtils::CacheData >( "TomahawkUtils::CacheData" ); } @@ -603,6 +603,20 @@ TomahawkApp::spotifyApiCheckFinished() } +void +TomahawkApp::accountManagerReady() +{ + Tomahawk::Accounts::LastFmAccountFactory* lastfmFactory = new Tomahawk::Accounts::LastFmAccountFactory(); + m_accountManager.data()->addAccountFactory( lastfmFactory ); + + Tomahawk::Accounts::SpotifyAccountFactory* spotifyFactory = new Tomahawk::Accounts::SpotifyAccountFactory; + m_accountManager.data()->addAccountFactory( spotifyFactory ); + m_accountManager.data()->registerAccountFactoryForFilesystem( spotifyFactory ); + + Tomahawk::Accounts::AccountManager::instance()->loadFromConfig(); +} + + void TomahawkApp::activate() { diff --git a/src/tomahawkapp.h b/src/tomahawkapp.h index 68b060360..2c76e641c 100644 --- a/src/tomahawkapp.h +++ b/src/tomahawkapp.h @@ -111,6 +111,7 @@ private slots: void initHTTP(); void spotifyApiCheckFinished(); + void accountManagerReady(); private: void registerMetaTypes();