Season Posters Anyone?

I simply cannot stop playing around with TV Shows and Nepomuk. We already had posters for the series but not for the seasons. Well, we do now:

Fun, isnt’ it? But sadly it required an improved libtvdb and additions to SDO. The former is already in git master. But the SDO changes are a bit experimental. That is why I put them into branch nmm/banners. Also due to both these requirements not being that easy to install I put the improved season handling of nepomuktvnamer into branch seasonResources.

So in order to try this yourself you need to get libtvdb git master and the mentioned branches of SDO and nepomuktvnamer. But do not worry, I am pretty sure that I will get the SDO changes merged soon. Then this will become easier.

A Fun Release: Nepomuk TV Namer 0.2

As requested I prepared a release of the TV Show managing thingi I implemented. You can download it from download.kde.org mirrors at unstable/nepomuk/nepomuktvnamer-0.2.0.tar.bz2.

The nepomuktvnamer 0.2.0 is a little more polished than the original version and comes with a nice service menu extension allowing to manually start the fetching of TV Show information on folders or video files. This is important since the service does only react on new videos. So you need to start the initial information fetching manually on your TV Show folder.

The tvnamer has two requirements in addition to the typical KDE ones:

  • LibTVDb – LibTvdb is a Qt-based library which provides asynchronous access to TV series information from thetvdb.com via a very simple interface. Its use in the Nepomuk TV namer should be obvious.
  • Shared-Desktop-Ontologies 0.9.0 – The recently released new version of SDO provides the required nfo:depiction property used by the tvnamer to store banners.

I also recommend to apply the kdelibs patch I mentioned earlier to actually see the TV Show banners. Have fun with it – maybe someone will even package it.

Just For The Fun Of It: Browsing Music With Nepomuk

Since implementing the TV Show KIO slave was that easy I decided I could do the same for music – just to show how simple it can be. There a a few more lines but that is only because I added browsing by album, artist, and genre. So there are a lot of if/else constructs. Anyway, here goes:

Browsing music by artist is easy. As you can see I also implemented a preview generator plugin the same way I did for the TV Shows. The only problem is that there is no tool yet that automatically fetches those images. Thus, I had to do it manually for one example which looks somewhat like this:

qdbus org.kde.NepomukStorage 
  /datamanagement
  org.kde.nepomuk.DataManagement.addProperty
  "nepomuk:/res/0152825f-5c49-4ca8-aa0a-23fc9a1305f1"
  "nfo:depiction"
  "/home/trueg/atb2.jpg"
  "shell"

This is part of the fancy Data management API which allows me to add the file atb2.jg as a nfo:depiction of the nco:Contact resource identifying the artist ATB.

Anyway, entering the artist themselves and what lies beyond:

(Again I had to fetch the cover art manually. I did not want to implement my own cover art retrieval tool and I found the Amarok code not to be very reusable. Again maybe someone wants to take up this task?)

Finally we end up in the album tracks. Sadly dragging an album to a media player playlist does not work yet. I am not quite sure how to fix that.

Last but not least a quick look at browsing by genre:

This was fun. But before I go to bed let me share with you the very simple code which is responsible for the nice previews (abbreviated of course):

bool MusicThumbCreator::create(const QString &path,
                               int w, int h,
                               QImage &img)
{
  KUrl url(path);
  QStringList pathTokens
      = url.path().split('/', QString::SkipEmptyParts);
  if(pathTokens.count() < 2) {
    return false;
  }

  // there are only two cases for us: artists and albums
  if(pathTokens[pathTokens.count()-2] == QLatin1String("artists") ||
     pathTokens[pathTokens.count()-2] == QLatin1String("albums")) {
      const QUrl uri = recoverUriFromUrlToken(pathTokens.last());
    // we just query the first depiction there is
    Soprano::QueryResultIterator it
       = Nepomuk::ResourceManager::instance()->mainModel()
         ->executeQuery(
              QString::fromLatin1("select ?u where { "
                                  "%1 nfo:depiction [ nie:url ?u ] . "
                                  "} LIMIT 1")
              .arg(Soprano::Node::resourceToN3(uri)),
              Soprano::Query::QueryLanguageSparql);
    if(it.next()) {
      img.load(it["u"].uri().toLocalFile());
      return true;
    }
  }

  return false;
}

