Commit f3e894f9 authored by Matěj Laitl's avatar Matěj Laitl
Browse files

ScriptableService: change PlaylistController hack to something more decent

...or at least move the hack out of a very generic class
(PlaylistController) to where it belongs, the ScriptableService. Fixes
tracks not being instantly playable on click bug and removes use of a
deprecated Playlist::makeLoadingSync() from PlaylistController.

BUGFIXES:
 * Radio streams from scripted services are now instantly playable.

BUG: 320718
FIXED-IN: 2.8
parent a24a3451
......@@ -14,6 +14,7 @@ VERSION 2.8
* Amarok now uses FindFFMpeg.cmake from kdelibs
BUGFIXES:
* Radio streams from scripted services are now instantly playable. (BR 320718)
* Recently Played List widget now keeps its own time for tracks instead of relying on
the Last Played statistic. (BR 302485)
* Prevent hitting an assertion failure if just removed track from the Local Collection
......
......@@ -121,6 +121,7 @@ set(libamarokurl_SRCS
set(libscriptableservice_SRCS
services/scriptable/ScriptableService.cpp
services/scriptable/ScriptableServiceCollection.cpp
services/scriptable/ScriptableServiceCollectionTreeModel.cpp
services/scriptable/ScriptableServiceInfoParser.cpp
services/scriptable/ScriptableServiceManager.cpp
services/scriptable/ScriptableServiceMeta.cpp
......
......@@ -603,20 +603,9 @@ Controller::insertionHelper( int bottomModelRow, Meta::TrackList& tl )
i.next();
Meta::TrackPtr track = i.value();
if ( track == Meta::TrackPtr() )
{ /*ignore*/ }
else if( Playlists::canExpand( track ) )
if( !track )
{
Playlists::PlaylistPtr playlist = Playlists::expand( track ); //expand() can return 0 if the KIO job times out
if( playlist )
{
playlist->makeLoadingSync();
playlist->triggerTrackLoad(); //playlist track loading is on demand.
//since this is a playlist masqueurading as a single track, make a MultiTrack out of it:
if ( playlist->tracks().count() > 0 )
modifiedList << Meta::TrackPtr( new Meta::MultiTrack( playlist ) );
}
/*ignore*/
}
else if( typeid( *track.data() ) == typeid( MetaFile::Track ) )
{
......
......@@ -237,7 +237,7 @@ class AMAROK_EXPORT ServiceTrack : public Meta::Track,
int albumId() const;
void setArtistId( int id );
int artistId() const;
void setUidUrl( const QString &url );
virtual void setUidUrl( const QString &url );
void setDownloadableUrl( const QString &url );
private:
......
......@@ -22,6 +22,7 @@
#include "browsers/servicebrowser/ServiceBrowser.h"
#include "core/support/Amarok.h"
#include "core/support/Debug.h"
#include "services/scriptable/ScriptableServiceCollectionTreeModel.h"
#include "services/scriptable/ScriptableServiceInfoParser.h"
#include "widgets/SearchWidget.h"
......@@ -100,7 +101,7 @@ int ScriptableService::insertItem( int level, int parentId, const QString & name
if( !callbackData.isEmpty() || playableUrl.isEmpty() )
return -1;
ScriptableServiceTrack *track = new ScriptableServiceTrack( name );
KSharedPtr<ScriptableServiceTrack> track( new ScriptableServiceTrack( name ) );
track->setAlbumId( parentId );
track->setUidUrl( playableUrl );
track->setServiceName( m_name );
......@@ -115,7 +116,7 @@ int ScriptableService::insertItem( int level, int parentId, const QString & name
track->setServiceScalableEmblem( m_customScalableEmblem );
else
track->setServiceEmblem( KStandardDirs::locate( "data", "amarok/images/emblem-scripted-scalable.svgz" ) );
if ( !albumOverride.isEmpty() )
track->setAlbumName( albumOverride );
if ( !artistOverride.isEmpty() )
......@@ -128,8 +129,8 @@ int ScriptableService::insertItem( int level, int parentId, const QString & name
track->setYearNumber( yearOverride );
if ( !coverUrl.isEmpty() )
track->setCustomAlbumCoverUrl( coverUrl );
return addTrack( track );
return addTrack( track.data() );
break;
} case 1:
......@@ -377,7 +378,7 @@ void ScriptableService::polish()
return;
}
m_contentView->setModel( new SingleCollectionTreeItemModel( m_collection, viewLevels ) );
m_contentView->setModel( new ScriptableServiceCollectionTreeModel( m_collection, viewLevels ) );
m_polished = true;
}
......
/****************************************************************************************
* Copyright (c) 2013 Matěj Laitl <matej@laitl.cz> *
* *
* This program 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 2 of the License, or (at your option) any later *
* version. *
* *
* This program 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 *
* this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************************/
#define DEBUG_PREFIX "ScriptableServiceCollectionTreeModel"
#include "ScriptableServiceCollectionTreeModel.h"
#include "AmarokMimeData.h"
#include "core/support/Debug.h"
#include "core-impl/collections/support/TextualQueryFilter.h"
#include "services/scriptable/ScriptableServiceMeta.h"
#include "services/scriptable/ScriptableServiceQueryMaker.h"
ScriptableServiceCollectionTreeModel::ScriptableServiceCollectionTreeModel(
Collections::Collection *collection,
const QList<CategoryId::CatMenuId> &levelType )
: SingleCollectionTreeItemModel( collection, levelType )
{
}
QMimeData *
ScriptableServiceCollectionTreeModel::mimeData( const QList<CollectionTreeItem *> &items ) const
{
// this is basically a copy of superclass method with a couple of changes:
// 1. we don't reuse tracks already in the model
// 2. we tell the querymaker to masquerade special tracks
using namespace Collections;
Meta::TrackList tracks;
QList<QueryMaker *> queries;
foreach( CollectionTreeItem *item, items )
{
if( item->isTrackItem() )
{
using namespace Meta;
const ScriptableServiceTrack *serviceTrack =
dynamic_cast<const ScriptableServiceTrack *>( item->data().data() );
if( !serviceTrack )
{
error() << "failed to convert generic track" << item->data() << "to ScriptableServiceTrack";
continue;
}
tracks << serviceTrack->playableTrack();
continue;
}
ScriptableServiceQueryMaker *qm = qobject_cast<ScriptableServiceQueryMaker *>( item->queryMaker() );
if( !qm )
{
error() << "failed to convert generic QueryMaker to ScriptableService one";
continue;
}
qm->setConvertToMultiTracks( true );
for( CollectionTreeItem *tmp = item; tmp; tmp = tmp->parent() )
tmp->addMatch( qm, levelCategory( tmp->level() - 1 ) );
Collections::addTextualFilter( qm, m_currentFilter );
queries.append( qm );
}
if( queries.isEmpty() && tracks.isEmpty() )
return 0;
AmarokMimeData *mimeData = new AmarokMimeData();
mimeData->setTracks( tracks );
mimeData->setQueryMakers( queries );
mimeData->startQueries();
return mimeData;
}
/****************************************************************************************
* Copyright (c) 2013 Matěj Laitl <matej@laitl.cz> *
* *
* This program 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 2 of the License, or (at your option) any later *
* version. *
* *
* This program 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 *
* this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************************/
#ifndef SCRIPTABLESERVICECOLLECTIONTREEMODEL_H
#define SCRIPTABLESERVICECOLLECTIONTREEMODEL_H
#include "browsers/SingleCollectionTreeItemModel.h"
/**
* Tiny wrapper around SingleCollectionTreeItemModel that implements
* converting service tracks that are in fact playlist to MultiTracks (for mime data,
* with help of ScriptableServiceQueryMaker
*/
class ScriptableServiceCollectionTreeModel : public SingleCollectionTreeItemModel
{
public:
ScriptableServiceCollectionTreeModel( Collections::Collection *collection,
const QList<CategoryId::CatMenuId> &levelType );
using SingleCollectionTreeItemModel::mimeData; // prevent warning
/**
* Overriden to masquerade playlist tracks as MultiTracks
*/
virtual QMimeData* mimeData( const QList<CollectionTreeItem *> &items ) const;
};
#endif // SCRIPTABLESERVICECOLLECTIONTREEMODEL_H
......@@ -18,12 +18,13 @@
#include "ScriptableServiceMeta_p.h"
#include "core/meta/support/PrivateMetaRegistry.h"
#include "ScriptableService.h"
#include "ScriptableServiceManager.h"
#include "core-impl/meta/multi/MultiTrack.h"
#include "core-impl/playlists/types/file/PlaylistFileSupport.h"
#include "services/scriptable/ScriptableService.h"
#include "services/scriptable/ScriptableServiceManager.h"
using namespace Meta;
ScriptableServiceMetaItem::ScriptableServiceMetaItem( int level )
: m_callbackString( QString() )
, m_level( level )
......@@ -196,6 +197,25 @@ QString Meta::ScriptableServiceTrack::scalableEmblem()
return m_serviceScalableEmblem;
}
void ScriptableServiceTrack::setUidUrl( const QString &url )
{
Meta::ServiceTrack::setUidUrl( url );
using namespace Playlists;
Meta::TrackPtr track( this );
PlaylistPtr playlist = canExpand( track ) ? expand( track ) : PlaylistPtr();
if( playlist )
// since this is a playlist masqueurading as a single track, make a MultiTrack out of it:
m_playableTrack = Meta::TrackPtr( new Meta::MultiTrack( playlist ) );
else
m_playableTrack = TrackPtr();
}
TrackPtr ScriptableServiceTrack::playableTrack() const
{
return m_playableTrack ? m_playableTrack : TrackPtr( const_cast<ScriptableServiceTrack *>( this ) );
}
/* DynamicScriptableAlbum */
ScriptableServiceAlbum::ScriptableServiceAlbum( const QString & name )
......
......@@ -81,6 +81,16 @@ class ScriptableServiceTrack : public Meta::ServiceTrack, public ScriptableServi
void setCustomAlbumCoverUrl( const QString &coverurl );
virtual QString collectionName() const { return m_serviceName; }
virtual void setUidUrl( const QString &url );
/**
* If this track is in fact a remote playlist, return Meta::MultiTrack that wraps
* it here, else return pointer to self.
*/
Meta::TrackPtr playableTrack() const;
private:
Meta::TrackPtr m_playableTrack;
};
class ScriptableServiceAlbum : public Meta::ServiceAlbumWithCover, public ScriptableServiceMetaItem
......
......@@ -14,15 +14,15 @@
* this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************************/
#define DEBUG_PREFIX "ScriptableServiceQueryMaker"
#include "ScriptableServiceQueryMaker.h"
#include "core/meta/support/MetaConstants.h"
#include "ScriptableServiceMeta.h"
#include "ScriptManager.h"
#include "core-impl/collections/support/MemoryMatcher.h"
#include "core/meta/support/MetaConstants.h"
#include "core/support/Debug.h"
#include "core-impl/collections/support/MemoryMatcher.h"
#include "services/scriptable/ScriptableServiceMeta.h"
#include <QTimer>
......@@ -44,6 +44,7 @@ struct ScriptableServiceQueryMaker::Private {
ScriptableServiceQueryMaker::ScriptableServiceQueryMaker( ScriptableServiceCollection * collection, QString name )
: DynamicServiceQueryMaker()
, d( new Private )
, m_convertToMultiTracks( false )
{
setParent( collection );
m_collection = collection;
......@@ -185,12 +186,12 @@ QueryMaker * ScriptableServiceQueryMaker::addMatch( const Meta::AlbumPtr & album
return this;
}
void ScriptableServiceQueryMaker::handleResult()
void
ScriptableServiceQueryMaker::setConvertToMultiTracks( bool convert )
{
m_convertToMultiTracks = convert;
}
void ScriptableServiceQueryMaker::handleResult( const Meta::GenreList & genres )
{
if ( d->maxsize >= 0 && genres.count() > d->maxsize )
......@@ -215,17 +216,32 @@ void ScriptableServiceQueryMaker::handleResult( const Meta::ArtistList & artists
emit newResultReady( artists );
}
void ScriptableServiceQueryMaker::handleResult( const Meta::TrackList & tracks )
void ScriptableServiceQueryMaker::handleResult( const Meta::TrackList &tracks )
{
debug() << "Emitting " << tracks.count() << " tracks";
if ( d->maxsize >= 0 && tracks.count() > d->maxsize )
emit newResultReady( tracks.mid( 0, d->maxsize ) );
Meta::TrackList ret;
if( m_convertToMultiTracks )
{
foreach( const Meta::TrackPtr &track, tracks )
{
using namespace Meta;
const ScriptableServiceTrack *serviceTrack =
dynamic_cast<const ScriptableServiceTrack *>( track.data() );
if( !serviceTrack )
{
error() << "failed to convert generic track" << track.data() << "to ScriptableServiceTrack";
continue;
}
ret << serviceTrack->playableTrack();
}
}
else
emit newResultReady( tracks );
}
ret = tracks;
if ( d->maxsize >= 0 && ret.count() > d->maxsize )
emit newResultReady( ret.mid( 0, d->maxsize ) );
else
emit newResultReady( ret );
}
void ScriptableServiceQueryMaker::fetchGenre()
{
......
......@@ -50,8 +50,13 @@ public:
virtual QueryMaker* setAlbumQueryMode( AlbumQueryMode mode );
void handleResult();
// ScriptableServiceQueryMaker-specific methods
/**
* Set to true if ScriptableServiceQueryMaker should convert tracks which are in
* fact playlists to Meta::MultiTrack instances to be playable. Defaults to false.
*/
void setConvertToMultiTracks( bool convert );
protected slots:
void slotScriptComplete( );
......@@ -80,6 +85,7 @@ protected:
private:
QString m_name;
bool m_convertToMultiTracks;
};
} //namespace Collections
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment