diff --git a/Banshee.sln b/Banshee.sln
index 815c87a..83bfefb 100644
--- a/Banshee.sln
+++ b/Banshee.sln
@@ -144,6 +144,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.SoundMenu", "src\Ex
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.UbuntuOneMusicStore", "src\Extensions\Banshee.UbuntuOneMusicStore\Banshee.UbuntuOneMusicStore.csproj", "{3B855EBC-8E55-48DF-816B-B241BE45EBD8}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.Video", "src\Extensions\Banshee.Video\Banshee.Video.csproj", "{DD7AF1FC-80DE-4382-8FC8-38BD6D66A2B8}"
+EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{4F47D6F1-4047-4A89-AE85-3AE5EF9F2961}"
 	ProjectSection(SolutionItems) = postProject
 	EndProjectSection
@@ -420,6 +422,16 @@ Global
 		{CE2AB4B9-F618-4CCA-8805-E1603741147E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{CE2AB4B9-F618-4CCA-8805-E1603741147E}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{CE2AB4B9-F618-4CCA-8805-E1603741147E}.Windows|Any CPU.ActiveCfg = Windows|Any CPU
+		{DD7AF1FC-80DE-4382-8FC8-38BD6D66A2B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{DD7AF1FC-80DE-4382-8FC8-38BD6D66A2B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{DD7AF1FC-80DE-4382-8FC8-38BD6D66A2B8}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{DD7AF1FC-80DE-4382-8FC8-38BD6D66A2B8}.Debug|x86.Build.0 = Debug|Any CPU
+		{DD7AF1FC-80DE-4382-8FC8-38BD6D66A2B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{DD7AF1FC-80DE-4382-8FC8-38BD6D66A2B8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{DD7AF1FC-80DE-4382-8FC8-38BD6D66A2B8}.Release|x86.ActiveCfg = Release|Any CPU
+		{DD7AF1FC-80DE-4382-8FC8-38BD6D66A2B8}.Release|x86.Build.0 = Release|Any CPU
+		{DD7AF1FC-80DE-4382-8FC8-38BD6D66A2B8}.Windows|Any CPU.ActiveCfg = Debug|Any CPU
+		{DD7AF1FC-80DE-4382-8FC8-38BD6D66A2B8}.Windows|Any CPU.Build.0 = Debug|Any CPU
 		{DEADBEEF-CAFE-BABE-FACE-000C0FFEE000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{DEADBEEF-CAFE-BABE-FACE-000C0FFEE000}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{DEADBEEF-CAFE-BABE-FACE-000C0FFEE000}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -528,6 +540,7 @@ Global
 		{AF8A9C6D-2188-413D-8EB8-C5E242BD68AC} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
 		{EE91126B-5500-4719-9FAA-36CBAC95DEE3} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
 		{3B855EBC-8E55-48DF-816B-B241BE45EBD8} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
+		{DD7AF1FC-80DE-4382-8FC8-38BD6D66A2B8} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
 		{95374549-9553-4C1E-9D89-667755F90E12} = {4F47D6F1-4047-4A89-AE85-3AE5EF9F2961}
 		{95374549-9553-4C1E-9D89-667755F90E13} = {4F47D6F1-4047-4A89-AE85-3AE5EF9F2961}
 		{C856EFD8-E812-4E61-8B76-E3583D94C233} = {4F47D6F1-4047-4A89-AE85-3AE5EF9F2961}
diff --git a/configure.ac b/configure.ac
index 921fcd1..30c2c15 100644
--- a/configure.ac
+++ b/configure.ac
@@ -373,6 +373,7 @@ src/Extensions/Banshee.Sample/Makefile
 src/Extensions/Banshee.Torrent/Makefile
 src/Extensions/Banshee.RemoteAudio/Makefile
 src/Extensions/Banshee.UbuntuOneMusicStore/Makefile
+src/Extensions/Banshee.Video/Makefile
 src/Extensions/Banshee.Wikipedia/Makefile
 src/Extensions/Banshee.YouTube/Makefile
 
diff --git a/data/addin-xml-strings.cs b/data/addin-xml-strings.cs
index 1b9b8dd..ae45be5 100644
--- a/data/addin-xml-strings.cs
+++ b/data/addin-xml-strings.cs
@@ -164,6 +164,11 @@ internal static class AddinXmlStringCatalog
         Catalog.GetString (@"This extension grants access to the Ubuntu One Music Store, powered by 7digital.");
         Catalog.GetString (@"Online Sources");
 
+        // ../src/Extensions/Banshee.Video/Banshee.Video.addin.xml
+        Catalog.GetString (@"Video library");
+        Catalog.GetString (@"Browse and manage your video library.");
+        Catalog.GetString (@"Core");
+
         // ../src/Extensions/Banshee.Wikipedia/Banshee.Wikipedia.addin.xml
         Catalog.GetString (@"Wikipedia");
         Catalog.GetString (@"View artist information from Wikipedia in the context pane.");
diff --git a/src/Clients/Beroe/Beroe/IndexerClient.cs b/src/Clients/Beroe/Beroe/IndexerClient.cs
index 538ff87..2e74e70 100644
--- a/src/Clients/Beroe/Beroe/IndexerClient.cs
+++ b/src/Clients/Beroe/Beroe/IndexerClient.cs
@@ -96,7 +96,6 @@ namespace Beroe
             ServiceManager.Get<CollectionIndexerService> ().ShutdownHandler = DBusConnection.QuitMainLoop;
 
             ServiceManager.SourceManager.AddSource (new Banshee.Library.MusicLibrarySource ());
-            ServiceManager.SourceManager.AddSource (new Banshee.Library.VideoLibrarySource ());
 
             DBusConnection.RunMainLoop ();
 
diff --git a/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs b/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
index 90cddb6..f4cde53 100644
--- a/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
+++ b/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
@@ -43,6 +43,7 @@ using Banshee.Sources;
 using Banshee.Collection;
 using Banshee.Collection.Database;
 using Banshee.Streaming;
+using Banshee.Library;
 
 // MIGRATION NOTE: Return true if the step should allow the driver to continue
 //                 Return false if the step should terminate driver
@@ -1329,18 +1330,18 @@ namespace Banshee.Database
         {
             if (args.Service is JobScheduler) {
                 ServiceManager.ServiceStarted -= OnServiceStarted;
+
                 if (ServiceManager.SourceManager.MusicLibrary != null) {
                     RefreshMetadataDelayed ();
-                } else {
-                    ServiceManager.SourceManager.SourceAdded += OnSourceAdded;
                 }
+
+                ServiceManager.SourceManager.SourceAdded += OnSourceAdded;
             }
         }
 
         private void OnSourceAdded (SourceAddedArgs args)
         {
-            if (ServiceManager.SourceManager.MusicLibrary != null && ServiceManager.SourceManager.VideoLibrary != null) {
-                ServiceManager.SourceManager.SourceAdded -= OnSourceAdded;
+            if (args.Source is Library.LibrarySource) {
                 RefreshMetadataDelayed ();
             }
         }
diff --git a/src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs b/src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs
index 25ba9c6..5d67032 100644
--- a/src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs
+++ b/src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs
@@ -63,10 +63,11 @@ namespace Banshee.Library
         protected override int [] PrimarySourceIds {
             get {
                 if (base.PrimarySourceIds == null) {
-                    base.PrimarySourceIds = new int [] {
-                        ServiceManager.SourceManager.VideoLibrary.DbId,
-                        ServiceManager.SourceManager.MusicLibrary.DbId
-                    };
+                    List<int> ids = new List<int> ();
+                    foreach (var src in ServiceManager.SourceManager.FindSources<LibrarySource> ()) {
+                            ids.Add (src.DbId);
+                    }
+                    base.PrimarySourceIds = ids.ToArray ();
                 }
 
                 return base.PrimarySourceIds;
@@ -79,11 +80,8 @@ namespace Banshee.Library
 
         protected static PrimarySource DefaultTrackPrimarySourceChooser (DatabaseTrackInfo track)
         {
-            if ((track.MediaAttributes & TrackMediaAttributes.VideoStream) != 0) {
-                return ServiceManager.SourceManager.VideoLibrary;
-            } else {
-                return ServiceManager.SourceManager.MusicLibrary;
-            }
+            LibrarySource src = ServiceManager.SourceManager.GetBestSourceForTrack (track);
+            return src ?? ServiceManager.SourceManager.MusicLibrary;
         }
 
         string IService.ServiceName {
diff --git a/src/Core/Banshee.Services/Banshee.Library/LibrarySource.cs b/src/Core/Banshee.Services/Banshee.Library/LibrarySource.cs
index 6e3c03d..3b54798 100644
--- a/src/Core/Banshee.Services/Banshee.Library/LibrarySource.cs
+++ b/src/Core/Banshee.Services/Banshee.Library/LibrarySource.cs
@@ -76,15 +76,15 @@ namespace Banshee.Library
         }
 
         private TrackMediaAttributes media_types;
-        protected TrackMediaAttributes MediaTypes {
+        public TrackMediaAttributes MediaTypes {
             get { return media_types; }
-            set { media_types = value; }
+            protected set { media_types = value; }
         }
 
         private TrackMediaAttributes not_media_types = TrackMediaAttributes.None;
-        protected TrackMediaAttributes NotMediaTypes {
+        public TrackMediaAttributes NotMediaTypes {
             get { return not_media_types; }
-            set { not_media_types = value; }
+            protected set { not_media_types = value; }
         }
 
         public override string PreferencesPageId {
diff --git a/src/Core/Banshee.Services/Banshee.Library/VideoLibrarySource.cs b/src/Core/Banshee.Services/Banshee.Library/VideoLibrarySource.cs
deleted file mode 100644
index 64e2f14..0000000
--- a/src/Core/Banshee.Services/Banshee.Library/VideoLibrarySource.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-//
-// VideoLibrarySource.cs
-//
-// Author:
-//   Aaron Bockover <abockover@novell.com>
-//   Gabriel Burt <gburt@novell.com>
-//
-// Copyright (C) 2005-2008 Novell, Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Collections.Generic;
-
-using Mono.Unix;
-
-using Banshee.SmartPlaylist;
-using Banshee.Collection;
-
-namespace Banshee.Library
-{
-    public class VideoLibrarySource : LibrarySource
-    {
-        // Catalog.GetString ("Video Library")
-        public VideoLibrarySource () : base (Catalog.GetString ("Videos"), "VideoLibrary", 50)
-        {
-            MediaTypes = TrackMediaAttributes.VideoStream;
-            NotMediaTypes = TrackMediaAttributes.Podcast;
-            Properties.SetStringList ("Icon.Name", "video-x-generic", "video", "source-library");
-            Properties.Set<string> ("SearchEntryDescription", Catalog.GetString ("Search your videos"));
-            Properties.SetString ("TrackView.ColumnControllerXml", String.Format (@"
-                <column-controller>
-                  <add-all-defaults />
-                  <remove-default column=""DiscColumn"" />
-                  <remove-default column=""AlbumColumn"" />
-                  <remove-default column=""ComposerColumn"" />
-                  <remove-default column=""AlbumArtistColumn"" />
-                  <remove-default column=""ConductorColumn"" />
-                  <remove-default column=""ComposerColumn"" />
-                  <remove-default column=""BpmColumn"" />
-                  <sort-column direction=""asc"">track_title</sort-column>
-                  <column modify-default=""ArtistColumn"">
-                    <title>{0}</title>
-                    <long-title>{0}</long-title>
-                  </column>
-                </column-controller>
-            ", Catalog.GetString ("Produced By")));
-        }
-
-        public override string GetPluralItemCountString (int count)
-        {
-            return Catalog.GetPluralString ("{0} video", "{0} videos", count);
-        }
-
-        public override bool ShowBrowser {
-            get { return false; }
-        }
-
-        protected override bool HasArtistAlbum {
-            get { return false; }
-        }
-
-        public override string DefaultBaseDirectory {
-            get { return Hyena.XdgBaseDirectorySpec.GetXdgDirectoryUnderHome ("XDG_VIDEOS_DIR", "Videos"); }
-        }
-
-        public override IEnumerable<SmartPlaylistDefinition> DefaultSmartPlaylists {
-            get { return default_smart_playlists; }
-        }
-
-        protected override string SectionName {
-            get { return Catalog.GetString ("Videos Folder"); }
-        }
-
-        private static SmartPlaylistDefinition [] default_smart_playlists = new SmartPlaylistDefinition [] {
-            new SmartPlaylistDefinition (
-                Catalog.GetString ("Favorites"),
-                Catalog.GetString ("Videos rated four and five stars"),
-                "rating>=4"),
-
-            new SmartPlaylistDefinition (
-                Catalog.GetString ("Unwatched"),
-                Catalog.GetString ("Videos that haven't been played yet"),
-                "plays=0"),
-        };
-    }
-}
diff --git a/src/Core/Banshee.Services/Banshee.Metadata.LastFM/LastFMQueryJob.cs b/src/Core/Banshee.Services/Banshee.Metadata.LastFM/LastFMQueryJob.cs
index 53f1f0f..4f4b1cc 100644
--- a/src/Core/Banshee.Services/Banshee.Metadata.LastFM/LastFMQueryJob.cs
+++ b/src/Core/Banshee.Services/Banshee.Metadata.LastFM/LastFMQueryJob.cs
@@ -59,7 +59,7 @@ namespace Banshee.Metadata.LastFM
         public override void Run ()
         {
 
-            if (Track == null || (Track.MediaAttributes & TrackMediaAttributes.Podcast) != 0) {
+            if (Track == null || (Track.MediaAttributes & (TrackMediaAttributes.Podcast | TrackMediaAttributes.VideoStream)) != 0) {
                 return;
             }
 
diff --git a/src/Core/Banshee.Services/Banshee.Metadata.MusicBrainz/MusicBrainzQueryJob.cs b/src/Core/Banshee.Services/Banshee.Metadata.MusicBrainz/MusicBrainzQueryJob.cs
index 7dc9248..68b927a 100644
--- a/src/Core/Banshee.Services/Banshee.Metadata.MusicBrainz/MusicBrainzQueryJob.cs
+++ b/src/Core/Banshee.Services/Banshee.Metadata.MusicBrainz/MusicBrainzQueryJob.cs
@@ -93,7 +93,7 @@ namespace Banshee.Metadata.MusicBrainz
         public bool Lookup ()
         {
 
-            if (Track == null || (Track.MediaAttributes & TrackMediaAttributes.Podcast) != 0) {
+            if (Track == null || (Track.MediaAttributes & (TrackMediaAttributes.Podcast | TrackMediaAttributes.VideoStream)) != 0) {
                 return false;
             }
 
diff --git a/src/Core/Banshee.Services/Banshee.Metadata.Rhapsody/RhapsodyQueryJob.cs b/src/Core/Banshee.Services/Banshee.Metadata.Rhapsody/RhapsodyQueryJob.cs
index 3c04a24..a68d651 100644
--- a/src/Core/Banshee.Services/Banshee.Metadata.Rhapsody/RhapsodyQueryJob.cs
+++ b/src/Core/Banshee.Services/Banshee.Metadata.Rhapsody/RhapsodyQueryJob.cs
@@ -68,7 +68,7 @@ namespace Banshee.Metadata.Rhapsody
 
         public override void Run()
         {
-            if (Track == null || (Track.MediaAttributes & TrackMediaAttributes.Podcast) != 0) {
+            if (Track == null || (Track.MediaAttributes & (TrackMediaAttributes.Podcast | TrackMediaAttributes.VideoStream)) != 0) {
                 return;
             }
 
diff --git a/src/Core/Banshee.Services/Banshee.Metadata/MetadataService.cs b/src/Core/Banshee.Services/Banshee.Metadata/MetadataService.cs
index f90f9bf..6401d08 100644
--- a/src/Core/Banshee.Services/Banshee.Metadata/MetadataService.cs
+++ b/src/Core/Banshee.Services/Banshee.Metadata/MetadataService.cs
@@ -40,7 +40,7 @@ namespace Banshee.Metadata
 {
     public class MetadataService : BaseMetadataProvider
     {
-        private static MetadataService instance;
+        protected static MetadataService instance;
 
         public static MetadataService Instance {
             get {
@@ -58,14 +58,19 @@ namespace Banshee.Metadata
 
         public MetadataService ()
         {
+            AddDefaultProviders ();
+
+            Scheduler.JobFinished += OnSchedulerJobFinished;
+            Scheduler.JobUnscheduled += OnSchedulerJobUnscheduled;
+        }
+
+        public void AddDefaultProviders ()
+        {
             AddProvider (new Banshee.Metadata.Embedded.EmbeddedMetadataProvider ());
             AddProvider (new Banshee.Metadata.FileSystem.FileSystemMetadataProvider ());
             AddProvider (new Banshee.Metadata.Rhapsody.RhapsodyMetadataProvider ());
             AddProvider (new Banshee.Metadata.MusicBrainz.MusicBrainzMetadataProvider ());
             AddProvider (new Banshee.Metadata.LastFM.LastFMMetadataProvider ());
-
-            Scheduler.JobFinished += OnSchedulerJobFinished;
-            Scheduler.JobUnscheduled += OnSchedulerJobUnscheduled;
         }
 
         public override IMetadataLookupJob CreateJob (IBasicTrackInfo track)
diff --git a/src/Core/Banshee.Services/Banshee.Playlist/PlaylistFileUtil.cs b/src/Core/Banshee.Services/Banshee.Playlist/PlaylistFileUtil.cs
index 146201d..423ed7e 100644
--- a/src/Core/Banshee.Services/Banshee.Playlist/PlaylistFileUtil.cs
+++ b/src/Core/Banshee.Services/Banshee.Playlist/PlaylistFileUtil.cs
@@ -206,11 +206,7 @@ namespace Banshee.Playlist
                             SafeUri uri1 = new SafeUri (uris[0]);
                             var track = new TrackInfo ();
                             StreamTagger.TrackInfoMerge (track, uri1);
-
-                            if (track.HasAttribute (TrackMediaAttributes.VideoStream))
-                                source = ServiceManager.SourceManager.VideoLibrary;
-                            else
-                                source = ServiceManager.SourceManager.MusicLibrary;
+                            source = ServiceManager.SourceManager.GetBestSourceForTrack (track);
                         }
                     }
 
diff --git a/src/Core/Banshee.Services/Banshee.ServiceStack/Application.cs b/src/Core/Banshee.Services/Banshee.ServiceStack/Application.cs
index eeda040..3efc668 100644
--- a/src/Core/Banshee.Services/Banshee.ServiceStack/Application.cs
+++ b/src/Core/Banshee.Services/Banshee.ServiceStack/Application.cs
@@ -108,7 +108,6 @@ namespace Banshee.ServiceStack
             ServiceManager.Run ();
 
             ServiceManager.SourceManager.AddSource (new MusicLibrarySource (), true);
-            ServiceManager.SourceManager.AddSource (new VideoLibrarySource (), false);
             ServiceManager.SourceManager.LoadExtensionSources ();
 
             Banshee.Base.PlatformHacks.RestoreMonoJitSegv ();
diff --git a/src/Core/Banshee.Services/Banshee.Services.csproj b/src/Core/Banshee.Services/Banshee.Services.csproj
index b766ad7..f699751 100644
--- a/src/Core/Banshee.Services/Banshee.Services.csproj
+++ b/src/Core/Banshee.Services/Banshee.Services.csproj
@@ -215,7 +215,6 @@
     <Compile Include="Banshee.Sources\SourceMessage.cs" />
     <Compile Include="Banshee.Sources\MessageAction.cs" />
     <Compile Include="Banshee.Library\MusicLibrarySource.cs" />
-    <Compile Include="Banshee.Library\VideoLibrarySource.cs" />
     <Compile Include="Banshee.ServiceStack\Client.cs" />
     <Compile Include="Banshee.Collection.Database\DatabaseArtistInfo.cs" />
     <Compile Include="Banshee.Hardware\HardwareManager.cs" />
diff --git a/src/Core/Banshee.Services/Banshee.Sources/SourceManager.cs b/src/Core/Banshee.Services/Banshee.Sources/SourceManager.cs
index d50210c..07691a6 100644
--- a/src/Core/Banshee.Services/Banshee.Sources/SourceManager.cs
+++ b/src/Core/Banshee.Services/Banshee.Sources/SourceManager.cs
@@ -60,7 +60,6 @@ namespace Banshee.Sources
         private Source active_source;
         private Source default_source;
         private MusicLibrarySource music_library;
-        private VideoLibrarySource video_library;
 
         public event SourceEventHandler SourceUpdated;
         public event SourceAddedHandler SourceAdded;
@@ -102,7 +101,6 @@ namespace Banshee.Sources
                 active_source = null;
                 default_source = null;
                 music_library = null;
-                video_library = null;
 
                 // Do dispose extension sources
                 foreach (Source source in extension_sources.Values) {
@@ -167,8 +165,6 @@ namespace Banshee.Sources
 
             if (source is MusicLibrarySource) {
                 music_library = source as MusicLibrarySource;
-            } else if (source is VideoLibrarySource) {
-                video_library = source as VideoLibrarySource;
             }
 
             SourceAdded.SafeInvoke (new SourceAddedArgs () {
@@ -278,6 +274,21 @@ namespace Banshee.Sources
             });
         }
 
+        public LibrarySource GetBestSourceForTrack(Banshee.Collection.TrackInfo track)
+        {
+            foreach (Source source in sources) {
+                LibrarySource lib = source as LibrarySource;
+                if (lib == null) {
+                    continue;
+                }
+                if (track.HasAttribute (lib.MediaTypes) &&
+                    !track.HasAttribute (lib.NotMediaTypes)) {
+                    return lib;
+                }
+            }
+            return null;
+        }
+
         private void OnChildSourceAdded(SourceEventArgs args)
         {
             AddSource (args.Source);
@@ -314,10 +325,6 @@ namespace Banshee.Sources
             get { return music_library; }
         }
 
-        public VideoLibrarySource VideoLibrary {
-            get { return video_library; }
-        }
-
         public Source ActiveSource {
             get { return active_source; }
         }
diff --git a/src/Core/Banshee.Services/Makefile.am b/src/Core/Banshee.Services/Makefile.am
index 0de4ae2..bed4061 100644
--- a/src/Core/Banshee.Services/Makefile.am
+++ b/src/Core/Banshee.Services/Makefile.am
@@ -89,7 +89,6 @@ SOURCES =  \
 	Banshee.Library/MusicLibrarySource.cs \
 	Banshee.Library/Tests/FileNamePatternTests.cs \
 	Banshee.Library/ThreadPoolImportSource.cs \
-	Banshee.Library/VideoLibrarySource.cs \
 	Banshee.MediaEngine/IAudioCdRipper.cs \
 	Banshee.MediaEngine/IBpmDetector.cs \
 	Banshee.MediaEngine/IEqualizer.cs \
diff --git a/src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/FileChooserDialog.cs b/src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/FileChooserDialog.cs
index c904bf2..edbd535 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/FileChooserDialog.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Gui.Dialogs/FileChooserDialog.cs
@@ -27,6 +27,7 @@
 //
 
 using System;
+using System.Collections.Generic;
 using Gtk;
 
 using Banshee.Configuration;
@@ -51,13 +52,14 @@ namespace Banshee.Gui.Dialogs
             chooser.AddButton (Stock.Cancel, ResponseType.Cancel);
             // Translators: verb
             chooser.AddButton (Mono.Unix.Catalog.GetString("I_mport"), ResponseType.Ok);
+            List<string> lst = new List<string> ();
+            foreach (var library in ServiceManager.SourceManager.FindSources<Library.LibrarySource> ()) {
+                lst.Add (library.BaseDirectory);
+            }
+            lst.Add (GetPhotosFolder ());
 
-            // FIXME: this dialog should be library-specific, and so these shortcuts should be
-            // library-specific too
             Hyena.Gui.GtkUtilities.SetChooserShortcuts (chooser,
-                ServiceManager.SourceManager.MusicLibrary.BaseDirectory,
-                ServiceManager.SourceManager.VideoLibrary.BaseDirectory,
-                GetPhotosFolder ()
+                lst.ToArray ()
             );
 
             return chooser;
diff --git a/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackSubtitleActions.cs b/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackSubtitleActions.cs
index 749b8cc..f8790e0 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackSubtitleActions.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackSubtitleActions.cs
@@ -87,8 +87,14 @@ namespace Banshee.Gui
             chooser.AddButton (Stock.Cancel, ResponseType.Cancel);
             chooser.AddButton (Catalog.GetString ("L_oad"), ResponseType.Ok);
 
+            List<string> lst = new List<string> ();
+            foreach (var library in ServiceManager.SourceManager.FindSources<Library.LibrarySource> ()) {
+                if ((library.MediaTypes & TrackMediaAttributes.VideoStream) != 0)
+                    lst.Add (library.BaseDirectory);
+            }
+
             Hyena.Gui.GtkUtilities.SetChooserShortcuts (chooser,
-                ServiceManager.SourceManager.VideoLibrary.BaseDirectory
+                lst.ToArray ()
             );
 
             var filter = new FileFilter();
diff --git a/src/Dap/Banshee.Dap/Banshee.Dap/DapSync.cs b/src/Dap/Banshee.Dap/Banshee.Dap/DapSync.cs
index fc4e273..87051e8 100644
--- a/src/Dap/Banshee.Dap/Banshee.Dap/DapSync.cs
+++ b/src/Dap/Banshee.Dap/Banshee.Dap/DapSync.cs
@@ -48,6 +48,7 @@ using Banshee.Preferences;
 using Banshee.Gui;
 using Hyena.Widgets;
 using Gtk;
+using Banshee.Collection;
 
 namespace Banshee.Dap
 {
@@ -266,11 +267,7 @@ namespace Banshee.Dap
                     //return a.Order.CompareTo (b.Order);
                 //});
 
-            if (!dap.SupportsVideo && library == ServiceManager.SourceManager.VideoLibrary) {
-                return null;
-            }
-
-            if (!dap.SupportsPodcasts && library.UniqueId == "PodcastSource-PodcastLibrary") {
+            if (!dap.SupportsVideo && (library.MediaTypes & TrackMediaAttributes.VideoStream) != 0) {
                 return null;
             }
 
diff --git a/src/Dap/Banshee.Dap/Banshee.Dap/VideoGroupSource.cs b/src/Dap/Banshee.Dap/Banshee.Dap/VideoGroupSource.cs
index e6bd8d7..ae11419 100644
--- a/src/Dap/Banshee.Dap/Banshee.Dap/VideoGroupSource.cs
+++ b/src/Dap/Banshee.Dap/Banshee.Dap/VideoGroupSource.cs
@@ -38,8 +38,8 @@ namespace Banshee.Dap
         public VideoGroupSource (DapSource parent) : base (parent, Catalog.GetString ("Videos"))
         {
             Properties.Remove ("Icon.Name");
-            Properties.SetStringList ("Icon.Name", Banshee.ServiceStack.ServiceManager.SourceManager.VideoLibrary.Properties.GetStringList ("Icon.Name"));
-            ConditionSql = Banshee.ServiceStack.ServiceManager.SourceManager.VideoLibrary.AttributesCondition;
+            Properties.SetStringList ("Icon.Name", "video-x-generic", "video", "source-library");
+            ConditionSql = String.Format ("((CoreTracks.Attributes & {0}) == {0} AND (CoreTracks.Attributes & {1}) == 0)", (int)TrackMediaAttributes.VideoStream, (int)TrackMediaAttributes.Podcast);
             AutoHide = true;
         }
     }
diff --git a/src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher/LibraryWatcherService.cs b/src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher/LibraryWatcherService.cs
index 6e7558e..c1b6ee5 100644
--- a/src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher/LibraryWatcherService.cs
+++ b/src/Extensions/Banshee.LibraryWatcher/Banshee.LibraryWatcher/LibraryWatcherService.cs
@@ -53,27 +53,12 @@ namespace Banshee.LibraryWatcher
 
         public void DelayedInitialize ()
         {
-            // FIXME: Support sources other than the music and the video library (e.g. podcasts, audiobooks, etc)
-            // The SourceWatcher uses LibraryImportManager which is specific to music/video.
-            // To support other sources we need a separate importer for each of them.
-            /*
             ServiceManager.SourceManager.SourceAdded += OnSourceAdded;
             ServiceManager.SourceManager.SourceRemoved += OnSourceRemoved;
 
             foreach (var library in ServiceManager.SourceManager.FindSources<LibrarySource> ()) {
                 AddLibrary (library);
             }
-            */
-            AddLibrary (ServiceManager.SourceManager.MusicLibrary);
-            AddLibrary (ServiceManager.SourceManager.VideoLibrary);
-
-            if (ServiceManager.SourceManager.MusicLibrary.Count == 0) {
-                new Banshee.Collection.RescanPipeline (ServiceManager.SourceManager.MusicLibrary);
-            }
-
-            if (ServiceManager.SourceManager.VideoLibrary.Count == 0) {
-                new Banshee.Collection.RescanPipeline (ServiceManager.SourceManager.VideoLibrary);
-            }
         }
 
         public void Dispose ()
@@ -86,7 +71,6 @@ namespace Banshee.LibraryWatcher
             }
         }
 
-        /*
         private void OnSourceAdded (SourceAddedArgs args)
         {
             var library = args.Source as LibrarySource;
@@ -102,7 +86,6 @@ namespace Banshee.LibraryWatcher
                 RemoveLibrary (library);
             }
         }
-        */
 
         private void AddLibrary (LibrarySource library)
         {
@@ -126,9 +109,11 @@ namespace Banshee.LibraryWatcher
                     }
                 }
             }
+            if (library.Count == 0) {
+                new Banshee.Collection.RescanPipeline (library);
+            }
         }
 
-        /*
         private void RemoveLibrary (LibrarySource library)
         {
             lock (watchers) {
@@ -138,6 +123,5 @@ namespace Banshee.LibraryWatcher
                 }
             }
         }
-        */
     }
 }
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
index ab8113a..6027a64 100644
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
@@ -101,10 +101,10 @@ namespace Banshee.PlayQueue
             Properties.SetString ("GtkActionPath", "/PlayQueueContextMenu");
 
             // TODO listen to all primary sources, and handle transient primary sources
-            ServiceManager.SourceManager.MusicLibrary.TracksChanged += HandleTracksChanged;
-            ServiceManager.SourceManager.MusicLibrary.TracksDeleted += HandleTracksDeleted;
-            ServiceManager.SourceManager.VideoLibrary.TracksChanged += HandleTracksChanged;
-            ServiceManager.SourceManager.VideoLibrary.TracksDeleted += HandleTracksDeleted;
+            foreach (var library in ServiceManager.SourceManager.FindSources<LibrarySource> ()) {
+                library.TracksChanged += HandleTracksChanged;
+                library.TracksDeleted += HandleTracksDeleted;
+            }
 
             populate_from = ServiceManager.SourceManager.Sources.FirstOrDefault (
                 source => source.Name == populate_from_name) as DatabaseSource;
diff --git a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/QueueableSourceComboBox.cs b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/QueueableSourceComboBox.cs
index dccc590..7218659 100644
--- a/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/QueueableSourceComboBox.cs
+++ b/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/QueueableSourceComboBox.cs
@@ -31,6 +31,7 @@ using Banshee.Library;
 using Banshee.ServiceStack;
 using Banshee.Sources;
 using Banshee.Sources.Gui;
+using Banshee.Collection;
 
 namespace Banshee.PlayQueue
 {
@@ -95,9 +96,11 @@ namespace Banshee.PlayQueue
 
         private bool IsQueueable (Source source)
         {
-            return source != null && (
-                source is MusicLibrarySource || source.Parent is MusicLibrarySource ||
-                source is VideoLibrarySource || source.Parent is VideoLibrarySource);
+            LibrarySource lib_source = source as LibrarySource;
+            return lib_source != null && (
+                (lib_source.MediaTypes & (TrackMediaAttributes.Music | TrackMediaAttributes.VideoStream)) != 0 ||
+                (lib_source.Parent != null && lib_source.Parent is LibrarySource &&
+                (((LibrarySource)lib_source.Parent).MediaTypes & (TrackMediaAttributes.Music | TrackMediaAttributes.VideoStream)) != 0));
         }
 
         private TreeIter FindSource (string name, TreeIter iter)
diff --git a/src/Extensions/Banshee.Video/Banshee.Video.Metadata/ImdbMetadataJob.cs b/src/Extensions/Banshee.Video/Banshee.Video.Metadata/ImdbMetadataJob.cs
new file mode 100644
index 0000000..9f2c8b0
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video.Metadata/ImdbMetadataJob.cs
@@ -0,0 +1,45 @@
+// 
+// ImdbMetadataJob.cs
+// 
+// Author:
+//   Olivier Dufour <olivier.duff@gmail.com>
+// 
+// Copyright 2011 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using Banshee.Metadata;
+using Banshee.Collection;
+
+namespace Banshee.Video.Metadata
+{
+    public class ImdbMetadataJob : MetadataServiceJob
+    {
+        public ImdbMetadataJob (IBasicTrackInfo track)
+        {
+        }
+
+        public override void Run()
+        {
+            //http://www.imdb.com/
+
+        }
+    }
+}
+
diff --git a/src/Extensions/Banshee.Video/Banshee.Video.Metadata/ImdbMetadataProvider.cs b/src/Extensions/Banshee.Video/Banshee.Video.Metadata/ImdbMetadataProvider.cs
new file mode 100644
index 0000000..eb5048f
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video.Metadata/ImdbMetadataProvider.cs
@@ -0,0 +1,44 @@
+// 
+// ImdbMetadataProvider.cs
+// 
+// Author:
+//   Olivier Dufour <olivier (dot) duff (at) gmail (dot) com>
+// 
+// Copyright 2011 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using Banshee.Metadata;
+using Banshee.Collection;
+
+namespace Banshee.Video.Metadata
+{
+    public class ImdbMetadataProvider : BaseMetadataProvider
+    {
+        public ImdbMetadataProvider ()
+        {
+        }
+
+        public override IMetadataLookupJob CreateJob (IBasicTrackInfo track)
+        {
+            return new ImdbMetadataJob (track);
+        }
+    }
+}
+
diff --git a/src/Extensions/Banshee.Video/Banshee.Video.Metadata/TmdbMetadataJob.cs b/src/Extensions/Banshee.Video/Banshee.Video.Metadata/TmdbMetadataJob.cs
new file mode 100644
index 0000000..d193c3e
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video.Metadata/TmdbMetadataJob.cs
@@ -0,0 +1,165 @@
+// 
+// TmdbMetadataProvider.cs
+// 
+// Author:
+//   Olivier Dufour <olivier (dot) duff (at) gmail (dot) com>
+// 
+// Copyright 2011 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Globalization;
+using System.IO;
+using System.Xml;
+using System.Net;
+using System.Web;
+
+using Banshee.Base;
+using Banshee.Collection;
+using Banshee.Collection.Database;
+using Banshee.ServiceStack;
+using Banshee.Metadata;
+using Banshee.Web;
+
+using Hyena;
+
+namespace Banshee.Video.Metadata
+{
+    public class TmdbMetadataJob : MetadataServiceJob
+    {
+        private DatabaseTrackInfo track;
+
+        public TmdbMetadataJob (IBasicTrackInfo track)
+        {
+            this.track = track as DatabaseTrackInfo;
+        }
+        
+        public override void Run()
+        {
+            if (track == null || (track.MediaAttributes & TrackMediaAttributes.Podcast) != 0
+                              || (track.MediaAttributes & TrackMediaAttributes.VideoStream) == 0)
+                return;
+
+            VideoService service = ServiceStack.ServiceManager.Get<VideoService> ();
+
+            string cover_art_id = service.ArtworkIdFor (track);
+
+            if (CoverArtSpec.CoverExists (cover_art_id)) {
+                return;
+            } else if (!InternetConnected) {
+                return;
+            }
+
+            string tmdb_id = SearchMovie (track);
+            GetInfo (track, tmdb_id);
+        }
+
+        private readonly string API_KEY = "0e4632ee0881dce13dfc053ec42b819b";
+
+        private string SearchMovie (DatabaseTrackInfo track)
+        {
+            //TODO remove all not needed part or parasit char
+            string name = track.TrackTitle.Replace ('.', ' ').Replace ('_', ' ');
+            string url = string.Format("http://api.themoviedb.org/2.1/Movie.search/{0}/xml/{1}/{2}", CultureInfo.CurrentCulture.Name, API_KEY, HttpUtility.UrlEncode (name));
+            Log.Debug ("tmdb webservice:" + url);
+            HttpRequest request = new HttpRequest (url);
+            XmlDocument doc = new XmlDocument();
+            try {
+                request.GetResponse ();
+
+                using (Stream stream = request.GetResponseStream ()) {
+                    doc.Load (stream);
+                }
+                Log.Debug (doc.OuterXml);
+
+                XmlNode movie_node = doc.DocumentElement.SelectSingleNode ("/OpenSearchDescription/movies/movie");
+                if (movie_node == null) {
+                    return String.Empty;
+                }
+                VideoInfo video_info = (VideoInfo)track.ExternalObject;
+                video_info.Title = movie_node["name"].InnerXml;
+                video_info.OriginalTitle = movie_node["original_name"].InnerXml;
+                video_info.AlternativeTitle = movie_node["alternative_name"].InnerXml;
+                video_info.Language = movie_node["language"].InnerXml;
+                video_info.InfoUrl = movie_node["url"].InnerXml;
+                video_info.ImDbId = movie_node["imdb_id"].InnerXml;
+                video_info.ReleaseDate = DateTime.ParseExact (movie_node["released"].InnerXml, "yyyy-MM-dd", CultureInfo.InvariantCulture.DateTimeFormat);
+                video_info.Summary = movie_node["overview"].InnerXml;
+                video_info.VideoType = (int)videoType.Movie;
+                video_info.ExternalVideoId = movie_node["id"].InnerXml;
+                video_info.ParentId = 0;
+                video_info.Save ();
+
+                foreach (XmlNode n in movie_node.SelectNodes ("//image")) {
+                    if (LoadImage (n, video_info.ArtworkId))
+                        break;
+                }
+
+                return movie_node["id"].InnerXml;
+            } catch (WebException ex) {
+                Log.Error (ex.Status.ToString ());
+                Log.DebugException (ex);
+            } catch (Exception e) {
+               Log.Error (doc.InnerXml);
+               Log.DebugException (e);
+            }
+            return String.Empty;
+        }
+
+        private bool LoadImage (XmlNode obj, string cover_art_id)
+        {
+            string image_type = obj.Attributes["type"].Value;
+            string image_size = obj.Attributes["size"].Value;
+            string image_url = obj.Attributes["url"].Value;
+
+            if (image_type != "poster" && image_size != "thumb")
+                return false;
+            Log.Debug ("Load movie image");
+            if (SaveHttpStreamCover (new Uri (image_url), cover_art_id, null)) {
+                Banshee.Sources.Source src = ServiceManager.SourceManager.ActiveSource;
+                if (src != null && (src is VideoLibrarySource || src.Parent is VideoLibrarySource)) {
+                    (src as Banshee.Sources.DatabaseSource).Reload ();
+                }
+                return true;
+            }
+            return false;
+        }
+
+        private void GetInfo (DatabaseTrackInfo track, string tmdb_id)
+        {
+            /*HttpRequest request = new HttpRequest (string.Format("http://api.themoviedb.org/2.1/Movie.getInfo/{0}/xml/{1}/{2}", CultureInfo.CurrentCulture.TwoLetterISOLanguageName, API_KEY, tmdb_id));
+            try {
+                XmlDocument doc = new XmlDocument();
+                using (Stream stream = request.GetResponseStream ()) {
+                    doc.Load (stream);
+                }
+
+                XmlNode movie_node = doc.DocumentElement.SelectSingleNode ("TODO");
+
+
+            //TODO to get casting and few other things...
+                
+            }
+            catch (Exception e) {
+               Log.DebugException (e);
+            }*/
+        }
+    }
+}
+
diff --git a/src/Extensions/Banshee.Video/Banshee.Video.Metadata/TmdbMetadataProvider.cs b/src/Extensions/Banshee.Video/Banshee.Video.Metadata/TmdbMetadataProvider.cs
new file mode 100644
index 0000000..9d377a5
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video.Metadata/TmdbMetadataProvider.cs
@@ -0,0 +1,43 @@
+// 
+// TmdbMetadataProvider.cs
+// 
+// Author:
+//   Olivier Dufour <olivier.duff@gmail.com>
+// 
+// Copyright 2011 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using Banshee.Collection;
+using Banshee.Metadata;
+namespace Banshee.Video.Metadata
+{
+    public class TmdbMetadataProvider : BaseMetadataProvider
+    {
+        public TmdbMetadataProvider ()
+        {
+        }
+
+        public override IMetadataLookupJob CreateJob (IBasicTrackInfo track)
+        {
+            return new TmdbMetadataJob (track);
+        }
+    }
+}
+
diff --git a/src/Extensions/Banshee.Video/Banshee.Video.Metadata/TvdbMetadataJob.cs b/src/Extensions/Banshee.Video/Banshee.Video.Metadata/TvdbMetadataJob.cs
new file mode 100644
index 0000000..6187ce3
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video.Metadata/TvdbMetadataJob.cs
@@ -0,0 +1,362 @@
+// 
+// TvdbMetadataProvider.cs
+// 
+// Author:
+//   Olivier Dufour <olivier (dot) duff (at) gmail (dot) com>
+// 
+// Copyright 2011 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.IO;
+using System.Globalization;
+using System.Collections.Generic;
+using System.Xml;
+using System.Net;
+using System.Web;
+
+using Banshee.Base;
+using Banshee.Web;
+using Banshee.Metadata;
+using Banshee.Collection;
+using Banshee.Collection.Database;
+using Banshee.ServiceStack;
+
+using Hyena;
+
+namespace Banshee.Video.Metadata
+{
+    public class TvdbMetadataJob : MetadataServiceJob
+    {
+        private DatabaseTrackInfo track;
+
+        public TvdbMetadataJob (IBasicTrackInfo track) : base ()
+        {
+            this.track = track as DatabaseTrackInfo;
+        }
+
+        public override void Run()
+        {
+            if (track == null || (track.MediaAttributes & TrackMediaAttributes.Podcast) != 0
+                              || (track.MediaAttributes & TrackMediaAttributes.VideoStream) == 0)
+                return;
+
+            VideoService service = ServiceStack.ServiceManager.Get<VideoService> ();
+
+            string cover_art_id = service.ArtworkIdFor (track);
+
+            if (CoverArtSpec.CoverExists (cover_art_id)) {
+                return;
+            } else if (!InternetConnected) {
+                return;
+            }
+
+            if ((track.MediaAttributes & TrackMediaAttributes.TvShow) == 0)
+                return;
+
+            GetMetadata (track);
+        }
+
+        void GetMetadata (DatabaseTrackInfo track)
+        {
+            string seriesid = GetSerieId (track);
+            Log.Debug ("tv metadata job: serie id : " + seriesid);
+            if (String.IsNullOrEmpty (seriesid))
+                return;
+
+            int parentid;
+            VideoInfo parent = VideoInfo.Provider.FetchFirstMatching ("ExternalVideoId = ? AND VideoType = ?", seriesid, (int)videoType.Serie);
+            if (parent == null) {
+                Log.Debug ("no parent in db...");
+                parent = GetSerieMetadata (track, seriesid);
+                if (parent == null) {
+                    Log.Debug ("Serie can not been created!");
+                    return;
+                }
+            }
+            parentid = parent.DbId;
+            Log.Debug ("tv metadata job: parent id : " + parentid);
+            GetEpisodeMetadata (track, seriesid, parentid);
+            GetBanners (track, seriesid, parent.ArtworkId);
+
+        }
+
+        private readonly string API_KEY = "897F1CA903D4703A";
+
+ #region Mirror (unactived for moment)
+        private enum serverCapability : int
+        {
+            xmlfile= 1,
+            bannerfile = 2,
+            zipfile = 4
+        }
+
+        private class Mirror
+        {
+            public Mirror (string url, serverCapability capability)
+            {
+                this.url = url;
+                this.capability = capability;
+            }
+            public string url;
+            public serverCapability capability;
+        }
+
+        private List<Mirror> mirrors = new List<Mirror> ();
+        void GetMirrorList ()
+        {
+            HttpRequest request = new HttpRequest (string.Format ("http://www.thetvdb.com/api/{0}/mirrors.xml", API_KEY));
+            request.GetResponse ();
+            XmlDocument doc = new XmlDocument();
+            using (Stream stream = request.GetResponseStream ()) {
+                doc.Load (stream);
+            }
+            foreach (XmlNode mirror_node in doc.DocumentElement.SelectNodes("/Mirrors/Mirror")) {
+                mirrors.Add (new Mirror (mirror_node["mirrorpath"].InnerXml, (serverCapability)Int32.Parse(mirror_node["typemask"].InnerXml)));
+            }
+        }
+#endregion
+
+        //rarely updated so hardcoded
+        private List<string> languages = new List<string> () {"da", "fi", "nl", "de", "it", "es", "fr", "pl", "hu", "el", "tr",
+            "ru", "he", "ja", "pt", "zh", "cs", "sl", "hr", "ko", "en", "sv", "no"};
+        /*void GetLanguageList ()
+        {
+            HttpRequest request = new HttpRequest (string.Format ("http://www.thetvdb.com/api/{0}/languages.xml", API_KEY));
+            request.GetResponse ();
+            XmlDocument doc = new XmlDocument();
+            using (Stream stream = request.GetResponseStream ()) {
+                doc.Load (stream);
+            }
+
+            foreach (XmlNode item_node in doc.DocumentElement.SelectNodes ("/Languages/Language")) {
+                languages.Add (item_node["abbreviation"].InnerXml);
+            }
+        }*/
+
+        private string GetSerieId (DatabaseTrackInfo track)
+        {
+            string lang;
+            if (languages.Contains (CultureInfo.CurrentCulture.TwoLetterISOLanguageName)) {
+                lang = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
+            } else {
+                lang = "All";
+            }
+            string serieName = track.ArtistName.Replace ('.', ' ').Replace ('_', ' ').Replace ('-', ' ');
+            string url = string.Format("http://www.thetvdb.com/api/GetSeries.php?seriesname={0}&language={1}", HttpUtility.UrlEncode (serieName), lang);
+            Log.Debug ("tvdb webservice ; " + url);
+            HttpRequest request = new HttpRequest (url);
+            XmlDocument doc = new XmlDocument();
+            try {
+                request.GetResponse ();
+                using (Stream stream = request.GetResponseStream ()) {
+                    doc.Load (stream);
+                    Log.Debug (doc.InnerXml);
+                }
+
+                XmlNode item_node = doc.DocumentElement.SelectSingleNode ("/Data/Series");
+
+                return item_node["seriesid"].InnerXml;
+            } catch (WebException ex) {
+                Log.Debug (ex.Status.ToString ());
+                Log.DebugException (ex);
+            } catch (Exception e) {
+               Log.Error (doc.InnerXml);
+               Log.DebugException (e);
+            }
+            return string.Empty;
+        }
+
+        private VideoInfo GetSerieMetadata (DatabaseTrackInfo track, string seriesid)
+        {
+            string lang;
+            if (languages.Contains (CultureInfo.CurrentCulture.TwoLetterISOLanguageName)) {
+                lang = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
+            } else {
+                lang = "en";
+            }
+            HttpRequest request = new HttpRequest (string.Format("http://www.thetvdb.com/api/{0}/series/{1}/{2}.xml", API_KEY, seriesid, lang));
+            try {
+                request.GetResponse ();
+
+                XmlDocument doc = new XmlDocument();
+                using (Stream stream = request.GetResponseStream ()) {
+                    doc.Load (stream);
+                }
+
+                XmlNode serie_node = doc.DocumentElement.SelectSingleNode ("/Data/Series");
+                if (serie_node == null) {
+                    return null;
+                }
+                //create a new one as parent for serie
+                VideoInfo video_info = new VideoInfo ();
+
+                //serie_node["Genre"] //piped genre
+                //video_info.SerieId = seriesid;
+                video_info.Studios = serie_node["Network"].InnerXml;
+                video_info.Summary = serie_node["Overview"].InnerXml;
+                video_info.Title = serie_node["SeriesName"].InnerXml;
+                video_info.Language = serie_node["Language"].InnerXml;
+                video_info.ImDbId = serie_node["IMDB_ID"].InnerXml;
+                video_info.VideoType = (int)videoType.Serie;
+                video_info.ExternalVideoId = seriesid;
+                video_info.ParentId = 0;
+                video_info.ReleaseDate = DateTime.ParseExact (serie_node["FirstAired"].InnerXml, "yyyy-MM-dd", CultureInfo.InvariantCulture.DateTimeFormat);
+
+                video_info.Save ();
+
+                string actors = serie_node["Actors"].InnerXml;
+                if (actors != null) {
+                    foreach (string actor in actors.Split(new string[] {"|"}, StringSplitOptions.RemoveEmptyEntries)) {
+                        CastingMember member = new CastingMember ();
+                        member.VideoID = video_info.DbId;
+                        member.Name = actor;
+                        member.Job = "actor";
+                        member.Save ();
+                    }
+                }
+
+                return video_info;
+            }
+            catch (Exception e) {
+               Log.DebugException (e);
+               return null;
+            }
+        }
+
+        private void GetEpisodeMetadata (DatabaseTrackInfo track, string seriesid, int parentid)
+        {
+            string lang;
+            if (languages.Contains (CultureInfo.CurrentCulture.TwoLetterISOLanguageName)) {
+                lang = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
+            } else {
+                lang = "en";
+            }
+            string url = string.Format("http://www.thetvdb.com/api/{0}/series/{1}/default/{2}/{3}/{4}.xml", API_KEY, seriesid, HttpUtility.UrlEncode (track.AlbumTitle), track.TrackNumber, lang);
+            Log.Debug (url);
+            HttpRequest request = new HttpRequest (url);
+            try {
+                request.GetResponse ();
+                XmlDocument doc = new XmlDocument();
+                using (Stream stream = request.GetResponseStream ()) {
+                    doc.Load (stream);
+                }
+
+                XmlNode episode_node = doc.DocumentElement.SelectSingleNode ("/Data/Episode");
+
+                //track.TrackNumber
+                VideoInfo video_info = (VideoInfo)track.ExternalObject;
+                video_info.Title = episode_node["EpisodeName"].InnerXml;
+                video_info.Language = episode_node["Language"].InnerXml;
+                video_info.ImDbId = episode_node["IMDB_ID"].InnerXml;
+                video_info.ReleaseDate = DateTime.ParseExact (episode_node["FirstAired"].InnerXml, "yyyy-MM-dd", CultureInfo.InvariantCulture.DateTimeFormat);
+                video_info.Summary = episode_node["Overview"].InnerXml;
+                video_info.Title = episode_node["EpisodeName"].InnerXml;
+                video_info.VideoType = (int)videoType.SerieEpisode;
+                video_info.ExternalVideoId = episode_node["seriesid"].InnerXml;
+                video_info.ParentId = parentid;
+                video_info.Save ();
+
+                CastingMember member;
+                string members = episode_node["Director"].InnerXml;
+                if (members != null) {
+                    foreach (string director_name in members.Split(new string[] {"|"}, StringSplitOptions.RemoveEmptyEntries)) {
+                        member = new CastingMember ();
+                        member.VideoID = video_info.DbId;
+                        member.Name = director_name;
+                        member.Job = "Director";
+                        member.Save ();
+                    }
+                }
+
+                members = episode_node["GuestStars"].InnerXml;
+                if (members != null) {
+                    foreach (string guest_name in members.Split(new string[] {"|"}, StringSplitOptions.RemoveEmptyEntries)) {
+                        member = new CastingMember ();
+                        member.VideoID = video_info.DbId;
+                        member.Name = guest_name;
+                        member.Job = "GuestStar";
+                        member.Save ();
+                    }
+                }
+
+                members = episode_node["Writer"].InnerXml;
+                if (members != null) {
+                    foreach (string writer_name in members.Split(new string[] {"|"}, StringSplitOptions.RemoveEmptyEntries)) {
+                        member = new CastingMember ();
+                        member.VideoID = video_info.DbId;
+                        member.Name = writer_name;
+                        member.Job = "Writer";
+                        member.Save ();
+                    }
+                }
+                //not needed because ever done by regexp
+                //track.TrackNumber = episode_node["EpisodeNumber"].InnerXml;
+                //track.AlbumTitle = episode_node["SeasonNumber"].InnerXml;
+                //TODO
+                // <filename>episodes/80348-332179.jpg</filename>
+
+            }
+            catch (Exception e) {
+               Log.DebugException (e);
+            }
+        }
+
+        private void GetBanners (DatabaseTrackInfo track, string seriesid, string coverArtId)
+        {
+            HttpRequest request = new HttpRequest (string.Format("http://www.thetvdb.com/api/{0}/series/{1}/banners.xml", API_KEY, seriesid));
+            try {
+                request.GetResponse ();
+                XmlDocument doc = new XmlDocument();
+                using (Stream stream = request.GetResponseStream ()) {
+                    doc.Load (stream);
+                }
+
+                foreach (XmlNode banner_node in doc.DocumentElement.SelectNodes ("/Banners/Banner")) {
+                    LoadImage (banner_node, coverArtId);
+                }
+            }
+            catch (Exception e) {
+               Log.DebugException (e);
+            }
+        }
+
+        private bool LoadImage (XmlNode obj, string cover_art_id)
+        {
+            string image_type = obj["BannerType"].InnerXml;
+            string image_size = obj["BannerType2"].InnerXml;
+            string image_url = obj["BannerPath"].InnerXml;
+
+            if (image_type != "season" && image_size != "season")
+                return false;
+
+            image_url = "http://www.thetvdb.com/banners/" + image_url;
+            Log.Debug (image_url);
+            if (SaveHttpStreamCover (new Uri (image_url), cover_art_id, null)) {
+                Banshee.Sources.Source src = ServiceManager.SourceManager.ActiveSource;
+                if (src != null && (src is VideoLibrarySource || src.Parent is VideoLibrarySource)) {
+                    (src as Banshee.Sources.DatabaseSource).Reload ();
+                }
+                return true;
+            }
+            return false;
+        }
+    }
+}
+
diff --git a/src/Extensions/Banshee.Video/Banshee.Video.Metadata/TvdbMetadataProvider.cs b/src/Extensions/Banshee.Video/Banshee.Video.Metadata/TvdbMetadataProvider.cs
new file mode 100644
index 0000000..c7d37e3
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video.Metadata/TvdbMetadataProvider.cs
@@ -0,0 +1,43 @@
+// 
+// TvdbMetadataProvider.cs
+// 
+// Author:
+//   Olivier Dufour <olivier.duff@gmail.com>
+// 
+// Copyright 2011 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using Banshee.Collection;
+using Banshee.Metadata;
+namespace Banshee.Video.Metadata
+{
+    public class TvdbMetadataProvider : BaseMetadataProvider
+    {
+        public TvdbMetadataProvider ()
+        {
+        }
+
+        public override IMetadataLookupJob CreateJob (IBasicTrackInfo track)
+        {
+            return new TvdbMetadataJob (track);
+        }
+    }
+}
+
diff --git a/src/Extensions/Banshee.Video/Banshee.Video.addin.xml b/src/Extensions/Banshee.Video/Banshee.Video.addin.xml
new file mode 100644
index 0000000..af2a637
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video.addin.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Addin 
+    id="Banshee.Video"
+    version="1.0"
+    compatVersion="1.0"
+    copyright="Copyright © 2010 Olivier Dufour. Licensed under the MIT X11 license."
+    name="Video library"
+    category="Core"
+    description="Browse and manage your video library."
+    author="Olivier Dufour"
+    url="http://banshee.fm/"
+    defaultEnabled="true">
+
+  <Dependencies>
+    <Addin id="Banshee.Services" version="1.0"/>
+    <Addin id="Banshee.ThickClient" version="1.0"/>
+  </Dependencies>
+
+  <Extension path="/Banshee/ServiceManager/Service">
+    <Service class="Banshee.Video.VideoService"/>
+  </Extension>
+
+  <Extension path="/Banshee/ThickClient/ContextPane">
+    <ContextPage class="Banshee.Video.ContextPage"/>
+  </Extension>
+</Addin>
+
diff --git a/src/Extensions/Banshee.Video/Banshee.Video.csproj b/src/Extensions/Banshee.Video/Banshee.Video.csproj
new file mode 100644
index 0000000..ef9de43
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video.csproj
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.21022</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{DD7AF1FC-80DE-4382-8FC8-38BD6D66A2B8}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>Banshee.Video</RootNamespace>
+    <AssemblyName>Banshee.Video</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <ReleaseVersion>1.3</ReleaseVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>none</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\Core\Banshee.Core\Banshee.Core.csproj">
+      <Project>{2ADB831A-A050-47D0-B6B9-9C19D60233BB}</Project>
+      <Name>Banshee.Core</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Core\Banshee.Services\Banshee.Services.csproj">
+      <Project>{B28354F0-BA87-44E8-989F-B864A3C7C09F}</Project>
+      <Name>Banshee.Services</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Core\Banshee.ThickClient\Banshee.ThickClient.csproj">
+      <Project>{AC839523-7BDF-4AB6-8115-E17921B96EC6}</Project>
+      <Name>Banshee.ThickClient</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Hyena\Hyena\Hyena.csproj">
+      <Project>{95374549-9553-4C1E-9D89-667755F90E12}</Project>
+      <Name>Hyena</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Hyena\Hyena.Data.Sqlite\Hyena.Data.Sqlite.csproj">
+      <Project>{95374549-9553-4C1E-9D89-667755F90E13}</Project>
+      <Name>Hyena.Data.Sqlite</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\Hyena\Hyena.Gui\Hyena.Gui.csproj">
+      <Project>{C856EFD8-E812-4E61-8B76-E3583D94C233}</Project>
+      <Name>Hyena.Gui</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="Banshee.Video\" />
+    <Folder Include="Banshee.Video.Metadata\" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Banshee.Video.addin.xml">
+      <LogicalName>Banshee.Video.addin.xml</LogicalName>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Banshee.Video\VideoService.cs" />
+    <Compile Include="Banshee.Video\VideoLibrarySource.cs" />
+    <Compile Include="Banshee.Video\TvShowGroupSource.cs" />
+    <Compile Include="Banshee.Video\MovieGroupSource.cs" />
+    <Compile Include="Banshee.Video\VideoInfo.cs" />
+    <Compile Include="Banshee.Video\CastingMember.cs" />
+    <Compile Include="Banshee.Video.Metadata\ImdbMetadataProvider.cs" />
+    <Compile Include="Banshee.Video.Metadata\TmdbMetadataProvider.cs" />
+    <Compile Include="Banshee.Video.Metadata\TvdbMetadataProvider.cs" />
+    <Compile Include="Banshee.Video.Metadata\TmdbMetadataJob.cs" />
+    <Compile Include="Banshee.Video.Metadata\TvdbMetadataJob.cs" />
+    <Compile Include="Banshee.Video.Metadata\ImdbMetadataJob.cs" />
+    <Compile Include="Banshee.Video\VideoSourceContents.cs" />
+    <Compile Include="Banshee.Video\VideoInfoView.cs" />
+    <Compile Include="Banshee.Video\VideoInfoModel.cs" />
+    <Compile Include="Banshee.Video\ContextPage.cs" />
+    <Compile Include="Banshee.Video\VideoDetailPanel.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <ProjectExtensions>
+    <MonoDevelop>
+      <Properties>
+        <MonoDevelop.Autotools.MakefileInfo IntegrationEnabled="true" RelativeMakefileName="Makefile" IsAutotoolsProject="true" RelativeConfigureInPath="../../..">
+          <BuildFilesVar Sync="true" Name="SOURCES" />
+          <DeployFilesVar />
+          <ResourcesVar Sync="true" Name="RESOURCES" />
+          <OthersVar />
+          <GacRefVar Name="BOO_LIBS" Prefix="-r:" />
+          <AsmRefVar Name="BOO_LIBS" Prefix="-r:" />
+          <ProjectRefVar Name="BOO_LIBS" Prefix="-r:" />
+        </MonoDevelop.Autotools.MakefileInfo>
+      </Properties>
+    </MonoDevelop>
+  </ProjectExtensions>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="ICSharpCode.SharpZipLib" />
+    <Reference Include="System.Xml" />
+    <Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+    <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/Extensions/Banshee.Video/Banshee.Video/CastingMember.cs b/src/Extensions/Banshee.Video/Banshee.Video/CastingMember.cs
new file mode 100644
index 0000000..885b82c
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video/CastingMember.cs
@@ -0,0 +1,94 @@
+// 
+// CastingMember.cs
+// 
+// Author:
+//   Olivier Dufour <olivier.duff@gmail.com>
+// 
+// Copyright 2011 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+
+using Banshee.Collection;
+
+using Hyena.Data;
+using Hyena.Data.Sqlite;
+using Banshee.Database;
+using Banshee.ServiceStack;
+
+namespace Banshee.Video
+{
+    public class CastingMember : CacheableItem
+    {
+        private static SqliteModelProvider<CastingMember> provider;
+
+        public static void Init() {
+            provider= new SqliteModelProvider<CastingMember> (ServiceManager.DbConnection, "CastingMembers", true);
+        }
+
+        public static SqliteModelProvider<CastingMember> Provider {
+            get { return provider; }
+        }
+
+        [DatabaseColumn("CastingMemberID", Constraints = DatabaseColumnConstraints.PrimaryKey)]
+        private int dbid;
+        public int DbId {
+            get { return dbid; }
+        }
+
+        //Foreign key to video db table
+        [DatabaseColumn("VideoID")]
+        private int video_id;
+        public int VideoID {
+            get { return video_id; }
+            set { video_id = value; }
+        }
+
+        private string name;
+        private string character;
+        private string job;
+
+        [DatabaseColumn]
+        public string Name {
+            get { return name; }
+            set { name = value; }
+        }
+
+        //role
+        [DatabaseColumn]
+        public string Character {
+            get { return character; }
+            set { character = value; }
+        }
+
+        //director, actor, author, special guest...
+        [DatabaseColumn]
+        public string Job {
+            get { return job; }
+            set { job = value; }
+        }
+
+        public void Save ()
+        {
+            Provider.Save (this);
+        }
+    }
+}
+
+
diff --git a/src/Extensions/Banshee.Video/Banshee.Video/ContextPage.cs b/src/Extensions/Banshee.Video/Banshee.Video/ContextPage.cs
new file mode 100644
index 0000000..ea02c5c
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video/ContextPage.cs
@@ -0,0 +1,64 @@
+//
+// ContextPage.cs
+//
+// Author:
+//   Olivier Dufour <olivier.duff@gmail.com>
+//
+// Copyright (C) 2011 Olivier Dufour
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+using Banshee.ContextPane;
+using Gtk;
+using Banshee.I18n;
+
+namespace Banshee.Video
+{
+    public class ContextPage : BaseContextPage
+    {
+        public ContextPage ()
+        {
+            Id = "Video";
+            Name = Catalog.GetString ("Video Detail");
+            IconNames = new string[] { "video", "browser", "internet" };
+        }
+
+        public override void SetTrack (Banshee.Collection.TrackInfo track)
+        {
+            VideoInfo video = track.ExternalObject as VideoInfo;
+            view.VideoInfo = video;
+            view.Members = CastingMember.Provider.FetchAllMatching ("VideoID = ?", video.DbId);
+            view.Refresh ();
+        }
+
+        internal void SetState (ContextState state)
+        {
+            State = state;
+        }
+
+        private VideoDetailPanel view;
+        public override Widget Widget {
+            get { return view ?? (view = new VideoDetailPanel (this)); }
+        }
+    }
+}
diff --git a/src/Extensions/Banshee.Video/Banshee.Video/MovieGroupSource.cs b/src/Extensions/Banshee.Video/Banshee.Video/MovieGroupSource.cs
new file mode 100644
index 0000000..04c305f
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video/MovieGroupSource.cs
@@ -0,0 +1,47 @@
+//
+// MovieSource.cs
+// 
+// Author:
+//   Olivier Dufour <olivier (dot) duff (at) gmail (dot) com>
+// 
+// Copyright 2011 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+using Banshee.SmartPlaylist;
+using Banshee.Collection;
+using Banshee.Sources;
+using Banshee.I18n;
+
+namespace Banshee.Video
+{
+    public class MovieGroupSource : SmartPlaylistSource
+    {
+        public MovieGroupSource (PrimarySource parent) : base (Catalog.GetString ("Movies"), parent)
+        {
+            //Properties.Remove ("Icon.Name");
+            //Properties.SetStringList ("Icon.Name", "movie");
+            ConditionSql = String.Format ("(CoreTracks.Attributes & {0}) == {0}", (int)TrackMediaAttributes.Movie);
+            Save ();
+        }
+    }
+}
+
diff --git a/src/Extensions/Banshee.Video/Banshee.Video/TvShowGroupSource.cs b/src/Extensions/Banshee.Video/Banshee.Video/TvShowGroupSource.cs
new file mode 100644
index 0000000..81753fd
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video/TvShowGroupSource.cs
@@ -0,0 +1,47 @@
+// 
+// TvShowSource.cs
+// 
+// Author:
+//   Olivier Dufour <olivier (dot) duff (at) gmail (dot) com>
+// 
+// Copyright 2011 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+using Banshee.SmartPlaylist;
+using Banshee.Collection;
+using Banshee.Sources;
+using Banshee.I18n;
+
+namespace Banshee.Video
+{
+    public class TvShowGroupSource : SmartPlaylistSource
+    {
+        public TvShowGroupSource (PrimarySource parent) : base (Catalog.GetString ("TvShows"), parent)
+        {
+            //Properties.Remove ("Icon.Name");
+            //Properties.SetStringList ("Icon.Name", "tshow");
+            ConditionSql = String.Format ("(CoreTracks.Attributes & {0}) == {0}", (int)TrackMediaAttributes.TvShow);
+            Save ();
+        }
+    }
+}
+
diff --git a/src/Extensions/Banshee.Video/Banshee.Video/VideoDetailPanel.cs b/src/Extensions/Banshee.Video/Banshee.Video/VideoDetailPanel.cs
new file mode 100644
index 0000000..e7df9b6
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video/VideoDetailPanel.cs
@@ -0,0 +1,251 @@
+//
+// RecommendationPane.cs
+//
+// Author:
+//   Olivier Dufour <olivier.duff@gmail.com>
+//
+// Copyright (C) 2011 Olivier Dufour
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Gtk;
+using Banshee.Base;
+using Banshee.Collection.Gui;
+using Banshee.ServiceStack;
+using Banshee.ContextPane;
+using Hyena;
+
+namespace Banshee.Video
+{
+    public class VideoDetailPanel : HBox
+    {
+        private ContextPage context_page;
+        private HBox main_box;
+        private VBox left;
+        private VBox right;
+        private Image image;
+        private Label title_label;
+        private TextView summary_textview;
+        private Table urls_table;
+        private Table members_table;
+        private Label origine_label;
+        private Label studios_label;
+        private Label release_date_label;
+        
+        private LinkButton info_url_link;
+        private LinkButton homepage_url_link;
+        private LinkButton trailer_url_link;
+        private ArtworkManager artwork_manager;
+
+        public VideoDetailPanel (ContextPage contextPage) : base ()
+        {
+            this.context_page = contextPage;
+            main_box = this;
+            main_box.BorderWidth = 5;
+            left = new VBox ();
+            right = new VBox ();
+            left.WidthRequest = 160;
+
+            left.PackStart (image= new Image (), false, true, 5);
+            origine_label = new Label ();
+            origine_label.Justify = Justification.Left;
+            left.PackStart (origine_label, true, true, 5);
+
+            studios_label = new Label ();
+            studios_label.Justify = Justification.Left;
+            left.PackStart (studios_label, true, true, 5);
+
+
+            release_date_label = new Label ();
+            release_date_label.Justify = Justification.Left;
+            left.PackStart (release_date_label, true, true, 5);
+
+            title_label = new Label ();
+            title_label.Justify = Justification.Center;
+            right.PackStart (title_label, false, false, 5);
+
+            summary_textview = new TextView ();
+            summary_textview.Editable = false;
+            summary_textview.WrapMode = WrapMode.WordChar;
+            right.PackStart (summary_textview, true, true, 1);
+
+            right.PackStart (members_table = new Table (2, 2, false), false, true, 1);
+
+            right.PackStart (urls_table = new Table (3, 2, false));
+            Label temp_label = new Label ();
+            temp_label.Markup = "<span weight=\"bold\">Info URL:</span>";
+            //temp_label.Justify = Justification.Right;
+            temp_label.SetAlignment (1f, 0.5f);
+            urls_table.Attach (temp_label, 0, 1, 0, 1,
+                    AttachOptions.Expand | AttachOptions.Fill,
+                    AttachOptions.Shrink, 0, 0);
+            temp_label = new Label ();
+            temp_label.Markup = "<span weight=\"bold\">Homepage URL:</span>";
+            //temp_label.Justify = Justification.Right;
+            temp_label.SetAlignment (1f, 0.5f);
+            urls_table.Attach (temp_label, 0, 1, 1, 2,
+                    AttachOptions.Expand | AttachOptions.Fill,
+                    AttachOptions.Shrink, 0, 0);
+            temp_label = new Label ();
+            temp_label.Markup = "<span weight=\"bold\">Trailer URL:</span>";
+            //temp_label.Justify = Justification.Right;
+            temp_label.SetAlignment (1f, 0.5f);
+            urls_table.Attach (temp_label, 0, 1, 2, 3,
+                    AttachOptions.Expand | AttachOptions.Fill,
+                    AttachOptions.Shrink, 0, 0);
+            urls_table.Attach (info_url_link = new LinkButton (String.Empty), 1, 2, 0, 1,
+                    AttachOptions.Expand | AttachOptions.Fill,
+                    AttachOptions.Shrink, 0, 0);
+            urls_table.Attach (homepage_url_link = new LinkButton (String.Empty), 1, 2, 1, 2,
+                    AttachOptions.Expand | AttachOptions.Fill,
+                    AttachOptions.Shrink, 0, 0);
+            urls_table.Attach (trailer_url_link = new LinkButton (String.Empty), 1, 2, 2, 3,
+                    AttachOptions.Expand | AttachOptions.Fill,
+                    AttachOptions.Shrink, 0, 0);
+            
+
+            main_box.PackStart (left, false, true, 5);
+            main_box.PackStart (new VSeparator (), false, false, 0);
+            main_box.PackStart (right, true, true, 5);
+
+            artwork_manager = ServiceManager.Get<ArtworkManager> ();
+            // TODO
+            /*
+            ParentId ==> link to main serie
+            VideoType 
+                            */
+
+        }
+
+        public void Refresh ()
+        {
+            if (video_info == null) {
+                return;
+            }
+            context_page.SetState (ContextState.Loading);
+            Gdk.Pixbuf pixbuf = artwork_manager.LookupPixbuf (video_info.ArtworkId);
+            Log.Debug (pixbuf == null? "pixbuf NULL": "pixbuf not null");
+            if (pixbuf != null) {
+                int width = pixbuf.Width, height = pixbuf.Height;
+
+                if (height >= 110) {
+                    width = (int)((width * 110) / height);
+                    height = 110;
+                }
+
+                if (width >= 110) {
+                    height = (int)((height * 110) / width);
+                    width = 110;
+                }
+                Log.Debug (String.Format ("w:{0} h:{1} ", width, height));
+                if (width != pixbuf.Width || height != pixbuf.Height) {
+                    image.Pixbuf = pixbuf.ScaleSimple (width, height, Gdk.InterpType.Bilinear);
+                } else {
+                    image.Pixbuf = pixbuf;
+                }
+            } else {
+                image.Hide ();
+            }
+            title_label.Markup = String.Format ("<span size=\"x-large\" weight=\"bold\">{0}</span>", video_info.Title);
+            summary_textview.Buffer.Text = video_info.Summary;
+            if (!String.IsNullOrEmpty (video_info.AlternativeTitle) && video_info.AlternativeTitle == video_info.OriginalTitle) {
+                origine_label.Markup = String.Format ("<span weight=\"bold\">{0}</span> ({1}) from {2}", String.IsNullOrEmpty (video_info.OriginalTitle) ? video_info.Title : video_info.OriginalTitle, video_info.AlternativeTitle, video_info.Country);
+            } else if(!String.IsNullOrEmpty (video_info.Country)) {
+                origine_label.Markup = String.Format ("<span weight=\"bold\">{0}</span> from {1}", String.IsNullOrEmpty (video_info.OriginalTitle) ? video_info.Title : video_info.OriginalTitle, video_info.Country);
+            } else {
+                origine_label.Markup = String.Format ("<span weight=\"bold\">{0}</span>", String.IsNullOrEmpty (video_info.OriginalTitle) ? video_info.Title : video_info.OriginalTitle);
+            }
+
+            studios_label.Markup = String.Format ("<span size=\"small\">Studios: {0}</span>", video_info.Studios);
+            release_date_label.Markup = String.Format ("<span size=\"small\">Release date: {0}</span>", video_info.ReleaseDate.ToShortDateString ());
+
+            if (!String.IsNullOrEmpty (video_info.InfoUrl)) {
+                info_url_link.Uri = video_info.InfoUrl;
+            } else {
+                //TODO Hide row
+            }
+            if (!String.IsNullOrEmpty (video_info.HomepageUrl)) {
+                homepage_url_link.Uri = video_info.HomepageUrl;
+            }
+            if (!String.IsNullOrEmpty (video_info.TrailerUrl)) {
+                trailer_url_link.Uri = video_info.TrailerUrl;
+            }
+
+            if (members != null) {
+                RefreshMembers ();
+            }
+
+            main_box.ShowAll ();
+            context_page.SetState (ContextState.Loaded);
+        }
+
+        public void RefreshMembers ()
+        {
+            List<string> jobs = new List<string> ();
+            foreach (var member in members) {
+                if (!jobs.Contains (member.Job)) {
+                    jobs.Add (member.Job);
+                }
+            }
+            if (jobs.Count == 0) {
+                members_table.Hide ();
+                return;
+            }
+            members_table.Resize ((uint)jobs.Count, 2);
+
+            for (int i = 0; i < jobs.Count; i++) {
+                string job = jobs[i];
+
+                StringBuilder builder = new StringBuilder ();
+                builder.AppendFormat ("{0}: ", job);
+                foreach (var member in members) {
+                    if (member.Job != job) {
+                        continue;
+                    }
+                    if (!String.IsNullOrEmpty (member.Character)) {
+                        builder.AppendFormat ("{0} ({1}), ", member.Name, member.Character);
+                    } else {
+                        builder.AppendFormat ("{0}, ", member.Name);
+                    }
+                }
+                builder.Remove (builder.Length - 2, 1);
+                TextView view = new TextView ();
+                view.Editable = false;
+                view.WrapMode = WrapMode.WordChar;
+                view.Justification = Justification.Left;
+                view.Buffer.Text = builder.ToString ();
+                members_table.Attach (view, 0, 2, (uint)i, (uint)(i + 1),
+                    AttachOptions.Expand | AttachOptions.Fill,
+                    AttachOptions.Shrink, 0, 0);
+
+            }
+        }
+        private VideoInfo video_info;
+        public VideoInfo VideoInfo {set {video_info = value;}}
+        
+        private IEnumerable<CastingMember> members;
+        public IEnumerable<CastingMember> Members {set {members = value;}}
+    }
+}
diff --git a/src/Extensions/Banshee.Video/Banshee.Video/VideoInfo.cs b/src/Extensions/Banshee.Video/Banshee.Video/VideoInfo.cs
new file mode 100644
index 0000000..75cded2
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video/VideoInfo.cs
@@ -0,0 +1,196 @@
+using System;
+//using System.Data;
+
+using Mono.Unix;
+
+using Hyena.Data;
+using Hyena.Data.Sqlite;
+
+using Banshee.Database;
+using Banshee.ServiceStack;
+using Banshee.Collection;
+using Hyena;
+
+namespace Banshee.Video
+{
+    public enum videoType : int {
+        SerieEpisode = 0,
+        SerieSaison = 1,
+        Serie = 2,
+        Movie = 3,
+        Other = 4
+    }
+
+    public class VideoInfo : CacheableItem
+    {
+        private static SqliteModelProvider<VideoInfo> provider;
+
+        public static void Init() {
+            provider= new SqliteModelProvider<VideoInfo> (ServiceManager.DbConnection, "Videos", true);
+        }
+
+        public static SqliteModelProvider<VideoInfo> Provider {
+            get { return provider; }
+        }
+
+        public static readonly string UnknownTitle = Catalog.GetString ("Unknown Title");
+
+        public static void Reset ()
+        {
+            //last_Video = null;
+        }
+
+        public VideoInfo ()
+        {
+        }
+
+        public void Save ()
+        {
+            Provider.Save (this);
+        }
+
+        private string imdb_id;
+        private DateTime release_date = DateTime.MinValue;
+        private string language;
+        private string title;
+        private string original_title;
+        private string alternative_title;
+        private string info_url;
+        private string homepage_url;
+        private string trailer_url;
+        private string summary;
+        private string studios;
+        private string country;
+        private int parent;
+        private string external_video_id;
+        private int video_type;
+
+        [DatabaseColumn("VideoID", Constraints = DatabaseColumnConstraints.PrimaryKey)]
+        private int dbid;
+        public int DbId {
+            get { return dbid; }
+        }
+
+        [DatabaseColumn("Language")]
+        public string Language {
+            get { return language; }
+            set { language = value; }
+        }
+
+        [DatabaseColumn("IMDBID")]
+        public string ImDbId {
+            get { return imdb_id; }
+            set { imdb_id = value; }
+        }
+
+        [DatabaseColumn]
+        public DateTime ReleaseDate {
+            get { return release_date; }
+            set { release_date = value; }
+        }
+
+        [DatabaseColumn]
+        public string Title {
+            get { return title; }
+            set { title = value; }
+        }
+
+        [DatabaseColumn(Select = false)]
+        internal string TitleLowered {
+            get { return Hyena.StringUtil.SearchKey (Title); }
+        }
+
+        [DatabaseColumn]
+        public string OriginalTitle {
+            get { return original_title; }
+            set { original_title = value; }
+        }
+
+        [DatabaseColumn(Select = false)]
+        internal string OriginalTitleLowered {
+            get { return Hyena.StringUtil.SearchKey (OriginalTitle); }
+        }
+
+        [DatabaseColumn]
+        public string AlternativeTitle {
+            get { return alternative_title; }
+            set { alternative_title = value; }
+        }
+
+        [DatabaseColumn(Select = false)]
+        internal string AlternativeTitleLowered {
+            get { return Hyena.StringUtil.SearchKey (AlternativeTitle); }
+        }
+
+        [DatabaseColumn]
+        public string InfoUrl {
+            get { return info_url; }
+            set { info_url = value; }
+        }
+
+        [DatabaseColumn]
+        public string HomepageUrl {
+            get { return homepage_url; }
+            set { homepage_url = value; }
+        }
+
+        [DatabaseColumn]
+        public string TrailerUrl {
+            get { return trailer_url; }
+            set { trailer_url = value; }
+        }
+
+        [DatabaseColumn]
+        public string Summary {
+            get { return summary; }
+            set { summary = value; }
+        }
+
+        //MGM, miramax, dreamworks, ...)
+        [DatabaseColumn]
+        public string Studios {
+            get { return studios; }
+            set { studios = value; }
+        }
+
+        [DatabaseColumn]
+        public string Country {
+            get { return country; }
+            set { country = value; }
+        }
+
+        // For tvshow episode link to main description
+        [DatabaseColumn]
+        public int ParentId {
+            get { return parent; }
+            set { parent = value; }
+        }
+
+        [DatabaseColumn]
+        public string ExternalVideoId {
+            get { return external_video_id; }
+            set { external_video_id = value; }
+        }
+
+        [DatabaseColumn]
+        public int VideoType {
+            get { return video_type; }
+            set { video_type = value; }
+        }
+
+        public string DisplayTrackTitle {
+            get { return StringUtil.MaybeFallback (Title, UnknownTitle); }
+        }
+
+        public override string ToString ()
+        {
+            return String.Format ("<LibraryVideoInfo Title={0} DbId={1}>", Title, DbId);
+        }
+
+        public string ArtworkId {
+            get {
+                return String.Format ("video-{0}-{1}", VideoType, ExternalVideoId);
+            }
+        }
+    }
+}
diff --git a/src/Extensions/Banshee.Video/Banshee.Video/VideoInfoModel.cs b/src/Extensions/Banshee.Video/Banshee.Video/VideoInfoModel.cs
new file mode 100644
index 0000000..a6d6a55
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video/VideoInfoModel.cs
@@ -0,0 +1,109 @@
+// 
+// VideoInfoModel.cs
+// 
+// Author:
+//   Olivier Dufour <olivier.duff@gmail.com>
+// 
+// Copyright 2011 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using Banshee.Collection.Database;
+using Banshee.Database;
+using Banshee.I18n;
+using Hyena.Query;
+using System.Text;
+namespace Banshee.Video
+{
+    //TODO DatabaseVideoInfo, VideoInfo
+    public class VideoInfoModel : DatabaseFilterListModel<VideoInfo, VideoInfo>
+    {
+        public VideoInfoModel (Banshee.Sources.DatabaseSource source, DatabaseTrackListModel trackModel, BansheeDbConnection connection, string uuid)
+            : base ("Videos", Catalog.GetString ("Videos"),
+                    source, trackModel, connection, VideoInfo.Provider, new VideoInfo (), uuid)
+        {
+            QueryFields = new QueryFieldSet (VideoService.VideoTitleField, VideoService.VideoOriginalTitleField, VideoService.VideoAlternativeTitleField );
+
+            //int video_library_dbid = (source as VideoLibrarySource ?? source.Parent as VideoLibrarySource).DbId;
+            ReloadFragmentFormat = @"
+                    FROM Videos WHERE EXISTS
+                        (SELECT * FROM CoreTracks, CoreCache{0}
+                            WHERE Videos.ParentId = 0
+                            AND   CoreTracks.ExternalID = Videos.VideoID
+                            AND   CoreCache.ModelID = {1} AND CoreCache.ItemId = {2} {3}
+                        )
+                        OR EXISTS
+                        (SELECT * FROM Videos AS episode, CoreTracks, CoreCache{0}
+                            WHERE episode.ParentId != 0
+                            AND   episode.ParentId = Videos.VideoID
+                            AND   CoreTracks.ExternalID = episode.VideoID
+                            AND   CoreCache.ModelID = {1} AND CoreCache.ItemId = {2} {3}
+                        )
+                    ORDER BY Videos.Title";
+        }
+
+        protected override string ItemToFilterValue (object o)
+        {
+            return null;
+        }
+
+        public override string FilterColumn { get {return null;} }
+
+        public override string GetSqlFilter ()
+        {
+            if (Selection.AllSelected)
+                return null;
+
+            int count = 0;
+            StringBuilder builder = new StringBuilder ();
+
+            foreach (object item in GetSelectedObjects ()) {
+                VideoInfo video = item as VideoInfo;
+                if (video != null) {
+                    builder.AppendFormat ("{0}, ", video.DbId.ToString ());
+                    count++;
+                }
+            }
+
+            if(count > 0) {
+                // Remove the last comma
+                builder.Remove(builder.Length - 2, 1);
+                builder.Insert(0, @" EXISTS
+                        (SELECT * FROM Videos, Videos AS episode
+                            WHERE ( (Videos.ParentId = 0
+                            AND   CoreTracks.ExternalID = Videos.VideoID
+                            AND   episode.VideoID = Videos.VideoID)
+                            OR (  episode.ParentId != 0
+                            AND   episode.ParentId = Videos.VideoID
+                            AND   CoreTracks.ExternalID = episode.VideoID ) )
+                            AND Videos.VideoID in (");
+                builder.Append(") )");
+
+                return builder.ToString();
+            }
+            return " 1=1";
+        }
+
+        public override void UpdateSelectAllItem (long count)
+        {
+            select_all_item.Title = String.Format (Catalog.GetString ("All Videos ({0})"), count);
+        }
+    }
+}
+
diff --git a/src/Extensions/Banshee.Video/Banshee.Video/VideoInfoView.cs b/src/Extensions/Banshee.Video/Banshee.Video/VideoInfoView.cs
new file mode 100644
index 0000000..d8623f4
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video/VideoInfoView.cs
@@ -0,0 +1,82 @@
+// 
+// VideoInfoView.cs
+// 
+// Author:
+//   Olivier Dufour <olivier.duff@gmail.com>
+// 
+// Copyright 2011 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+
+using Banshee.Collection.Gui;
+
+using Hyena.Data.Gui;
+using Hyena.Gui.Canvas;
+using Banshee.ServiceStack;
+using Hyena;
+
+namespace Banshee.Video
+{
+    public class VideoInfoView : TrackFilterListView<VideoInfo>
+    {
+        public VideoInfoView ()
+        {
+            var layout = new DataViewLayoutGrid () {
+                Fill = true,
+                ChildAllocator = () => {
+                    DataViewChildImage img = new DataViewChildImage () {
+                        ImageSize = 90
+                    };
+                    return new StackPanel () {
+                        Margin = new Thickness (5),
+                        Orientation = Orientation.Vertical,
+                        Width = 100,
+                        Height = 150,
+                        Spacing = 5,
+                        Children = {
+                                img,
+                                new TextBlock () {
+                                    UseMarkup = true,
+                                    TextWrap = TextWrap.WordChar,
+                                    TextGenerator = o => {
+                                        var track = o as VideoInfo;
+                                        if (track != null) {
+                                            return string.Format ("<b>{0}</b>\n<small>{1}\n</small>",
+                                                    track.Title, track.ReleaseDate.ToShortDateString ());
+                                        }
+                                        return "";
+                                    }
+                                }
+                            },
+
+                        // Render the prelight just on the cover art, but triggered by being anywhere over the album
+                        PrelightRenderer = (cr, theme, size, o) => {
+                            Prelight.Gradient (cr, theme, new Rect (img.ContentAllocation.X, img.ContentAllocation.Y, img.ImageSize, img.ImageSize), o);
+                        }
+                    };
+                },
+                View = this
+            };
+
+            ViewLayout = layout;
+        }
+    }
+}
+
diff --git a/src/Extensions/Banshee.Video/Banshee.Video/VideoLibrarySource.cs b/src/Extensions/Banshee.Video/Banshee.Video/VideoLibrarySource.cs
new file mode 100644
index 0000000..f608c83
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video/VideoLibrarySource.cs
@@ -0,0 +1,137 @@
+//
+// VideoLibrarySource.cs
+//
+// Author:
+//   Aaron Bockover <abockover@novell.com>
+//   Gabriel Burt <gburt@novell.com>
+//
+// Copyright (C) 2005-2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+
+using Mono.Unix;
+
+using Banshee.SmartPlaylist;
+using Banshee.Collection;
+using Banshee.Library;
+using Banshee.Collection.Database;
+using Banshee.Sources.Gui;
+using Banshee.ServiceStack;
+
+namespace Banshee.Video
+{
+    public class VideoLibrarySource : LibrarySource
+    {
+        //TODO delete orphelan videoInfo.
+        public VideoLibrarySource () : base (Catalog.GetString ("Videos"), "VideoLibrary", 50)
+        {
+            MediaTypes = TrackMediaAttributes.VideoStream;
+            NotMediaTypes = TrackMediaAttributes.Podcast;
+            TrackExternalObjectHandler = GetVideoInfoObject;
+            TrackArtworkIdHandler = GetTrackArtworkId;
+            Properties.SetStringList ("Icon.Name", "video-x-generic", "video", "source-library");
+            Properties.Set<string> ("SearchEntryDescription", Catalog.GetString ("Search your videos"));
+            Properties.SetString ("TrackView.ColumnControllerXml", String.Format (@"
+                <column-controller>
+                  <add-all-defaults />
+                  <remove-default column=""DiscColumn"" />
+                  <remove-default column=""ComposerColumn"" />
+                  <remove-default column=""AlbumArtistColumn"" />
+                  <remove-default column=""ConductorColumn"" />
+                  <remove-default column=""ComposerColumn"" />
+                  <remove-default column=""BpmColumn"" />
+                  <sort-column direction=""asc"">track_title</sort-column>
+                  <column modify-default=""AlbumColumn"">
+                    <title>{0}</title>
+                    <long-title>{0}</long-title>
+                  </column>
+                  <column modify-default=""TrackNumberAndCountColumn"">
+                    <title>{1}</title>
+                    <long-title>{1}</long-title>
+                  </column>
+                </column-controller>
+            ", Catalog.GetString ("Season"), Catalog.GetString ("Episode")));
+            Properties.Set<ISourceContents> ("Nereid.SourceContents", new LazyLoadSourceContents<VideoSourceContents> ());
+            Properties.Set<bool> ("Nereid.SourceContentsPropagate", true);
+        }
+
+        public object GetVideoInfoObject (DatabaseTrackInfo track)
+        {
+            VideoService service = ServiceStack.ServiceManager.Get<VideoService> ();
+            return service.GetExternalObject (track);
+        }
+
+        public string GetTrackArtworkId (DatabaseTrackInfo track)
+        {
+            VideoService service = ServiceStack.ServiceManager.Get<VideoService> ();
+            return service.ArtworkIdFor (track);
+        }
+
+        public override string GetPluralItemCountString (int count)
+        {
+            return Catalog.GetPluralString ("{0} video", "{0} videos", count);
+        }
+
+        VideoInfoModel video_model;
+        protected override IEnumerable<IFilterListModel> CreateFiltersFor (Sources.DatabaseSource src)
+        {
+            if (video_model == null) {
+                video_model = new VideoInfoModel (src, src.DatabaseTrackModel, ServiceManager.DbConnection, src.UniqueId);
+            }
+            yield return video_model;
+        }
+
+        public override bool ShowBrowser {
+            get { return false; }
+        }
+
+        protected override bool HasArtistAlbum {
+            get { return false; }
+        }
+
+        public override string DefaultBaseDirectory {
+            get { return Hyena.XdgBaseDirectorySpec.GetXdgDirectoryUnderHome ("XDG_VIDEOS_DIR", "Videos"); }
+        }
+
+        public override IEnumerable<SmartPlaylistDefinition> DefaultSmartPlaylists {
+            get { return default_smart_playlists; }
+        }
+
+        protected override string SectionName {
+            get { return Catalog.GetString ("Videos Folder"); }
+        }
+
+        private static SmartPlaylistDefinition [] default_smart_playlists = new SmartPlaylistDefinition [] {
+            new SmartPlaylistDefinition (
+                Catalog.GetString ("Favorites"),
+                Catalog.GetString ("Videos rated four and five stars"),
+                "rating>=4"),
+
+            new SmartPlaylistDefinition (
+                Catalog.GetString ("Unwatched"),
+                Catalog.GetString ("Videos that haven't been played yet"),
+                "plays=0"),
+        };
+    }
+}
diff --git a/src/Extensions/Banshee.Video/Banshee.Video/VideoService.cs b/src/Extensions/Banshee.Video/Banshee.Video/VideoService.cs
new file mode 100644
index 0000000..dbe8c20
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video/VideoService.cs
@@ -0,0 +1,233 @@
+// 
+// VideoService.cs
+// 
+// Author:
+//   Olivier Dufour <olivier (dot) duff (at) gmail (dot) com>
+// 
+// Copyright 2011 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Text.RegularExpressions;
+
+using Banshee.Collection.Database;
+using Banshee.Collection;
+using Banshee.MediaEngine;
+using Banshee.ServiceStack;
+using Banshee.Metadata;
+using Banshee.Video.Metadata;
+using Hyena.Query;
+using Banshee.I18n;
+using Hyena;
+using Banshee.SmartPlaylist;
+
+
+namespace Banshee.Video
+{
+    public class VideoService : IExtensionService
+    {
+        public VideoService ()
+        {
+        }
+
+
+        public string ArtworkIdFor (DatabaseTrackInfo track)
+        {
+            VideoInfo vi = GetExternalObject (track);
+            if (vi == null) {
+                Log.Debug ("can not get artworkid");
+                return null;
+            }
+            if (vi.ParentId > 0 ) {
+                //get serie
+                vi = VideoInfo.Provider.FetchSingle (vi.ParentId);
+                if (vi == null) {
+                    Log.Debug ("can not get artworkid");
+                    return null;
+                }
+            }
+            if (String.IsNullOrEmpty (vi.ExternalVideoId)) {
+                //return that because do not want to find this file
+                //and do not return null because metadata job do not lookup in this case
+                return string.Empty;
+            }
+            return vi.ArtworkId;
+        }
+
+        public VideoInfo GetExternalObject (DatabaseTrackInfo track)
+        {
+            return VideoInfo.Provider.FetchFirstMatching ("VideoID = ?", track.ExternalId);
+        }
+
+        private readonly Regex regexp = new Regex (@"(.*)[._][\[]?[sS]?([0-9]+)[\]]?[._]?[\[]?[-EeXx]([0-9]+)[\]]?[._].*");
+        private readonly Regex regexp2 = new Regex (@"(.*)[._]([0-9])([0-9][0-9])[._].*");
+
+        private VideoLibrarySource source;
+
+        public void Initialize ()
+        {
+            VideoInfo.Init ();
+            CastingMember.Init ();
+            source = new VideoLibrarySource ();
+
+            SmartPlaylistSource tvshow_src = null;
+            SmartPlaylistSource movie_src = null;
+            foreach (SmartPlaylistSource child in source.Children) {
+                if (child.Name == Catalog.GetString ("TvShows")) {
+                    tvshow_src = child;
+                }
+                else if (child.Name == Catalog.GetString ("Movies")) {
+                    movie_src = child;
+                }
+            }
+            if (tvshow_src == null)
+                tvshow_src = new TvShowGroupSource (source);
+            source.AddChildSource (tvshow_src);
+            if (movie_src == null)
+                movie_src = new MovieGroupSource (source);
+            source.AddChildSource (movie_src);
+
+            ServiceManager.SourceManager.AddSource (source);
+            // To debug regexp uncoment this
+            /*VideoInfo.Provider.Delete ("1=1");
+            CastingMember.Provider.Delete ("1=1");
+
+            foreach (DatabaseTrackInfo track in DatabaseTrackInfo.Provider.FetchAllMatching ("PrimarySourceID = ? AND ExternalID > 0", source.DbId)) {
+                track.ExternalId = 0;
+                track.Save ();
+            }*/
+            RefreshTracks ();
+            source.TracksAdded += OnTracksAdded;
+
+            // Use regular MetadataService. It is very complicated for a small task and need a refactor to have provider by
+            // trackMediaAttibute with priority because video do not need music provider and reverse
+            // and tvshow will use tvdb in priority ...
+            MetadataService.Instance.AddProvider (new TmdbMetadataProvider ());
+            MetadataService.Instance.AddProvider (new TvdbMetadataProvider ());
+            MetadataService.Instance.AddProvider (new ImdbMetadataProvider ());
+        }
+
+        void OnTracksAdded (Sources.Source sender, Sources.TrackEventArgs args)
+        {
+            RefreshTracks ();
+        }
+
+        void RefreshTracks ()
+        {
+            Log.Debug ("Refreshtrack");
+            foreach (DatabaseTrackInfo track in DatabaseTrackInfo.Provider.FetchAllMatching ("PrimarySourceID = ? AND ExternalID = 0", source.DbId)) {
+                var video = new VideoInfo ();
+                Log.Debug (String.Format ("Create videoinfo : {0}", track.TrackTitle));
+                // Temporary set title but will be updated by webservice
+                video.Title = track.TrackTitle;
+                ParseName (track, video, System.IO.Path.GetFileName (SafeUri.UriToFilename (track.Uri)));
+                video.Save ();
+                track.ExternalId = video.DbId;
+                track.Save ();
+            }
+        }
+
+        public void ParseName (DatabaseTrackInfo track, VideoInfo video, string name)
+        {
+            int temp;
+            Match match = regexp.Match (name);
+            if (match.Success) {
+                Log.Debug ("videoinfo is tv show");
+                track.ArtistName = match.Groups[1].Value;//name of serie
+                if (Int32.TryParse (match.Groups[2].Value, out temp)) {
+                    //season: use temp to remove 0
+                    track.AlbumTitle = temp.ToString ();
+                }
+                Int32.TryParse (match.Groups[3].Value, out temp);
+                track.TrackNumber = temp;
+                track.MediaAttributes |= TrackMediaAttributes.TvShow;
+                return;
+            }
+            else {
+                match = regexp2.Match (name);
+                if (match.Success) {
+                    Log.Debug ("videoinfo is tv show");
+                    track.ArtistName = match.Groups[1].Value;
+                    if (Int32.TryParse (match.Groups[2].Value, out temp)) {
+                        //season
+                        track.AlbumTitle = temp.ToString ();
+                    }
+                    Int32.TryParse (match.Groups[3].Value, out temp);
+                    track.TrackNumber = temp;
+                    track.MediaAttributes |= TrackMediaAttributes.TvShow;
+                    return;
+                }
+            }
+            Log.Debug ("videoinfo is movie");
+            //by default video is movie if not tv show
+            track.MediaAttributes |= TrackMediaAttributes.Movie;
+
+            //TODO multi-part tv show
+            /*The defaults will match the following structures/file name formats
+            foo.s01e01.*
+            foo.s01.e01.*
+            foo.s01_e01.*
+            foo_[s01]_[e01]_*
+            foo.1x01.*
+            foo.101.*
+
+            Defaults for two-part TV Show Episode will match
+            foo.s01e01-02.*
+            foo_[s01]_[e01-02]_*
+            foo.1x01.1x02.*
+
+            Same with three- or four-part TV Show Episode
+            foo.s01e01-02-03-04.*
+            foo_[s01]_[e01-02-03-04]_*
+            foo.1x01.1x02.1x03.1x04.*
+            */
+
+        }
+
+        public void Dispose ()
+        {
+            ServiceManager.SourceManager.RemoveSource (source);
+        }
+
+        public string ServiceName {get{ return "VideoService";}}
+
+        public static QueryField VideoTitleField = new QueryField (
+            "title", "VideoTitle",
+            Catalog.GetString ("Name"), "Videos.Title", true,
+            // Translators: These are unique search fields. You can use CSV for synonyms. Please, no spaces. Blank ok.
+            Catalog.GetString ("title"), Catalog.GetString ("titled"), Catalog.GetString ("name"), Catalog.GetString ("named"),
+            "title", "titled", "name", "named"
+        );
+        public static QueryField VideoOriginalTitleField = new QueryField (
+            "originaltitle", "VideoOriginalTitle",
+            Catalog.GetString ("Name"), "Videos.OriginalTitleLowered", true,
+            // Translators: These are unique search fields. You can use CSV for synonyms. Please, no spaces. Blank ok.
+            Catalog.GetString ("title"), Catalog.GetString ("titled"), Catalog.GetString ("name"), Catalog.GetString ("named"),
+            "title", "titled", "name", "named"
+        );
+        public static QueryField VideoAlternativeTitleField = new QueryField (
+            "alternativetitle", "VideoAlternativeTrackTitle",
+            Catalog.GetString ("Name"), "Videos.AlternativeTitleLowered", true,
+            // Translators: These are unique search fields. You can use CSV for synonyms. Please, no spaces. Blank ok.
+            Catalog.GetString ("title"), Catalog.GetString ("titled"), Catalog.GetString ("name"), Catalog.GetString ("named"),
+            "title", "titled", "name", "named"
+        );
+    }
+}
+
diff --git a/src/Extensions/Banshee.Video/Banshee.Video/VideoSourceContents.cs b/src/Extensions/Banshee.Video/Banshee.Video/VideoSourceContents.cs
new file mode 100644
index 0000000..b1ce7d4
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Banshee.Video/VideoSourceContents.cs
@@ -0,0 +1,105 @@
+// 
+// VideoSourceContents.cs
+// 
+// Author:
+//   Olivier Dufour <olivier.duff@gmail.com>
+// 
+// Copyright 2011 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using Banshee.Sources.Gui;
+using Banshee.Collection.Gui;
+using Banshee.Sources;
+using Hyena.Data.Gui;
+using Banshee.Collection;
+using Hyena.Data;
+namespace Banshee.Video
+{
+    public class VideoSourceContents : FilteredListSourceContents, ITrackModelSourceContents
+    {
+        private TrackListView track_view;
+        private VideoInfoView video_view;
+
+        public VideoSourceContents () : base ("video")
+        {
+        }
+
+        protected override void InitializeViews ()
+        {
+            SetupMainView (track_view = new TrackListView ());
+            SetupFilterView (video_view = new VideoInfoView ());
+        }
+
+        protected override void ClearFilterSelections ()
+        {
+            if (video_view.Model != null) {
+                video_view.Selection.Clear ();
+            }
+        }
+
+        protected override bool ActiveSourceCanHasBrowser {
+            get { return true; }
+        }
+
+        protected override string ForcePosition {
+            get { return "top"; }
+        }
+
+        #region Implement ISourceContents
+
+        public override bool SetSource (ISource source)
+        {
+            DatabaseSource track_source = source as DatabaseSource;
+            if (track_source == null) {
+                return false;
+            }
+
+            this.source = source;
+
+            SetModel (track_view, track_source.TrackModel);
+
+            foreach (IListModel model in track_source.CurrentFilters) {
+                if (model is VideoInfoModel) {
+                    Hyena.Log.Debug ("SET MODEL");
+                    SetModel (video_view, (model as IListModel<VideoInfo>));
+                }
+            }
+
+            return true;
+        }
+
+        public override void ResetSource ()
+        {
+            source = null;
+            SetModel (track_view, null);
+            SetModel (video_view, null);
+        }
+
+        #endregion
+
+        #region ITrackModelSourceContents implementation
+
+        public IListView<TrackInfo> TrackView {
+            get { return track_view; }
+        }
+
+        #endregion
+    }
+}
diff --git a/src/Extensions/Banshee.Video/Makefile.am b/src/Extensions/Banshee.Video/Makefile.am
new file mode 100644
index 0000000..a198c94
--- /dev/null
+++ b/src/Extensions/Banshee.Video/Makefile.am
@@ -0,0 +1,27 @@
+ASSEMBLY = Banshee.Video
+TARGET = library
+LINK = $(LINK_BANSHEE_THICKCLIENT_DEPS)
+INSTALL_DIR = $(EXTENSIONS_INSTALL_DIR)
+
+SOURCES =  \
+	Banshee.Video/CastingMember.cs \
+	Banshee.Video/ContextPage.cs \
+	Banshee.Video/MovieGroupSource.cs \
+	Banshee.Video/TvShowGroupSource.cs \
+	Banshee.Video/VideoDetailPanel.cs \
+	Banshee.Video/VideoInfo.cs \
+	Banshee.Video/VideoInfoModel.cs \
+	Banshee.Video/VideoInfoView.cs \
+	Banshee.Video/VideoLibrarySource.cs \
+	Banshee.Video/VideoService.cs \
+	Banshee.Video/VideoSourceContents.cs \
+    Banshee.Video.Metadata/ImdbMetadataJob.cs \
+    Banshee.Video.Metadata/ImdbMetadataProvider.cs \
+    Banshee.Video.Metadata/TmdbMetadataJob.cs \
+    Banshee.Video.Metadata/TmdbMetadataProvider.cs \
+    Banshee.Video.Metadata/TvdbMetadataJob.cs \
+    Banshee.Video.Metadata/TvdbMetadataProvider.cs
+	
+RESOURCES = Banshee.Video.addin.xml
+
+include $(top_srcdir)/build/build.mk
diff --git a/src/Extensions/Makefile.am b/src/Extensions/Makefile.am
index d0c1509..b5c9079 100644
--- a/src/Extensions/Makefile.am
+++ b/src/Extensions/Makefile.am
@@ -31,6 +31,7 @@ SUBDIRS = \
 	Banshee.Torrent \
 	Banshee.RemoteAudio \
 	Banshee.UbuntuOneMusicStore \
+	Banshee.Video \
 	Banshee.Wikipedia \
 	Banshee.YouTube
 
diff --git a/src/Hyena b/src/Hyena
index 5e8c4d1..b0a0a9c 160000
--- a/src/Hyena
+++ b/src/Hyena
@@ -1 +1 @@
-Subproject commit 5e8c4d153ebddcdbffa75a1a02f5d7228c3d0238
+Subproject commit b0a0a9c96f52f141f84e0e6bdd8100b26535b900