The rest of the code can be found in the nepomuk-audio-kio-slave scratch repository. Maybe at some point I could just throw all of those things into some “Nepomuk KIO extensions” package… oh, well, off to bed now…

More Fun With TV Shows

After fetching all the details about TV Shows from thetvdb.com I went back to my favorite way of browsing things: KIO slaves. So without further ado let me introduce the tvshow:/ KIO slave:

So the root folder lists all TV Series. As you can see the previews are messed up aspect-ratio-wise. If anyone has an idea of how to improve that without patching KIcon or KIO or caching my own thumbnails in some tmp folder please tell me.

Entering the season listing…

And finally the episodes. And just because it is fun here is one more:

Why do this? Well, nepomuksearch cannot create sub-folders (yet) and this only has about 120 relevant lines of code, most of which is used up by the three queries it creates.

To try it simply update your git clone of the nepomuktvnamer and have fun.

A Little Bit Of Query Optimization

Every once in a while I add another piece of query optimization code to the Nepomuk Query API. This time it was a direct result of my earlier TV Show handling. I simply thought that a query like “downton season=2 episode=2” takes too long to complete.

Now in order to understand this you need to know that there is a rather simple QueryParser class which converts a query string like the above into a Nepomuk::Query::Query which is simply a collection of Nepomuk::Query::Term instances. A Query instance is then converted into a SPARQL query which can be handled by Virtuoso. This SPARQL query already contains a set of optimizations, some specific to Virtuoso, some specific to Nepomuk. Of course there is always room for improvement.

So let us get back to our query “downton season=2 episode=2” and look at the resulting SPARQL query string (I simplified the query a bit for readability. The important parts are still there):

select distinct ?r where {
  ?r nmm:season "2"^^xsd:int .
  {
    ?r nmm:hasEpisode ?v2 .
    ?v2 ?v3 "2"^^xsd:int .
    ?v3 rdfs:subPropertyOf rdfs:label .
  } UNION {
    ?r nmm:episodeNumber "2"^^xsd:int .
  } .
  {
    ?r ?v4 ?v6 .
    FILTER(bif:contains(?v6, "'downton'")) .
  } UNION {
    ?r ?v4 ?v7 .
    ?v7 ?v5 ?v6 .
    ?v5 rdfs:subPropertyOf rdfs:label .
    FILTER(bif:contains(?v6, "'downton'")) .
  } .
}

Like the user query the SPARQL query has three main parts: the graph pattern checking the nmm:season, the graph pattern checking the episode and the graph pattern checking the full text search term “downton“. The latter we can safely ignore in this case. It is always a UNION so full text searches also include relations to tags and the like.

The interesting bit is the second UNION. The query parser matched the term “episode” to properties nmm:hasEpisode and nmm:episodeNumber. On first glance this is fine since both contain the term “episode“. However, the property nmm:season which is used in the first non-optional graph-pattern has a domain of nmm:TVShow. nmm:hasEpisode on the other hand has a domain of nmm:TVSeries. That means that the first pattern in the UNION can never match in combination with the first graph pattern since the domains are different.

The obvious optimization is to remove the first part of the UNION which yields a much simpler and way faster query:

select distinct ?r where {
  ?r nmm:season "2"^^xsd:int .
  ?r nmm:episodeNumber "2"^^xsd:int .
  {
    ?r ?v4 ?v6 .
    FILTER(bif:contains(?v6, "'downton'")) .
  } UNION {
    ?r ?v4 ?v7 .
    ?v7 ?v5 ?v6 .
    ?v5 rdfs:subPropertyOf rdfs:label .
    FILTER(bif:contains(?v6, "'downton'")) .
  } .
}

Well, sadly this is not generically true since resources can be double/triple/whateveriple-typed, meaning that in theory an nmm:TVShow could also have type nmm:TVSeries. In this case it is obviously not likely but there are many cases in which it in fact does apply. Thus, this optimization cannot be applied to all queries. I will, however, include it in the parser where it is very likely that the user does not take double-typing into account.

If you have good examples that show why this optimization should not be included in the query parser by default please tell me so I can re-consider.

Now after having written this and proof-reading the SPARQL query I realize that this particular query could have been optimized in a much simpler way: the value “2” is obviously an integer value, thus it can never match to a non-literal like required for the nmm:hasEpisode property…