Explorar o código

Removed unused properties from BaseItem.

Luke Pulverenti %!s(int64=11) %!d(string=hai) anos
pai
achega
55a776427b
Modificáronse 59 ficheiros con 762 adicións e 552 borrados
  1. 0 6
      MediaBrowser.Api/EnvironmentService.cs
  2. 21 16
      MediaBrowser.Api/Images/ImageService.cs
  3. 31 3
      MediaBrowser.Api/ItemUpdateService.cs
  4. 0 5
      MediaBrowser.Api/Library/LibraryHelpers.cs
  5. 32 8
      MediaBrowser.Api/LibraryService.cs
  6. 9 12
      MediaBrowser.Api/MediaBrowser.Api.csproj
  7. 12 1
      MediaBrowser.Api/SimilarItemsHelper.cs
  8. 32 3
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  9. 1 2
      MediaBrowser.Common.Implementations/BaseApplicationPaths.cs
  10. 6 3
      MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
  11. 9 12
      MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
  12. 6 9
      MediaBrowser.Common/MediaBrowser.Common.csproj
  13. 5 4
      MediaBrowser.Common/Plugins/BasePlugin.cs
  14. 7 1
      MediaBrowser.Controller/Entities/Audio/Audio.cs
  15. 8 1
      MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
  16. 11 1
      MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
  17. 21 100
      MediaBrowser.Controller/Entities/BaseItem.cs
  18. 13 2
      MediaBrowser.Controller/Entities/Folder.cs
  19. 28 3
      MediaBrowser.Controller/Entities/Game.cs
  20. 15 0
      MediaBrowser.Controller/Entities/IHasLanguage.cs
  21. 34 0
      MediaBrowser.Controller/Entities/IHasProductionLocations.cs
  22. 16 0
      MediaBrowser.Controller/Entities/IHasScreenshots.cs
  23. 39 0
      MediaBrowser.Controller/Entities/IHasTaglines.cs
  24. 34 0
      MediaBrowser.Controller/Entities/IHasTags.cs
  25. 23 0
      MediaBrowser.Controller/Entities/IHasThemeMedia.cs
  26. 8 1
      MediaBrowser.Controller/Entities/Movies/BoxSet.cs
  27. 20 1
      MediaBrowser.Controller/Entities/Movies/Movie.cs
  28. 6 1
      MediaBrowser.Controller/Entities/Person.cs
  29. 9 2
      MediaBrowser.Controller/Entities/TV/Series.cs
  30. 15 2
      MediaBrowser.Controller/Entities/Trailer.cs
  31. 6 0
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  32. 55 12
      MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
  33. 0 5
      MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs
  34. 6 6
      MediaBrowser.Mono.sln
  35. 6 22
      MediaBrowser.Mono.userprefs
  36. 5 1
      MediaBrowser.Providers/ImageFromMediaLocationProvider.cs
  37. 1 1
      MediaBrowser.Providers/Movies/MovieDbPersonProvider.cs
  38. 11 3
      MediaBrowser.Providers/Movies/MovieDbProvider.cs
  39. 1 14
      MediaBrowser.Providers/Music/LastfmArtistProvider.cs
  40. 9 10
      MediaBrowser.Providers/Music/LastfmHelper.cs
  41. 4 2
      MediaBrowser.Providers/Savers/PersonXmlSaver.cs
  42. 29 17
      MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
  43. 0 10
      MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs
  44. 59 5
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  45. 1 1
      MediaBrowser.Server.Implementations/HttpServer/SwaggerService.cs
  46. 0 1
      MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs
  47. 1 1
      MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
  48. 10 9
      MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
  49. 30 40
      MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
  50. 11 7
      MediaBrowser.Server.Implementations/Providers/ImageSaver.cs
  51. 5 27
      MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
  52. 12 120
      MediaBrowser.Server.Mono/Program.cs
  53. 4 1
      MediaBrowser.Server.Mono/Properties/AssemblyInfo.cs
  54. 2 2
      MediaBrowser.Server.Mono/app.config
  55. 0 1
      MediaBrowser.ServerApplication/ApplicationHost.cs
  56. 2 13
      MediaBrowser.ServerApplication/EntryPoints/StartupWizard.cs
  57. 0 2
      MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
  58. 12 8
      MediaBrowser.ServerApplication/Native/BrowserLauncher.cs
  59. 9 12
      MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

+ 0 - 6
MediaBrowser.Api/EnvironmentService.cs

@@ -116,12 +116,6 @@ namespace MediaBrowser.Api
                 return ToOptimizedResult(GetNetworkShares(path).OrderBy(i => i.Path).ToList());
             }
 
-            // Reject invalid input
-            if (!Path.IsPathRooted(path))
-            {
-                throw new ArgumentException(string.Format("Invalid path: {0}", path));
-            }
-
             return ToOptimizedResult(GetFileSystemEntries(request).OrderBy(i => i.Path).ToList());
         }
 

+ 21 - 16
MediaBrowser.Api/Images/ImageService.cs

@@ -92,7 +92,7 @@ namespace MediaBrowser.Api.Images
         [ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
         public string Id { get; set; }
     }
-    
+
     /// <summary>
     /// Class UpdateItemImageIndex
     /// </summary>
@@ -281,7 +281,7 @@ namespace MediaBrowser.Api.Images
         [ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
         public string Id { get; set; }
     }
-    
+
     /// <summary>
     /// Class PostUserImage
     /// </summary>
@@ -375,7 +375,7 @@ namespace MediaBrowser.Api.Images
         /// <value>The request stream.</value>
         public Stream RequestStream { get; set; }
     }
-    
+
     /// <summary>
     /// Class ImageService
     /// </summary>
@@ -400,7 +400,7 @@ namespace MediaBrowser.Api.Images
         private readonly IImageProcessor _imageProcessor;
 
         private readonly ILiveTvManager _liveTv;
-        
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ImageService" /> class.
         /// </summary>
@@ -438,7 +438,7 @@ namespace MediaBrowser.Api.Images
 
             return ToOptimizedResult(result);
         }
-        
+
         public object Get(GetItemByNameImageInfos request)
         {
             var result = GetItemByNameImageInfos(request);
@@ -496,16 +496,20 @@ namespace MediaBrowser.Api.Images
 
             index = 0;
 
-            foreach (var image in item.ScreenshotImagePaths)
+            var hasScreenshots = item as IHasScreenshots;
+            if (hasScreenshots != null)
             {
-                var info = GetImageInfo(image, item, index, ImageType.Screenshot);
-
-                if (info != null)
+                foreach (var image in hasScreenshots.ScreenshotImagePaths)
                 {
-                    list.Add(info);
-                }
+                    var info = GetImageInfo(image, item, index, ImageType.Screenshot);
 
-                index++;
+                    if (info != null)
+                    {
+                        list.Add(info);
+                    }
+
+                    index++;
+                }
             }
 
             var video = item as Video;
@@ -667,7 +671,7 @@ namespace MediaBrowser.Api.Images
 
             Task.WaitAll(task);
         }
-        
+
         /// <summary>
         /// Deletes the specified request.
         /// </summary>
@@ -702,7 +706,7 @@ namespace MediaBrowser.Api.Images
 
             Task.WaitAll(task);
         }
-        
+
         /// <summary>
         /// Deletes the specified request.
         /// </summary>
@@ -764,8 +768,9 @@ namespace MediaBrowser.Api.Images
 
             if (type == ImageType.Screenshot)
             {
-                file1 = item.ScreenshotImagePaths[currentIndex];
-                file2 = item.ScreenshotImagePaths[newIndex];
+                var hasScreenshots = (IHasScreenshots)item;
+                file1 = hasScreenshots.ScreenshotImagePaths[currentIndex];
+                file2 = hasScreenshots.ScreenshotImagePaths[newIndex];
             }
             else if (type == ImageType.Backdrop)
             {

+ 31 - 3
MediaBrowser.Api/ItemUpdateService.cs

@@ -270,7 +270,12 @@ namespace MediaBrowser.Api
             item.ParentIndexNumber = request.ParentIndexNumber;
             item.Overview = request.Overview;
             item.Genres = request.Genres;
-            item.Tags = request.Tags;
+
+            var hasTags = item as IHasTags;
+            if (hasTags != null)
+            {
+                hasTags.Tags = request.Tags;
+            }
 
             if (request.Studios != null)
             {
@@ -290,11 +295,17 @@ namespace MediaBrowser.Api
             item.EndDate = request.EndDate.HasValue ? request.EndDate.Value.ToUniversalTime() : (DateTime?)null;
             item.PremiereDate = request.PremiereDate.HasValue ? request.PremiereDate.Value.ToUniversalTime() : (DateTime?)null;
             item.ProductionYear = request.ProductionYear;
-            item.ProductionLocations = request.ProductionLocations;
-            item.Language = request.Language;
             item.OfficialRating = request.OfficialRating;
             item.CustomRating = request.CustomRating;
 
+            SetProductionLocations(item, request);
+
+            var hasLanguage = item as IHasLanguage;
+            if (hasLanguage != null)
+            {
+                hasLanguage.Language = request.Language;
+            }
+            
             var hasAspectRatio = item as IHasAspectRatio;
             if (hasAspectRatio != null)
             {
@@ -366,5 +377,22 @@ namespace MediaBrowser.Api
             }
         }
 
+        private void SetProductionLocations(BaseItem item, BaseItemDto request)
+        {
+            var hasProductionLocations = item as IHasProductionLocations;
+
+            if (hasProductionLocations != null)
+            {
+                hasProductionLocations.ProductionLocations = request.ProductionLocations;
+            }
+
+            var person = item as Person;
+            if (person != null)
+            {
+                person.PlaceOfBirth = request.ProductionLocations == null
+                    ? null
+                    : request.ProductionLocations.FirstOrDefault();
+            }
+        }
     }
 }

+ 0 - 5
MediaBrowser.Api/Library/LibraryHelpers.cs

@@ -60,11 +60,6 @@ namespace MediaBrowser.Api.Library
         /// <exception cref="System.IO.DirectoryNotFoundException">The path does not exist.</exception>
         public static void AddMediaPath(IFileSystem fileSystem, string virtualFolderName, string path, User user, IServerApplicationPaths appPaths)
         {
-            if (!Path.IsPathRooted(path))
-            {
-                throw new ArgumentException("The path is not valid.");
-            }
-
             if (!Directory.Exists(path))
             {
                 throw new DirectoryNotFoundException("The path does not exist.");

+ 32 - 8
MediaBrowser.Api/LibraryService.cs

@@ -588,7 +588,7 @@ namespace MediaBrowser.Api
 
             var originalItem = item;
 
-            while (item.ThemeSongIds.Count == 0 && request.InheritFromParent && item.Parent != null)
+            while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.Parent != null)
             {
                 item = item.Parent;
             }
@@ -598,7 +598,7 @@ namespace MediaBrowser.Api
                     .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
                     .ToList();
 
-            var themeSongIds = item.ThemeSongIds;
+            var themeSongIds = GetThemeSongIds(item);
 
             if (themeSongIds.Count == 0 && request.InheritFromParent)
             {
@@ -608,11 +608,11 @@ namespace MediaBrowser.Api
                 {
                     var linkedItemWithThemes = album.SoundtrackIds
                         .Select(i => _libraryManager.GetItemById(i))
-                        .FirstOrDefault(i => i.ThemeSongIds.Count > 0);
+                        .FirstOrDefault(i => GetThemeSongIds(i).Count > 0);
 
                     if (linkedItemWithThemes != null)
                     {
-                        themeSongIds = linkedItemWithThemes.ThemeSongIds;
+                        themeSongIds = GetThemeSongIds(linkedItemWithThemes);
                         item = linkedItemWithThemes;
                     }
                 }
@@ -656,7 +656,7 @@ namespace MediaBrowser.Api
 
             var originalItem = item;
 
-            while (item.ThemeVideoIds.Count == 0 && request.InheritFromParent && item.Parent != null)
+            while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.Parent != null)
             {
                 item = item.Parent;
             }
@@ -666,7 +666,7 @@ namespace MediaBrowser.Api
                     .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
                     .ToList();
 
-            var themeVideoIds = item.ThemeVideoIds;
+            var themeVideoIds = GetThemeVideoIds(item);
 
             if (themeVideoIds.Count == 0 && request.InheritFromParent)
             {
@@ -681,11 +681,11 @@ namespace MediaBrowser.Api
                 {
                     var linkedItemWithThemes = album.SoundtrackIds
                         .Select(i => _libraryManager.GetItemById(i))
-                        .FirstOrDefault(i => i.ThemeVideoIds.Count > 0);
+                        .FirstOrDefault(i => GetThemeVideoIds(i).Count > 0);
 
                     if (linkedItemWithThemes != null)
                     {
-                        themeVideoIds = linkedItemWithThemes.ThemeVideoIds;
+                        themeVideoIds = GetThemeVideoIds(linkedItemWithThemes);
                         item = linkedItemWithThemes;
                     }
                 }
@@ -705,6 +705,30 @@ namespace MediaBrowser.Api
             };
         }
 
+        private List<Guid> GetThemeVideoIds(BaseItem item)
+        {
+            var i = item as IHasThemeMedia;
+
+            if (i != null)
+            {
+                return i.ThemeVideoIds;
+            }
+
+            return new List<Guid>();
+        }
+
+        private List<Guid> GetThemeSongIds(BaseItem item)
+        {
+            var i = item as IHasThemeMedia;
+
+            if (i != null)
+            {
+                return i.ThemeSongIds;
+            }
+
+            return new List<Guid>();
+        }
+
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
         public object Get(GetYearIndex request)

+ 9 - 12
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -38,18 +38,6 @@
     <RunPostBuildEvent>Always</RunPostBuildEvent>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="ServiceStack.Common, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll</HintPath>
-    </Reference>
-    <Reference Include="ServiceStack.Interfaces, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll</HintPath>
-    </Reference>
-    <Reference Include="ServiceStack.Text, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="Microsoft.CSharp" />
@@ -60,6 +48,15 @@
       <HintPath>..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll</HintPath>
     </Reference>
     <Reference Include="System.Xml" />
+    <Reference Include="ServiceStack.Common">
+      <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll</HintPath>
+    </Reference>
+    <Reference Include="ServiceStack.Interfaces">
+      <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll</HintPath>
+    </Reference>
+    <Reference Include="ServiceStack.Text">
+      <HintPath>..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">

+ 12 - 1
MediaBrowser.Api/SimilarItemsHelper.cs

@@ -119,6 +119,17 @@ namespace MediaBrowser.Api
                 .Select(i => i.Item1);
         }
 
+        private static IEnumerable<string> GetTags(BaseItem item)
+        {
+            var hasTags = item as IHasTags;
+            if (hasTags != null)
+            {
+                return hasTags.Tags;
+            }
+
+            return new List<string>();
+        }
+
         /// <summary>
         /// Gets the similiarity score.
         /// </summary>
@@ -138,7 +149,7 @@ namespace MediaBrowser.Api
             points += item1.Genres.Where(i => item2.Genres.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10);
 
             // Find common tags
-            points += item1.Tags.Where(i => item2.Tags.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10);
+            points += GetTags(item1).Where(i => GetTags(item2).Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10);
 
             // Find common studios
             points += item1.Studios.Where(i => item2.Studios.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 3);

+ 32 - 3
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -884,12 +884,36 @@ namespace MediaBrowser.Api.UserLibrary
 
             if (request.HasThemeSong.HasValue)
             {
-                items = items.Where(i => request.HasThemeSong.Value ? i.ThemeSongIds.Count > 0 : i.ThemeSongIds.Count == 0);
+                var filterValue = request.HasThemeSong.Value;
+
+                items = items.Where(i =>
+                {
+                    var themeCount = 0;
+                    var iHasThemeMedia = i as IHasThemeMedia;
+
+                    if (iHasThemeMedia != null)
+                    {
+                        themeCount = iHasThemeMedia.ThemeSongIds.Count;
+                    }
+                    return filterValue ? themeCount > 0 : themeCount == 0;
+                });
             }
 
             if (request.HasThemeVideo.HasValue)
             {
-                items = items.Where(i => request.HasThemeVideo.Value ? i.ThemeVideoIds.Count > 0 : i.ThemeVideoIds.Count == 0);
+                var filterValue = request.HasThemeVideo.Value;
+
+                items = items.Where(i =>
+                {
+                    var themeCount = 0;
+                    var iHasThemeMedia = i as IHasThemeMedia;
+
+                    if (iHasThemeMedia != null)
+                    {
+                        themeCount = iHasThemeMedia.ThemeVideoIds.Count;
+                    }
+                    return filterValue ? themeCount > 0 : themeCount == 0;
+                });
             }
 
             if (request.MinPlayers.HasValue)
@@ -1166,7 +1190,12 @@ namespace MediaBrowser.Api.UserLibrary
 
             if (imageType == ImageType.Screenshot)
             {
-                return item.ScreenshotImagePaths.Count > 0;
+                var hasScreenshots = item as IHasScreenshots;
+                if (hasScreenshots == null)
+                {
+                    return false;
+                }
+                return hasScreenshots.ScreenshotImagePaths.Count > 0;
             }
 
             return item.HasImage(imageType);

+ 1 - 2
MediaBrowser.Common.Implementations/BaseApplicationPaths.cs

@@ -280,8 +280,7 @@ namespace MediaBrowser.Common.Implementations
             // If it's a relative path, e.g. "..\"
             if (!Path.IsPathRooted(programDataPath))
             {
-                var path = Assembly.GetExecutingAssembly().Location;
-                path = Path.GetDirectoryName(path);
+                var path = Path.GetDirectoryName(ApplicationPath);
 
                 if (string.IsNullOrEmpty(path))
                 {

+ 6 - 3
MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs

@@ -103,21 +103,24 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
 
         private WebRequest GetMonoRequest(HttpRequestOptions options, string method, bool enableHttpCompression)
         {
-            var request = WebRequest.Create(options.Url);
+            var request = (HttpWebRequest)WebRequest.Create(options.Url);
 
             if (!string.IsNullOrEmpty(options.AcceptHeader))
             {
-                request.Headers.Add("Accept", options.AcceptHeader);
+                request.Accept = options.AcceptHeader;
             }
 
+            request.AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None;
             request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate);
             request.ConnectionGroupName = GetHostFromUrl(options.Url);
+            request.KeepAlive = true;
             request.Method = method;
+            request.Pipelined = true;
             request.Timeout = 20000;
 
             if (!string.IsNullOrEmpty(options.UserAgent))
             {
-                request.Headers.Add("User-Agent", options.UserAgent);
+                request.UserAgent = options.UserAgent;
             }
 
             return request;

+ 9 - 12
MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj

@@ -37,21 +37,9 @@
     <RunPostBuildEvent>Always</RunPostBuildEvent>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\NLog.2.1.0\lib\net45\NLog.dll</HintPath>
-    </Reference>
-    <Reference Include="ServiceStack.Text, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll</HintPath>
-    </Reference>
     <Reference Include="SharpCompress">
       <HintPath>..\packages\sharpcompress.0.10.1.3\lib\net40\SharpCompress.dll</HintPath>
     </Reference>
-    <Reference Include="SimpleInjector, Version=2.3.6.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\SimpleInjector.2.3.6\lib\net40-client\SimpleInjector.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
@@ -59,6 +47,15 @@
     <Reference Include="System.Net" />
     <Reference Include="System.Net.Http" />
     <Reference Include="System.Xml" />
+    <Reference Include="NLog">
+      <HintPath>..\packages\NLog.2.1.0\lib\net45\NLog.dll</HintPath>
+    </Reference>
+    <Reference Include="ServiceStack.Text">
+      <HintPath>..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll</HintPath>
+    </Reference>
+    <Reference Include="SimpleInjector">
+      <HintPath>..\packages\SimpleInjector.2.3.6\lib\net40-client\SimpleInjector.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">

+ 6 - 9
MediaBrowser.Common/MediaBrowser.Common.csproj

@@ -35,21 +35,18 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="ServiceStack.Common, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="ServiceStack.Common">
       <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll</HintPath>
     </Reference>
-    <Reference Include="ServiceStack.Interfaces, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
+    <Reference Include="ServiceStack.Interfaces">
       <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll</HintPath>
     </Reference>
-    <Reference Include="ServiceStack.Text, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
+    <Reference Include="ServiceStack.Text">
       <HintPath>..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll</HintPath>
     </Reference>
-    <Reference Include="System" />
-    <Reference Include="System.Core" />
-    <Reference Include="Microsoft.CSharp" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">

+ 5 - 4
MediaBrowser.Common/Plugins/BasePlugin.cs

@@ -57,7 +57,7 @@ namespace MediaBrowser.Common.Plugins
         {
             get { return typeof(TConfigurationType); }
         }
-        
+
         /// <summary>
         /// The _assembly name
         /// </summary>
@@ -90,7 +90,8 @@ namespace MediaBrowser.Common.Plugins
 
                 if (!_uniqueId.HasValue)
                 {
-                    _uniqueId = Marshal.GetTypeLibGuidForAssembly(GetType().Assembly);
+                    var attribute = (GuidAttribute)GetType().Assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0];
+                    _uniqueId = new Guid(attribute.Value);
                 }
 
                 return _uniqueId.Value;
@@ -282,7 +283,7 @@ namespace MediaBrowser.Common.Plugins
             {
                 throw new ArgumentNullException("configuration");
             }
-            
+
             Configuration = (TConfigurationType)configuration;
 
             SaveConfiguration();
@@ -315,7 +316,7 @@ namespace MediaBrowser.Common.Plugins
         /// </summary>
         public virtual void OnUninstalling()
         {
-            
+
         }
 
         /// <summary>

+ 7 - 1
MediaBrowser.Controller/Entities/Audio/Audio.cs

@@ -9,7 +9,7 @@ namespace MediaBrowser.Controller.Entities.Audio
     /// <summary>
     /// Class Audio
     /// </summary>
-    public class Audio : BaseItem, IHasMediaStreams, IHasAlbumArtist, IHasArtist, IHasMusicGenres
+    public class Audio : BaseItem, IHasMediaStreams, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLanguage
     {
         public Audio()
         {
@@ -17,6 +17,12 @@ namespace MediaBrowser.Controller.Entities.Audio
             Artists = new List<string>();
         }
 
+        /// <summary>
+        /// Gets or sets the language.
+        /// </summary>
+        /// <value>The language.</value>
+        public string Language { get; set; }
+        
         /// <summary>
         /// Gets or sets the media streams.
         /// </summary>

+ 8 - 1
MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs

@@ -9,7 +9,7 @@ namespace MediaBrowser.Controller.Entities.Audio
     /// <summary>
     /// Class MusicAlbum
     /// </summary>
-    public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres
+    public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasTags
     {
         public List<Guid> SoundtrackIds { get; set; }
         
@@ -17,11 +17,18 @@ namespace MediaBrowser.Controller.Entities.Audio
         {
             Artists = new List<string>();
             SoundtrackIds = new List<Guid>();
+            Tags = new List<string>();
         }
 
         public string LastFmImageUrl { get; set; }
         public string LastFmImageSize { get; set; }
 
+        /// <summary>
+        /// Gets or sets the tags.
+        /// </summary>
+        /// <value>The tags.</value>
+        public List<string> Tags { get; set; }
+
         /// <summary>
         /// Songs will group into us so don't also include us in the index
         /// </summary>

+ 11 - 1
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -11,13 +11,21 @@ namespace MediaBrowser.Controller.Entities.Audio
     /// <summary>
     /// Class MusicArtist
     /// </summary>
-    public class MusicArtist : Folder, IItemByName, IHasMusicGenres, IHasDualAccess
+    public class MusicArtist : Folder, IItemByName, IHasMusicGenres, IHasDualAccess, IHasTags, IHasProductionLocations
     {
         [IgnoreDataMember]
         public List<ItemByNameCounts> UserItemCountList { get; set; }
 
         public bool IsAccessedByName { get; set; }
 
+        /// <summary>
+        /// Gets or sets the tags.
+        /// </summary>
+        /// <value>The tags.</value>
+        public List<string> Tags { get; set; }
+
+        public List<string> ProductionLocations { get; set; }
+        
         public override bool IsFolder
         {
             get
@@ -70,6 +78,8 @@ namespace MediaBrowser.Controller.Entities.Audio
         public MusicArtist()
         {
             UserItemCountList = new List<ItemByNameCounts>();
+            Tags = new List<string>();
+            ProductionLocations = new List<string>();
         }
 
         /// <summary>

+ 21 - 100
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -29,16 +29,10 @@ namespace MediaBrowser.Controller.Entities
             Genres = new List<string>();
             Studios = new List<string>();
             People = new List<PersonInfo>();
-            ScreenshotImagePaths = new List<string>();
             BackdropImagePaths = new List<string>();
-            ProductionLocations = new List<string>();
             Images = new Dictionary<ImageType, string>();
             ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-            Tags = new List<string>();
-            ThemeSongIds = new List<Guid>();
-            ThemeVideoIds = new List<Guid>();
             LockedFields = new List<MetadataFields>();
-            Taglines = new List<string>();
             ImageSources = new List<ImageSourceInfo>();
         }
 
@@ -84,12 +78,6 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The id.</value>
         public Guid Id { get; set; }
 
-        /// <summary>
-        /// Gets or sets the taglines.
-        /// </summary>
-        /// <value>The taglines.</value>
-        public List<string> Taglines { get; set; }
-
         /// <summary>
         /// Return the id that should be used to key display prefs for this item.
         /// Default is based on the type for everything except actual generic folders.
@@ -509,12 +497,6 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The backdrop image sources.</value>
         public List<ImageSourceInfo> ImageSources { get; set; }
 
-        /// <summary>
-        /// Gets or sets the screenshot image paths.
-        /// </summary>
-        /// <value>The screenshot image paths.</value>
-        public List<string> ScreenshotImagePaths { get; set; }
-
         /// <summary>
         /// Gets or sets the official rating.
         /// </summary>
@@ -533,11 +515,6 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The custom rating.</value>
         public string CustomRating { get; set; }
 
-        /// <summary>
-        /// Gets or sets the language.
-        /// </summary>
-        /// <value>The language.</value>
-        public string Language { get; set; }
         /// <summary>
         /// Gets or sets the overview.
         /// </summary>
@@ -550,12 +527,6 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The people.</value>
         public List<PersonInfo> People { get; set; }
 
-        /// <summary>
-        /// Gets or sets the tags.
-        /// </summary>
-        /// <value>The tags.</value>
-        public List<string> Tags { get; set; }
-
         /// <summary>
         /// Override this if you need to combine/collapse person information
         /// </summary>
@@ -596,12 +567,6 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The home page URL.</value>
         public string HomePageUrl { get; set; }
 
-        /// <summary>
-        /// Gets or sets the production locations.
-        /// </summary>
-        /// <value>The production locations.</value>
-        public List<string> ProductionLocations { get; set; }
-
         /// <summary>
         /// Gets or sets the community rating.
         /// </summary>
@@ -644,9 +609,6 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The parent index number.</value>
         public int? ParentIndexNumber { get; set; }
 
-        public List<Guid> ThemeSongIds { get; set; }
-        public List<Guid> ThemeVideoIds { get; set; }
-
         [IgnoreDataMember]
         public virtual string OfficialRatingForComparison
         {
@@ -884,9 +846,13 @@ namespace MediaBrowser.Controller.Entities
 
             if (LocationType == LocationType.FileSystem && Parent != null)
             {
-                themeSongsChanged = await RefreshThemeSongs(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
+                var hasThemeMedia = this as IHasThemeMedia;
+                if (hasThemeMedia != null)
+                {
+                    themeSongsChanged = await RefreshThemeSongs(hasThemeMedia, cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
 
-                themeVideosChanged = await RefreshThemeVideos(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
+                    themeVideosChanged = await RefreshThemeVideos(hasThemeMedia, cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false);
+                }
 
                 var hasTrailers = this as IHasTrailers;
                 if (hasTrailers != null)
@@ -928,18 +894,18 @@ namespace MediaBrowser.Controller.Entities
             return itemsChanged || results.Contains(true);
         }
 
-        private async Task<bool> RefreshThemeVideos(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
+        private async Task<bool> RefreshThemeVideos(IHasThemeMedia item, CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
         {
             var newThemeVideos = LoadThemeVideos().ToList();
             var newThemeVideoIds = newThemeVideos.Select(i => i.Id).ToList();
 
-            var themeVideosChanged = !ThemeVideoIds.SequenceEqual(newThemeVideoIds);
+            var themeVideosChanged = !item.ThemeVideoIds.SequenceEqual(newThemeVideoIds);
 
             var tasks = newThemeVideos.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs: false));
 
             var results = await Task.WhenAll(tasks).ConfigureAwait(false);
 
-            ThemeVideoIds = newThemeVideoIds;
+            item.ThemeVideoIds = newThemeVideoIds;
 
             return themeVideosChanged || results.Contains(true);
         }
@@ -947,18 +913,18 @@ namespace MediaBrowser.Controller.Entities
         /// <summary>
         /// Refreshes the theme songs.
         /// </summary>
-        private async Task<bool> RefreshThemeSongs(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
+        private async Task<bool> RefreshThemeSongs(IHasThemeMedia item, CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
         {
             var newThemeSongs = LoadThemeSongs().ToList();
             var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList();
 
-            var themeSongsChanged = !ThemeSongIds.SequenceEqual(newThemeSongIds);
+            var themeSongsChanged = !item.ThemeSongIds.SequenceEqual(newThemeSongIds);
 
             var tasks = newThemeSongs.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs: false));
 
             var results = await Task.WhenAll(tasks).ConfigureAwait(false);
 
-            ThemeSongIds = newThemeSongIds;
+            item.ThemeSongIds = newThemeSongIds;
 
             return themeSongsChanged || results.Contains(true);
         }
@@ -1229,24 +1195,6 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        /// <summary>
-        /// Adds the tagline.
-        /// </summary>
-        /// <param name="tagline">The tagline.</param>
-        /// <exception cref="System.ArgumentNullException">tagline</exception>
-        public void AddTagline(string tagline)
-        {
-            if (string.IsNullOrWhiteSpace(tagline))
-            {
-                throw new ArgumentNullException("tagline");
-            }
-
-            if (!Taglines.Contains(tagline, StringComparer.OrdinalIgnoreCase))
-            {
-                Taglines.Add(tagline);
-            }
-        }
-
         /// <summary>
         /// Adds a studio to the item
         /// </summary>
@@ -1265,19 +1213,6 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        public void AddTag(string name)
-        {
-            if (string.IsNullOrWhiteSpace(name))
-            {
-                throw new ArgumentNullException("name");
-            }
-
-            if (!Tags.Contains(name, StringComparer.OrdinalIgnoreCase))
-            {
-                Tags.Add(name);
-            }
-        }
-
         /// <summary>
         /// Adds a genre to the item
         /// </summary>
@@ -1296,24 +1231,6 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        /// <summary>
-        /// Adds the production location.
-        /// </summary>
-        /// <param name="location">The location.</param>
-        /// <exception cref="System.ArgumentNullException">location</exception>
-        public void AddProductionLocation(string location)
-        {
-            if (string.IsNullOrWhiteSpace(location))
-            {
-                throw new ArgumentNullException("location");
-            }
-
-            if (!ProductionLocations.Contains(location, StringComparer.OrdinalIgnoreCase))
-            {
-                ProductionLocations.Add(location);
-            }
-        }
-
         /// <summary>
         /// Marks the played.
         /// </summary>
@@ -1516,9 +1433,10 @@ namespace MediaBrowser.Controller.Entities
                     throw new ArgumentException("Please specify a screenshot image index to delete.");
                 }
 
-                var file = ScreenshotImagePaths[index.Value];
+                var hasScreenshots = (IHasScreenshots)this;
+                var file = hasScreenshots.ScreenshotImagePaths[index.Value];
 
-                ScreenshotImagePaths.Remove(file);
+                hasScreenshots.ScreenshotImagePaths.Remove(file);
 
                 // Delete the source file
                 DeleteImagePath(file);
@@ -1673,15 +1591,17 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         public void ValidateScreenshots()
         {
+            var hasScreenshots = (IHasScreenshots)this;
+
             // Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below
-            var deletedImages = ScreenshotImagePaths
+            var deletedImages = hasScreenshots.ScreenshotImagePaths
                 .Where(path => !File.Exists(path))
                 .ToList();
 
             // Now remove them from the dictionary
             foreach (var path in deletedImages)
             {
-                ScreenshotImagePaths.Remove(path);
+                hasScreenshots.ScreenshotImagePaths.Remove(path);
             }
         }
 
@@ -1703,7 +1623,8 @@ namespace MediaBrowser.Controller.Entities
 
             if (imageType == ImageType.Screenshot)
             {
-                return ScreenshotImagePaths[imageIndex];
+                var hasScreenshots = (IHasScreenshots)this;
+                return hasScreenshots.ScreenshotImagePaths[imageIndex];
             }
 
             if (imageType == ImageType.Chapter)

+ 13 - 2
MediaBrowser.Controller/Entities/Folder.cs

@@ -21,13 +21,19 @@ namespace MediaBrowser.Controller.Entities
     /// <summary>
     /// Class Folder
     /// </summary>
-    public class Folder : BaseItem
+    public class Folder : BaseItem, IHasThemeMedia
     {
         public static IUserManager UserManager { get; set; }
 
+        public List<Guid> ThemeSongIds { get; set; }
+        public List<Guid> ThemeVideoIds { get; set; }
+        
         public Folder()
         {
             LinkedChildren = new List<LinkedChild>();
+
+            ThemeSongIds = new List<Guid>();
+            ThemeVideoIds = new List<Guid>();
         }
 
         /// <summary>
@@ -687,7 +693,12 @@ namespace MediaBrowser.Controller.Entities
                     //existing item - check if it has changed
                     if (currentChild.HasChanged(child))
                     {
-                        EntityResolutionHelper.EnsureDates(FileSystem, currentChild, child.ResolveArgs, false);
+                        var currentChildLocationType = currentChild.LocationType;
+                        if (currentChildLocationType != LocationType.Remote &&
+                            currentChildLocationType != LocationType.Virtual)
+                        {
+                            EntityResolutionHelper.EnsureDates(FileSystem, currentChild, child.ResolveArgs, false);
+                        }
 
                         validChildren.Add(new Tuple<BaseItem, bool>(currentChild, true));
                     }

+ 28 - 3
MediaBrowser.Controller/Entities/Game.cs

@@ -4,26 +4,51 @@ using System.Collections.Generic;
 
 namespace MediaBrowser.Controller.Entities
 {
-    public class Game : BaseItem, IHasSoundtracks, IHasTrailers
+    public class Game : BaseItem, IHasSoundtracks, IHasTrailers, IHasThemeMedia, IHasTags, IHasLanguage, IHasScreenshots
     {
         public List<Guid> SoundtrackIds { get; set; }
 
+        public List<Guid> ThemeSongIds { get; set; }
+        public List<Guid> ThemeVideoIds { get; set; }
+
         public Game()
         {
             MultiPartGameFiles = new List<string>();
             SoundtrackIds = new List<Guid>();
             RemoteTrailers = new List<MediaUrl>();
             LocalTrailerIds = new List<Guid>();
+            ThemeSongIds = new List<Guid>();
+            ThemeVideoIds = new List<Guid>();
+            Tags = new List<string>();
+            ScreenshotImagePaths = new List<string>();
         }
 
+        /// <summary>
+        /// Gets or sets the language.
+        /// </summary>
+        /// <value>The language.</value>
+        public string Language { get; set; }
+
         public List<Guid> LocalTrailerIds { get; set; }
-        
+
+        /// <summary>
+        /// Gets or sets the screenshot image paths.
+        /// </summary>
+        /// <value>The screenshot image paths.</value>
+        public List<string> ScreenshotImagePaths { get; set; }
+
+        /// <summary>
+        /// Gets or sets the tags.
+        /// </summary>
+        /// <value>The tags.</value>
+        public List<string> Tags { get; set; }
+
         /// <summary>
         /// Gets or sets the remote trailers.
         /// </summary>
         /// <value>The remote trailers.</value>
         public List<MediaUrl> RemoteTrailers { get; set; }
-        
+
         /// <summary>
         /// Gets the type of the media.
         /// </summary>

+ 15 - 0
MediaBrowser.Controller/Entities/IHasLanguage.cs

@@ -0,0 +1,15 @@
+
+namespace MediaBrowser.Controller.Entities
+{
+    /// <summary>
+    /// Interface IHasLanguage
+    /// </summary>
+    public interface IHasLanguage
+    {
+        /// <summary>
+        /// Gets or sets the language.
+        /// </summary>
+        /// <value>The language.</value>
+        string Language { get; set; }
+    }
+}

+ 34 - 0
MediaBrowser.Controller/Entities/IHasProductionLocations.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MediaBrowser.Controller.Entities
+{
+    /// <summary>
+    /// Interface IHasProductionLocations
+    /// </summary>
+    public interface IHasProductionLocations
+    {
+        /// <summary>
+        /// Gets or sets the production locations.
+        /// </summary>
+        /// <value>The production locations.</value>
+        List<string> ProductionLocations { get; set; }
+    }
+
+    public static class ProductionLocationExtensions
+    {
+        public static void AddProductionLocation(this IHasProductionLocations item, string name)
+        {
+            if (string.IsNullOrWhiteSpace(name))
+            {
+                throw new ArgumentNullException("name");
+            }
+
+            if (!item.ProductionLocations.Contains(name, StringComparer.OrdinalIgnoreCase))
+            {
+                item.ProductionLocations.Add(name);
+            }
+        }
+    }
+}

+ 16 - 0
MediaBrowser.Controller/Entities/IHasScreenshots.cs

@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+
+namespace MediaBrowser.Controller.Entities
+{
+    /// <summary>
+    /// Interface IHasScreenshots
+    /// </summary>
+    public interface IHasScreenshots
+    {
+        /// <summary>
+        /// Gets or sets the screenshot image paths.
+        /// </summary>
+        /// <value>The screenshot image paths.</value>
+        List<string> ScreenshotImagePaths { get; set; }
+    }
+}

+ 39 - 0
MediaBrowser.Controller/Entities/IHasTaglines.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MediaBrowser.Controller.Entities
+{
+    /// <summary>
+    /// Interface IHasTaglines
+    /// </summary>
+    public interface IHasTaglines
+    {
+        /// <summary>
+        /// Gets or sets the taglines.
+        /// </summary>
+        /// <value>The taglines.</value>
+        List<string> Taglines { get; set; }
+    }
+
+    public static class TaglineExtensions
+    {
+        /// <summary>
+        /// Adds the tagline.
+        /// </summary>
+        /// <param name="tagline">The tagline.</param>
+        /// <exception cref="System.ArgumentNullException">tagline</exception>
+        public static void AddTagline(this IHasTaglines item, string tagline)
+        {
+            if (string.IsNullOrWhiteSpace(tagline))
+            {
+                throw new ArgumentNullException("tagline");
+            }
+
+            if (!item.Taglines.Contains(tagline, StringComparer.OrdinalIgnoreCase))
+            {
+                item.Taglines.Add(tagline);
+            }
+        }
+    }
+}

+ 34 - 0
MediaBrowser.Controller/Entities/IHasTags.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MediaBrowser.Controller.Entities
+{
+    /// <summary>
+    /// Interface IHasTags
+    /// </summary>
+    public interface IHasTags
+    {
+        /// <summary>
+        /// Gets or sets the tags.
+        /// </summary>
+        /// <value>The tags.</value>
+        List<string> Tags { get; set; }
+    }
+
+    public static class TagExtensions
+    {
+        public static void AddTag(this IHasTags item, string name)
+        {
+            if (string.IsNullOrWhiteSpace(name))
+            {
+                throw new ArgumentNullException("name");
+            }
+
+            if (!item.Tags.Contains(name, StringComparer.OrdinalIgnoreCase))
+            {
+                item.Tags.Add(name);
+            }
+        }
+    }
+}

+ 23 - 0
MediaBrowser.Controller/Entities/IHasThemeMedia.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Controller.Entities
+{
+    /// <summary>
+    /// Interface IHasThemeMedia
+    /// </summary>
+    public interface IHasThemeMedia
+    {
+        /// <summary>
+        /// Gets or sets the theme song ids.
+        /// </summary>
+        /// <value>The theme song ids.</value>
+        List<Guid> ThemeSongIds { get; set; }
+
+        /// <summary>
+        /// Gets or sets the theme video ids.
+        /// </summary>
+        /// <value>The theme video ids.</value>
+        List<Guid> ThemeVideoIds { get; set; }
+    }
+}

+ 8 - 1
MediaBrowser.Controller/Entities/Movies/BoxSet.cs

@@ -7,12 +7,13 @@ namespace MediaBrowser.Controller.Entities.Movies
     /// <summary>
     /// Class BoxSet
     /// </summary>
-    public class BoxSet : Folder, IHasTrailers
+    public class BoxSet : Folder, IHasTrailers, IHasTags
     {
         public BoxSet()
         {
             RemoteTrailers = new List<MediaUrl>();
             LocalTrailerIds = new List<Guid>();
+            Tags = new List<string>();
         }
 
         public List<Guid> LocalTrailerIds { get; set; }
@@ -22,5 +23,11 @@ namespace MediaBrowser.Controller.Entities.Movies
         /// </summary>
         /// <value>The remote trailers.</value>
         public List<MediaUrl> RemoteTrailers { get; set; }
+
+        /// <summary>
+        /// Gets or sets the tags.
+        /// </summary>
+        /// <value>The tags.</value>
+        public List<string> Tags { get; set; }
     }
 }

+ 20 - 1
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -11,24 +11,43 @@ namespace MediaBrowser.Controller.Entities.Movies
     /// <summary>
     /// Class Movie
     /// </summary>
-    public class Movie : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasTrailers
+    public class Movie : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasTags
     {
         public List<Guid> SpecialFeatureIds { get; set; }
 
         public List<Guid> SoundtrackIds { get; set; }
 
+        public List<Guid> ThemeSongIds { get; set; }
+        public List<Guid> ThemeVideoIds { get; set; }
+        
         public Movie()
         {
             SpecialFeatureIds = new List<Guid>();
             SoundtrackIds = new List<Guid>();
             RemoteTrailers = new List<MediaUrl>();
             LocalTrailerIds = new List<Guid>();
+            ThemeSongIds = new List<Guid>();
+            ThemeVideoIds = new List<Guid>();
+            Taglines = new List<string>();
+            Tags = new List<string>();
         }
 
         public List<Guid> LocalTrailerIds { get; set; }
         
         public List<MediaUrl> RemoteTrailers { get; set; }
 
+        /// <summary>
+        /// Gets or sets the tags.
+        /// </summary>
+        /// <value>The tags.</value>
+        public List<string> Tags { get; set; }
+
+        /// <summary>
+        /// Gets or sets the taglines.
+        /// </summary>
+        /// <value>The taglines.</value>
+        public List<string> Taglines { get; set; }
+
         /// <summary>
         /// Gets or sets the budget.
         /// </summary>

+ 6 - 1
MediaBrowser.Controller/Entities/Person.cs

@@ -1,5 +1,4 @@
 using MediaBrowser.Model.Dto;
-using System;
 using System.Collections.Generic;
 using System.Runtime.Serialization;
 
@@ -15,6 +14,12 @@ namespace MediaBrowser.Controller.Entities
             UserItemCountList = new List<ItemByNameCounts>();
         }
 
+        /// <summary>
+        /// Gets or sets the place of birth.
+        /// </summary>
+        /// <value>The place of birth.</value>
+        public string PlaceOfBirth { get; set; }
+
         [IgnoreDataMember]
         public List<ItemByNameCounts> UserItemCountList { get; set; }
         

+ 9 - 2
MediaBrowser.Controller/Entities/TV/Series.cs

@@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.Entities.TV
     /// <summary>
     /// Class Series
     /// </summary>
-    public class Series : Folder, IHasSoundtracks, IHasTrailers
+    public class Series : Folder, IHasSoundtracks, IHasTrailers, IHasTags
     {
         public List<Guid> SpecialFeatureIds { get; set; }
         public List<Guid> SoundtrackIds { get; set; }
@@ -27,12 +27,19 @@ namespace MediaBrowser.Controller.Entities.TV
             SoundtrackIds = new List<Guid>();
             RemoteTrailers = new List<MediaUrl>();
             LocalTrailerIds = new List<Guid>();
+            Tags = new List<string>();
         }
 
         public List<Guid> LocalTrailerIds { get; set; }
         
         public List<MediaUrl> RemoteTrailers { get; set; }
-        
+
+        /// <summary>
+        /// Gets or sets the tags.
+        /// </summary>
+        /// <value>The tags.</value>
+        public List<string> Tags { get; set; }
+     
         /// <summary>
         /// Gets or sets the status.
         /// </summary>

+ 15 - 2
MediaBrowser.Controller/Entities/Trailer.cs

@@ -8,7 +8,7 @@ namespace MediaBrowser.Controller.Entities
     /// <summary>
     /// Class Trailer
     /// </summary>
-    public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasTrailers
+    public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasTrailers, IHasTaglines, IHasTags
     {
         public List<Guid> SoundtrackIds { get; set; }
         
@@ -18,12 +18,25 @@ namespace MediaBrowser.Controller.Entities
             Taglines = new List<string>();
             SoundtrackIds = new List<Guid>();
             LocalTrailerIds = new List<Guid>();
+            Tags = new List<string>();
         }
 
         public List<Guid> LocalTrailerIds { get; set; }
         
         public List<MediaUrl> RemoteTrailers { get; set; }
-        
+
+        /// <summary>
+        /// Gets or sets the tags.
+        /// </summary>
+        /// <value>The tags.</value>
+        public List<string> Tags { get; set; }
+
+        /// <summary>
+        /// Gets or sets the taglines.
+        /// </summary>
+        /// <value>The taglines.</value>
+        public List<string> Taglines { get; set; }
+   
         /// <summary>
         /// Gets or sets the budget.
         /// </summary>

+ 6 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -92,7 +92,13 @@
     <Compile Include="Entities\IHasAspectRatio.cs" />
     <Compile Include="Entities\IHasBudget.cs" />
     <Compile Include="Entities\IHasCriticRating.cs" />
+    <Compile Include="Entities\IHasLanguage.cs" />
+    <Compile Include="Entities\IHasProductionLocations.cs" />
+    <Compile Include="Entities\IHasScreenshots.cs" />
     <Compile Include="Entities\IHasSoundtracks.cs" />
+    <Compile Include="Entities\IHasTaglines.cs" />
+    <Compile Include="Entities\IHasTags.cs" />
+    <Compile Include="Entities\IHasThemeMedia.cs" />
     <Compile Include="Entities\IHasTrailers.cs" />
     <Compile Include="Entities\IItemByName.cs" />
     <Compile Include="Entities\ILibraryItem.cs" />

+ 55 - 12
MediaBrowser.Controller/Providers/BaseItemXmlParser.cs

@@ -63,11 +63,22 @@ namespace MediaBrowser.Controller.Providers
                 ValidationType = ValidationType.None
             };
 
-            item.Taglines.Clear();
+            var hasTaglines = item as IHasTaglines;
+            if (hasTaglines != null)
+            {
+                hasTaglines.Taglines.Clear();
+            }
+
             item.Studios.Clear();
             item.Genres.Clear();
             item.People.Clear();
-            item.Tags.Clear();
+
+            var hasTags = item as IHasTags;
+            if (hasTags != null)
+            {
+                hasTags.Tags.Clear();
+            }
+
 
             var hasTrailers = item as IHasTrailers;
             if (hasTrailers != null)
@@ -242,9 +253,26 @@ namespace MediaBrowser.Controller.Providers
                     {
                         var tagline = reader.ReadElementContentAsString();
 
-                        if (!string.IsNullOrWhiteSpace(tagline))
+                        var hasTaglines = item as IHasTaglines;
+                        if (hasTaglines != null)
                         {
-                            item.AddTagline(tagline);
+                            if (!string.IsNullOrWhiteSpace(tagline))
+                            {
+                                hasTaglines.AddTagline(tagline);
+                            }
+                        }
+
+                        break;
+                    }
+
+                case "Language":
+                    {
+                        var val = reader.ReadElementContentAsString();
+
+                        var hasLanguage = item as IHasLanguage;
+                        if (hasLanguage != null)
+                        {
+                            hasLanguage.Language = val;
                         }
 
                         break;
@@ -256,7 +284,11 @@ namespace MediaBrowser.Controller.Providers
 
                         if (!string.IsNullOrWhiteSpace(val))
                         {
-                            item.ProductionLocations = new List<string> { val };
+                            var person = item as Person;
+                            if (person != null)
+                            {
+                                person.PlaceOfBirth = val;
+                            }
                         }
 
                         break;
@@ -690,7 +722,11 @@ namespace MediaBrowser.Controller.Providers
                     {
                         using (var subtree = reader.ReadSubtree())
                         {
-                            FetchFromTagsNode(subtree, item);
+                            var hasTags = item as IHasTags;
+                            if (hasTags != null)
+                            {
+                                FetchFromTagsNode(subtree, hasTags);
+                            }
                         }
                         break;
                     }
@@ -841,7 +877,14 @@ namespace MediaBrowser.Controller.Providers
 
                                 if (!string.IsNullOrWhiteSpace(val))
                                 {
-                                    item.AddTagline(val);
+                                    var hasTaglines = item as IHasTaglines;
+                                    if (hasTaglines != null)
+                                    {
+                                        if (!string.IsNullOrWhiteSpace(val))
+                                        {
+                                            hasTaglines.AddTagline(val);
+                                        }
+                                    }
                                 }
                                 break;
                             }
@@ -888,7 +931,7 @@ namespace MediaBrowser.Controller.Providers
             }
         }
 
-        private void FetchFromTagsNode(XmlReader reader, T item)
+        private void FetchFromTagsNode(XmlReader reader, IHasTags item)
         {
             reader.MoveToContent();
 
@@ -981,7 +1024,7 @@ namespace MediaBrowser.Controller.Providers
                 }
             }
         }
-        
+
         protected async Task FetchChaptersFromXmlNode(BaseItem item, XmlReader reader, IItemRepository repository, CancellationToken cancellationToken)
         {
             var runtime = item.RunTimeTicks ?? 0;
@@ -1194,9 +1237,9 @@ namespace MediaBrowser.Controller.Providers
 
             var personInfo = new PersonInfo
             {
-                Name = name.Trim(), 
-                Role = role, 
-                Type = type, 
+                Name = name.Trim(),
+                Role = role,
+                Type = type,
                 SortOrder = sortOrder
             };
 

+ 0 - 5
MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs

@@ -133,11 +133,6 @@ namespace MediaBrowser.Controller.Resolvers
         /// <param name="includeCreationTime">if set to <c>true</c> [include creation time].</param>
         public static void EnsureDates(IFileSystem fileSystem, BaseItem item, ItemResolveArgs args, bool includeCreationTime)
         {
-            if (!Path.IsPathRooted(item.Path))
-            {
-                return;
-            }
-
             // See if a different path came out of the resolver than what went in
             if (!string.Equals(args.Path, item.Path, StringComparison.OrdinalIgnoreCase))
             {

+ 6 - 6
MediaBrowser.Mono.sln

@@ -17,7 +17,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.WebDashboard",
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Api", "MediaBrowser.Api\MediaBrowser.Api.csproj", "{4FD51AC5-2C16-4308-A993-C3A84F3B4582}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Mono", "MediaBrowser.Server.Mono\MediaBrowser.Server.Mono.csproj", "{A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Mono", "MediaBrowser.Server.Mono\MediaBrowser.Server.Mono.csproj", "{175A9388-F352-4586-A6B4-070DED62B644}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -25,6 +25,10 @@ Global
 		Release|x86 = Release|x86
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{175A9388-F352-4586-A6B4-070DED62B644}.Debug|x86.ActiveCfg = Debug|x86
+		{175A9388-F352-4586-A6B4-070DED62B644}.Debug|x86.Build.0 = Debug|x86
+		{175A9388-F352-4586-A6B4-070DED62B644}.Release|x86.ActiveCfg = Release|x86
+		{175A9388-F352-4586-A6B4-070DED62B644}.Release|x86.Build.0 = Release|x86
 		{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|x86.ActiveCfg = Debug|x86
 		{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|x86.Build.0 = Debug|x86
 		{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|x86.ActiveCfg = Release|x86
@@ -53,16 +57,12 @@ Global
 		{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|x86.Build.0 = Debug|Any CPU
 		{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|x86.ActiveCfg = Release|Any CPU
 		{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|x86.Build.0 = Release|Any CPU
-		{A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}.Debug|x86.ActiveCfg = Debug|x86
-		{A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}.Debug|x86.Build.0 = Debug|x86
-		{A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}.Release|x86.ActiveCfg = Release|x86
-		{A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}.Release|x86.Build.0 = Release|x86
 		{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x86.ActiveCfg = Debug|Any CPU
 		{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x86.Build.0 = Debug|Any CPU
 		{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x86.ActiveCfg = Release|Any CPU
 		{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(MonoDevelopProperties) = preSolution
-		StartupItem = MediaBrowser.Server.Mono\MediaBrowser.Server.Mono.csproj
+		StartupItem = MediaBrowser.Model\MediaBrowser.Model.csproj
 	EndGlobalSection
 EndGlobal

+ 6 - 22
MediaBrowser.Mono.userprefs

@@ -1,32 +1,16 @@
 <Properties>
-  <MonoDevelop.Ide.Workspace ActiveConfiguration="Debug|x86" />
-  <MonoDevelop.Ide.Workbench ActiveDocument="MediaBrowser.Server.Implementations\Persistence\SqliteExtensions.cs">
+  <MonoDevelop.Ide.Workspace ActiveConfiguration="Release|x86" />
+  <MonoDevelop.Ide.Workbench ActiveDocument="MediaBrowser.ServerApplication\EntryPoints\StartupWizard.cs">
     <Files>
-      <File FileName="MediaBrowser.Server.Mono\Program.cs" Line="1" Column="1" />
-      <File FileName="MediaBrowser.Server.Mono\IO\FileSystemFactory.cs" Line="1" Column="1" />
-      <File FileName="MediaBrowser.Server.Implementations\Persistence\SqliteExtensions.cs" Line="22" Column="29" />
-      <File FileName="MediaBrowser.Server.Mono\Native\ServerAuthorization.cs" Line="1" Column="1" />
-      <File FileName="MediaBrowser.Server.Mono\Native\NativeApp.cs" Line="1" Column="1" />
-      <File FileName="MediaBrowser.Server.Mono\Native\Assemblies.cs" Line="1" Column="1" />
-      <File FileName="MediaBrowser.Server.Mono\Native\Autorun.cs" Line="1" Column="1" />
+      <File FileName="MediaBrowser.Server.Mono\Program.cs" Line="13" Column="1" />
+      <File FileName="MediaBrowser.ServerApplication\EntryPoints\StartupWizard.cs" Line="32" Column="23" />
     </Files>
     <Pads>
       <Pad Id="ProjectPad">
         <State expanded="True">
-          <Node name="MediaBrowser.Common" expanded="True" />
-          <Node name="MediaBrowser.Common.Implementations" expanded="True" />
-          <Node name="MediaBrowser.Controller" expanded="True" />
-          <Node name="MediaBrowser.Model" expanded="True">
-            <Node name="References" expanded="True" />
-            <Node name="Web" expanded="True" />
-          </Node>
-          <Node name="MediaBrowser.Server.Implementations" expanded="True">
-            <Node name="Persistence" expanded="True">
-              <Node name="SqliteExtensions.cs" selected="True" />
-            </Node>
-          </Node>
-          <Node name="MediaBrowser.Server.Mono" expanded="True">
+          <Node name="MediaBrowser.Server.Mono" expanded="True" selected="True">
             <Node name="References" expanded="True" />
+            <Node name="EntryPoints" expanded="True" />
           </Node>
         </State>
       </Pad>

+ 5 - 1
MediaBrowser.Providers/ImageFromMediaLocationProvider.cs

@@ -560,7 +560,11 @@ namespace MediaBrowser.Providers
 
             if (screenshotFiles.Count > 0)
             {
-                item.ScreenshotImagePaths = screenshotFiles;
+                var hasScreenshots = item as IHasScreenshots;
+                if (hasScreenshots != null)
+                {
+                    hasScreenshots.ScreenshotImagePaths = screenshotFiles;
+                }
             }
         }
 

+ 1 - 1
MediaBrowser.Providers/Movies/MovieDbPersonProvider.cs

@@ -310,7 +310,7 @@ namespace MediaBrowser.Providers.Movies
             {
                 if (!string.IsNullOrEmpty(searchResult.place_of_birth))
                 {
-                    person.ProductionLocations = new List<string> { searchResult.place_of_birth };
+                    person.PlaceOfBirth = searchResult.place_of_birth;
                 }
             }
 

+ 11 - 3
MediaBrowser.Providers/Movies/MovieDbProvider.cs

@@ -710,8 +710,12 @@ namespace MediaBrowser.Providers.Movies
 
             if (!string.IsNullOrEmpty(movieData.tagline))
             {
-                movie.Taglines.Clear();
-                movie.AddTagline(movieData.tagline);
+                var hasTagline = movie as IHasTaglines;
+                if (hasTagline != null)
+                {
+                    hasTagline.Taglines.Clear();
+                    hasTagline.AddTagline(movieData.tagline);
+                }
             }
 
             movie.SetProviderId(MetadataProviders.Tmdb, movieData.id.ToString(_usCulture));
@@ -868,7 +872,11 @@ namespace MediaBrowser.Providers.Movies
 
             if (movieData.keywords != null && movieData.keywords.keywords != null && !movie.LockedFields.Contains(MetadataFields.Tags))
             {
-                movie.Tags = movieData.keywords.keywords.Select(i => i.name).ToList();
+                var hasTags = movie as IHasTags;
+                if (hasTags != null)
+                {
+                    hasTags.Tags = movieData.keywords.keywords.Select(i => i.name).ToList();
+                }
             }
 
             if (movieData.trailers != null && movieData.trailers.youtube != null &&

+ 1 - 14
MediaBrowser.Providers/Music/LastfmArtistProvider.cs

@@ -127,19 +127,6 @@ namespace MediaBrowser.Providers.Music
             return true;
         }
 
-        /// <summary>
-        /// Finds the id from music artist entity.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>System.String.</returns>
-        private string FindIdFromMusicArtistEntity(BaseItem item)
-        {
-            var artist = LibraryManager.RootFolder.RecursiveChildren.OfType<MusicArtist>()
-                .FirstOrDefault(i => string.Compare(i.Name, item.Name, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols) == 0);
-
-            return artist != null ? artist.GetProviderId(MetadataProviders.Musicbrainz) : null;
-        }
-
         /// <summary>
         /// Finds the id from music brainz.
         /// </summary>
@@ -244,7 +231,7 @@ namespace MediaBrowser.Providers.Music
 
             if (result != null && result.artist != null)
             {
-                LastfmHelper.ProcessArtistData(item, result.artist);
+                LastfmHelper.ProcessArtistData((MusicArtist)item, result.artist);
             }
         }
 

+ 9 - 10
MediaBrowser.Providers/Music/LastfmHelper.cs

@@ -8,7 +8,7 @@ namespace MediaBrowser.Providers.Music
 {
     public static class LastfmHelper
     {
-        public static void ProcessArtistData(BaseItem artist, LastfmArtist data)
+        public static void ProcessArtistData(MusicArtist artist, LastfmArtist data)
         {
             var yearFormed = 0;
 
@@ -37,14 +37,9 @@ namespace MediaBrowser.Providers.Music
                 AddTags(artist, data.tags);
             }
 
-            var musicArtist = artist as MusicArtist;
-
-            if (musicArtist != null)
-            {
-                string imageSize;
-                musicArtist.LastFmImageUrl = GetImageUrl(data, out imageSize);
-                musicArtist.LastFmImageSize = imageSize;
-            }
+            string imageSize;
+            artist.LastFmImageUrl = GetImageUrl(data, out imageSize);
+            artist.LastFmImageSize = imageSize;
         }
 
         private static string GetImageUrl(IHasLastFmImages data, out string size)
@@ -118,7 +113,11 @@ namespace MediaBrowser.Providers.Music
         {
             var itemTags = (from tag in tags.tag where !string.IsNullOrEmpty(tag.name) select tag.name).ToList();
 
-            item.Tags = itemTags;
+            var hasTags = item as IHasTags;
+            if (hasTags != null)
+            {
+                hasTags.Tags = itemTags;
+            }
         }
     }
 }

+ 4 - 2
MediaBrowser.Providers/Savers/PersonXmlSaver.cs

@@ -49,9 +49,11 @@ namespace MediaBrowser.Providers.Savers
 
             XmlSaverHelpers.AddCommonNodes(item, builder);
 
-            if (item.ProductionLocations.Count > 0)
+            var person = (Person)item;
+
+            if (!string.IsNullOrEmpty(person.PlaceOfBirth))
             {
-                builder.Append("<PlaceOfBirth>" + SecurityElement.Escape(item.ProductionLocations[0]) + "</PlaceOfBirth>");
+                builder.Append("<PlaceOfBirth>" + SecurityElement.Escape(person.PlaceOfBirth) + "</PlaceOfBirth>");
             }
 
             builder.Append("</Item>");

+ 29 - 17
MediaBrowser.Providers/Savers/XmlSaverHelpers.cs

@@ -326,9 +326,13 @@ namespace MediaBrowser.Providers.Savers
                 }
             }
 
-            if (!string.IsNullOrEmpty(item.Language))
+            var hasLanguage = item as IHasLanguage;
+            if (hasLanguage != null)
             {
-                builder.Append("<Language>" + SecurityElement.Escape(item.Language) + "</Language>");
+                if (!string.IsNullOrEmpty(hasLanguage.Language))
+                {
+                    builder.Append("<Language>" + SecurityElement.Escape(hasLanguage.Language) + "</Language>");
+                }
             }
 
             // Use original runtime here, actual file runtime later in MediaInfo
@@ -417,18 +421,22 @@ namespace MediaBrowser.Providers.Savers
                 builder.Append("<TMDbCollectionId>" + SecurityElement.Escape(tmdbCollection) + "</TMDbCollectionId>");
             }
 
-            if (item.Taglines.Count > 0)
+            var hasTagline = item as IHasTaglines;
+            if (hasTagline != null)
             {
-                builder.Append("<TagLine>" + SecurityElement.Escape(item.Taglines[0]) + "</TagLine>");
+                if (hasTagline.Taglines.Count > 0)
+                {
+                    builder.Append("<TagLine>" + SecurityElement.Escape(hasTagline.Taglines[0]) + "</TagLine>");
 
-                builder.Append("<Taglines>");
+                    builder.Append("<Taglines>");
 
-                foreach (var tagline in item.Taglines)
-                {
-                    builder.Append("<Tagline>" + SecurityElement.Escape(tagline) + "</Tagline>");
-                }
+                    foreach (var tagline in hasTagline.Taglines)
+                    {
+                        builder.Append("<Tagline>" + SecurityElement.Escape(tagline) + "</Tagline>");
+                    }
 
-                builder.Append("</Taglines>");
+                    builder.Append("</Taglines>");
+                }
             }
 
             if (item.Genres.Count > 0)
@@ -457,16 +465,20 @@ namespace MediaBrowser.Providers.Savers
                 builder.Append("</Studios>");
             }
 
-            if (item.Tags.Count > 0)
+            var hasTags = item as IHasTags;
+            if (hasTags != null)
             {
-                builder.Append("<Tags>");
-
-                foreach (var tag in item.Tags)
+                if (hasTags.Tags.Count > 0)
                 {
-                    builder.Append("<Tag>" + SecurityElement.Escape(tag) + "</Tag>");
-                }
+                    builder.Append("<Tags>");
 
-                builder.Append("</Tags>");
+                    foreach (var tag in hasTags.Tags)
+                    {
+                        builder.Append("<Tag>" + SecurityElement.Escape(tag) + "</Tag>");
+                    }
+
+                    builder.Append("</Tags>");
+                }
             }
 
             if (item.People.Count > 0)

+ 0 - 10
MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs

@@ -471,16 +471,6 @@ namespace MediaBrowser.Providers.TV
                                         break;
                                     }
 
-                                case "Language":
-                                    {
-                                        var val = reader.ReadElementContentAsString();
-                                        if (!string.IsNullOrWhiteSpace(val))
-                                        {
-                                            item.Language = val;
-                                        }
-                                        break;
-                                    }
-
                                 case "filename":
                                     {
                                         if (string.IsNullOrEmpty(item.PrimaryImagePath))

+ 59 - 5
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -392,7 +392,13 @@ namespace MediaBrowser.Server.Implementations.Dto
         /// <returns>List{Guid}.</returns>
         private List<Guid> GetScreenshotImageTags(BaseItem item)
         {
-            return item.ScreenshotImagePaths
+            var hasScreenshots = item as IHasScreenshots;
+            if (hasScreenshots == null)
+            {
+                return new List<Guid>();
+            }
+
+            return hasScreenshots.ScreenshotImagePaths
                 .Select(p => GetImageCacheTag(item, ImageType.Screenshot, p))
                 .Where(i => i.HasValue)
                 .Select(i => i.Value)
@@ -746,12 +752,21 @@ namespace MediaBrowser.Server.Implementations.Dto
 
             if (fields.Contains(ItemFields.Tags))
             {
-                dto.Tags = item.Tags;
+                var hasTags = item as IHasTags;
+                if (hasTags != null)
+                {
+                    dto.Tags = hasTags.Tags;
+                }
+
+                if (dto.Tags == null)
+                {
+                    dto.Tags = new List<string>();
+                }
             }
 
             if (fields.Contains(ItemFields.ProductionLocations))
             {
-                dto.ProductionLocations = item.ProductionLocations;
+                SetProductionLocations(item, dto);
             }
 
             var hasAspectRatio = item as IHasAspectRatio;
@@ -789,10 +804,15 @@ namespace MediaBrowser.Server.Implementations.Dto
             dto.Id = GetDtoId(item);
             dto.IndexNumber = item.IndexNumber;
             dto.IsFolder = item.IsFolder;
-            dto.Language = item.Language;
             dto.MediaType = item.MediaType;
             dto.LocationType = item.LocationType;
 
+            var hasLanguage = item as IHasLanguage;
+            if (hasLanguage != null)
+            {
+                dto.Language = hasLanguage.Language;
+            }
+
             var hasCriticRating = item as IHasCriticRating;
             if (hasCriticRating != null)
             {
@@ -924,7 +944,16 @@ namespace MediaBrowser.Server.Implementations.Dto
 
             if (fields.Contains(ItemFields.Taglines))
             {
-                dto.Taglines = item.Taglines;
+                var hasTagline = item as IHasTaglines;
+                if (hasTagline != null)
+                {
+                    dto.Taglines = hasTagline.Taglines;
+                }
+
+                if (dto.Taglines == null)
+                {
+                    dto.Taglines = new List<string>();
+                }
             }
 
             dto.Type = item.GetClientTypeName();
@@ -1122,6 +1151,31 @@ namespace MediaBrowser.Server.Implementations.Dto
             }
         }
 
+        private void SetProductionLocations(BaseItem item, BaseItemDto dto)
+        {
+            var hasProductionLocations = item as IHasProductionLocations;
+
+            if (hasProductionLocations != null)
+            {
+                dto.ProductionLocations = hasProductionLocations.ProductionLocations;
+            }
+
+            var person = item as Person;
+            if (person != null)
+            {
+                dto.ProductionLocations = new List<string>();
+                if (!string.IsNullOrEmpty(person.PlaceOfBirth))
+                {
+                    dto.ProductionLocations.Add(person.PlaceOfBirth);
+                }
+            }
+
+            if (dto.ProductionLocations == null)
+            {
+                dto.ProductionLocations = new List<string>();
+            }
+        }
+
         /// <summary>
         /// Since it can be slow to make all of these calculations independently, this method will provide a way to do them all at once
         /// </summary>

+ 1 - 1
MediaBrowser.Server.Implementations/HttpServer/SwaggerService.cs

@@ -38,7 +38,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
 
             var swaggerDirectory = Path.Combine(runningDirectory, "swagger-ui");
 
-            var requestedFile = Path.Combine(swaggerDirectory, request.ResourceName.Replace('/', '\\'));
+            var requestedFile = Path.Combine(swaggerDirectory, request.ResourceName.Replace('/', Path.DirectorySeparatorChar));
 
             return ResultFactory.GetStaticFileResult(RequestContext, requestedFile);
         }

+ 0 - 1
MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs

@@ -150,7 +150,6 @@ namespace MediaBrowser.Server.Implementations.IO
                         }
 
                     })
-                .Where(Path.IsPathRooted)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .OrderBy(i => i)
                 .ToList();

+ 1 - 1
MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs

@@ -43,7 +43,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
                 }
                 
                 // Optimization to avoid running these tests against Seasons
-                if (args.Parent is Series || args.Parent is MusicArtist || args.Parent is MusicAlbum)
+                if (args.Parent is Series || args.Parent is Season || args.Parent is MusicArtist || args.Parent is MusicAlbum)
                 {
                     return null;
                 }

+ 10 - 9
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -308,29 +308,30 @@ namespace MediaBrowser.Server.Implementations.LiveTv
         internal async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
         {
             // Avoid implicitly captured closure
-            var currentCancellationToken = cancellationToken;
+            var service = ActiveService;
 
-            var channelTasks = _services.Select(i => GetChannels(i, currentCancellationToken));
+            if (service == null)
+            {
+                progress.Report(100);
+                return;
+            }
 
             progress.Report(10);
 
-            var results = await Task.WhenAll(channelTasks).ConfigureAwait(false);
-
-            var allChannels = results.SelectMany(i => i).ToList();
+            var allChannels = await GetChannels(service, cancellationToken).ConfigureAwait(false);
+            var allChannelsList = allChannels.ToList();
 
             var list = new List<Channel>();
             var programs = new List<ProgramInfoDto>();
 
             var numComplete = 0;
 
-            foreach (var channelInfo in allChannels)
+            foreach (var channelInfo in allChannelsList)
             {
                 try
                 {
                     var item = await GetChannel(channelInfo.Item2, channelInfo.Item1, cancellationToken).ConfigureAwait(false);
 
-                    var service = _services.First(i => string.Equals(channelInfo.Item1, i.Name, StringComparison.OrdinalIgnoreCase));
-
                     var channelPrograms = await service.GetProgramsAsync(channelInfo.Item2.Id, cancellationToken).ConfigureAwait(false);
 
                     programs.AddRange(channelPrograms.Select(program => GetProgramInfoDto(program, item)));
@@ -348,7 +349,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
                 numComplete++;
                 double percent = numComplete;
-                percent /= allChannels.Count;
+                percent /= allChannelsList.Count;
 
                 progress.Report(90 * percent + 10);
             }

+ 30 - 40
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -37,54 +37,14 @@
     <Reference Include="Alchemy">
       <HintPath>..\packages\Alchemy.2.2.1\lib\net40\Alchemy.dll</HintPath>
     </Reference>
-    <Reference Include="BDInfo, Version=1.0.5037.22194, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.5\lib\net20\BDInfo.dll</HintPath>
-    </Reference>
     <Reference Include="Mono.Data.Sqlite">
       <HintPath>..\packages\ServiceStack.OrmLite.Sqlite.Mono.3.9.70\lib\net35\Mono.Data.Sqlite.dll</HintPath>
     </Reference>
-    <Reference Include="ServiceStack, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.3.9.70\lib\net35\ServiceStack.dll</HintPath>
-    </Reference>
-    <Reference Include="ServiceStack.Api.Swagger, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.Api.Swagger.3.9.70\lib\net35\ServiceStack.Api.Swagger.dll</HintPath>
-    </Reference>
-    <Reference Include="ServiceStack.Common, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll</HintPath>
-    </Reference>
-    <Reference Include="ServiceStack.Interfaces, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll</HintPath>
-    </Reference>
-    <Reference Include="ServiceStack.OrmLite, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.OrmLite.Sqlite.Mono.3.9.70\lib\net35\ServiceStack.OrmLite.dll</HintPath>
-    </Reference>
     <Reference Include="ServiceStack.OrmLite.Sqlite">
       <HintPath>..\packages\ServiceStack.OrmLite.Sqlite.Mono.3.9.70\lib\net35\ServiceStack.OrmLite.Sqlite.dll</HintPath>
     </Reference>
-    <Reference Include="ServiceStack.ServiceInterface, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.3.9.70\lib\net35\ServiceStack.ServiceInterface.dll</HintPath>
-    </Reference>
-    <Reference Include="ServiceStack.Text, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
-    <Reference Include="System.Data.SQLite, Version=1.0.89.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\System.Data.SQLite.x86.1.0.89.0\lib\net45\System.Data.SQLite.dll</HintPath>
-    </Reference>
-    <Reference Include="System.Data.SQLite.Linq, Version=1.0.89.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\System.Data.SQLite.x86.1.0.89.0\lib\net45\System.Data.SQLite.Linq.dll</HintPath>
-    </Reference>
     <Reference Include="System.Drawing" />
     <Reference Include="System.Reactive.Core">
       <HintPath>..\packages\Rx-Core.2.1.30214.0\lib\Net45\System.Reactive.Core.dll</HintPath>
@@ -109,6 +69,36 @@
     <Reference Include="ServiceStack.Redis">
       <HintPath>..\packages\ServiceStack.Redis.3.9.43\lib\net35\ServiceStack.Redis.dll</HintPath>
     </Reference>
+    <Reference Include="BDInfo">
+      <HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.5\lib\net20\BDInfo.dll</HintPath>
+    </Reference>
+    <Reference Include="ServiceStack">
+      <HintPath>..\packages\ServiceStack.3.9.70\lib\net35\ServiceStack.dll</HintPath>
+    </Reference>
+    <Reference Include="ServiceStack.Api.Swagger">
+      <HintPath>..\packages\ServiceStack.Api.Swagger.3.9.70\lib\net35\ServiceStack.Api.Swagger.dll</HintPath>
+    </Reference>
+    <Reference Include="ServiceStack.Common">
+      <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll</HintPath>
+    </Reference>
+    <Reference Include="ServiceStack.Interfaces">
+      <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll</HintPath>
+    </Reference>
+    <Reference Include="ServiceStack.OrmLite">
+      <HintPath>..\packages\ServiceStack.OrmLite.Sqlite.Mono.3.9.70\lib\net35\ServiceStack.OrmLite.dll</HintPath>
+    </Reference>
+    <Reference Include="ServiceStack.ServiceInterface">
+      <HintPath>..\packages\ServiceStack.3.9.70\lib\net35\ServiceStack.ServiceInterface.dll</HintPath>
+    </Reference>
+    <Reference Include="ServiceStack.Text">
+      <HintPath>..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Data.SQLite">
+      <HintPath>..\packages\System.Data.SQLite.x86.1.0.89.0\lib\net45\System.Data.SQLite.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Data.SQLite.Linq">
+      <HintPath>..\packages\System.Data.SQLite.x86.1.0.89.0\lib\net45\System.Data.SQLite.Linq.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">

+ 11 - 7
MediaBrowser.Server.Implementations/Providers/ImageSaver.cs

@@ -114,7 +114,8 @@ namespace MediaBrowser.Server.Implementations.Providers
             }
             else if (type == ImageType.Screenshot && imageIndex == null)
             {
-                imageIndex = item.ScreenshotImagePaths.Count;
+                var hasScreenshots = (IHasScreenshots)item;
+                imageIndex = hasScreenshots.ScreenshotImagePaths.Count;
             }
 
             var paths = GetSavePaths(item, type, imageIndex, mimeType, saveLocally);
@@ -262,11 +263,12 @@ namespace MediaBrowser.Server.Implementations.Providers
             {
                 case ImageType.Screenshot:
 
+                    var hasScreenshots = (IHasScreenshots)item;
                     if (!imageIndex.HasValue)
                     {
                         throw new ArgumentNullException("imageIndex");
                     }
-                    return item.ScreenshotImagePaths.Count > imageIndex.Value ? item.ScreenshotImagePaths[imageIndex.Value] : null;
+                    return hasScreenshots.ScreenshotImagePaths.Count > imageIndex.Value ? hasScreenshots.ScreenshotImagePaths[imageIndex.Value] : null;
                 case ImageType.Backdrop:
                     if (!imageIndex.HasValue)
                     {
@@ -300,13 +302,14 @@ namespace MediaBrowser.Server.Implementations.Providers
                         throw new ArgumentNullException("imageIndex");
                     }
 
-                    if (item.ScreenshotImagePaths.Count > imageIndex.Value)
+                    var hasScreenshots = (IHasScreenshots)item;
+                    if (hasScreenshots.ScreenshotImagePaths.Count > imageIndex.Value)
                     {
-                        item.ScreenshotImagePaths[imageIndex.Value] = path;
+                        hasScreenshots.ScreenshotImagePaths[imageIndex.Value] = path;
                     }
-                    else if (!item.ScreenshotImagePaths.Contains(path, StringComparer.OrdinalIgnoreCase))
+                    else if (!hasScreenshots.ScreenshotImagePaths.Contains(path, StringComparer.OrdinalIgnoreCase))
                     {
-                        item.ScreenshotImagePaths.Add(path);
+                        hasScreenshots.ScreenshotImagePaths.Add(path);
                     }
                     break;
                 case ImageType.Backdrop:
@@ -379,7 +382,8 @@ namespace MediaBrowser.Server.Implementations.Providers
                     {
                         throw new ArgumentNullException("imageIndex");
                     }
-                    filename = GetBackdropSaveFilename(item.ScreenshotImagePaths, "screenshot", "screenshot", imageIndex.Value);
+                    var hasScreenshots = (IHasScreenshots)item;
+                    filename = GetBackdropSaveFilename(hasScreenshots.ScreenshotImagePaths, "screenshot", "screenshot", imageIndex.Value);
                     break;
                 default:
                     filename = type.ToString().ToLower();

+ 5 - 27
MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj

@@ -5,13 +5,12 @@
     <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
     <ProductVersion>10.0.0</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}</ProjectGuid>
-    <OutputType>WinExe</OutputType>
+    <ProjectGuid>{175A9388-F352-4586-A6B4-070DED62B644}</ProjectGuid>
+    <OutputType>Exe</OutputType>
     <RootNamespace>MediaBrowser.Server.Mono</RootNamespace>
     <AssemblyName>MediaBrowser.Server.Mono</AssemblyName>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <StartupObject>MediaBrowser.Server.Mono.MainClass</StartupObject>
-    <ApplicationIcon>..\MediaBrowser.ServerApplication\Resources\Images\Icon.ico</ApplicationIcon>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
     <DebugSymbols>true</DebugSymbols>
@@ -22,7 +21,7 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <PlatformTarget>x86</PlatformTarget>
-    <ConsolePause>false</ConsolePause>
+    <Externalconsole>true</Externalconsole>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
     <DebugType>full</DebugType>
@@ -31,20 +30,10 @@
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <PlatformTarget>x86</PlatformTarget>
-    <ConsolePause>false</ConsolePause>
+    <Externalconsole>true</Externalconsole>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
-    <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
-    <Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
-    <Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
-    <Reference Include="glade-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
-    <Reference Include="pango-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
-    <Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
-    <Reference Include="System.Windows.Forms" />
-    <Reference Include="System.Net.Http" />
-    <Reference Include="System.Data" />
-    <Reference Include="Mono.Posix" />
     <Reference Include="ServiceStack.Common">
       <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll</HintPath>
     </Reference>
@@ -52,18 +41,10 @@
       <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll</HintPath>
     </Reference>
   </ItemGroup>
-  <ItemGroup>
-    <EmbeddedResource Include="gtk-gui\gui.stetic">
-      <LogicalName>gui.stetic</LogicalName>
-    </EmbeddedResource>
-  </ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">
       <Link>Properties\SharedVersion.cs</Link>
     </Compile>
-    <Compile Include="gtk-gui\generated.cs" />
-    <Compile Include="MainWindow.cs" />
-    <Compile Include="gtk-gui\MainWindow.cs" />
     <Compile Include="Program.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="..\MediaBrowser.ServerApplication\EntryPoints\StartupWizard.cs">
@@ -133,9 +114,6 @@
   </ItemGroup>
   <ItemGroup>
     <None Include="app.config" />
-    <None Include="tray.png">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
     <None Include="sqlite3.dll">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>

+ 12 - 120
MediaBrowser.Server.Mono/Program.cs

@@ -14,8 +14,6 @@ using System.Windows;
 using System.Net;
 using System.Net.Security;
 using System.Security.Cryptography.X509Certificates;
-using Gtk;
-using Gdk;
 using System.Threading.Tasks;
 using System.Reflection;
 
@@ -27,15 +25,8 @@ namespace MediaBrowser.Server.Mono
 
 		private static ILogger _logger;
 
-		private static MainWindow _mainWindow;
-
-		// The tray Icon
-		private static StatusIcon trayIcon;
-
 		public static void Main (string[] args)
 		{
-			Application.Init ();
-
 			var applicationPath = Assembly.GetEntryAssembly ().Location;
 
 			var appPaths = CreateApplicationPaths(applicationPath);
@@ -98,10 +89,10 @@ namespace MediaBrowser.Server.Mono
 
 		private static RemoteCertificateValidationCallback _ignoreCertificates = new RemoteCertificateValidationCallback(delegate { return true; });
 
+		private static TaskCompletionSource<bool> _applicationTaskCompletionSource = new TaskCompletionSource<bool>();
+
 		private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager)
 		{
-			// TODO: Show splash here
-
 			SystemEvents.SessionEnding += SystemEvents_SessionEnding;
 
 			// Allow all https requests
@@ -109,77 +100,19 @@ namespace MediaBrowser.Server.Mono
 
 			_appHost = new ApplicationHost(appPaths, logManager);
 
+			Console.WriteLine ("appHost.Init");
+
 			var task = _appHost.Init();
 			Task.WaitAll (task);
 
+			Console.WriteLine ("Running startup tasks");
+
 			task = _appHost.RunStartupTasks();
 			Task.WaitAll (task);
 
-			// TODO: Hide splash here
-			_mainWindow = new MainWindow ();
-
-			// Creation of the Icon
-			// Creation of the Icon
-			trayIcon = new StatusIcon(new Pixbuf ("tray.png"));
-			trayIcon.Visible = true;
-
-			// When the TrayIcon has been clicked.
-			trayIcon.Activate += delegate { };
-			// Show a pop up menu when the icon has been right clicked.
-			trayIcon.PopupMenu += OnTrayIconPopup;
+			task = _applicationTaskCompletionSource.Task;
 
-			// A Tooltip for the Icon
-			trayIcon.Tooltip = "Media Browser Server";
-
-			_mainWindow.ShowAll ();
-			_mainWindow.Visible = false;
-
-			Application.Run ();
-		}
-
-		// Create the popup menu, on right click.
-		static void OnTrayIconPopup (object o, EventArgs args) {
-
-			Menu popupMenu = new Menu();
-
-			var menuItemBrowse = new ImageMenuItem ("Browse Library");
-			menuItemBrowse.Image = new Gtk.Image(Stock.MediaPlay, IconSize.Menu);
-			popupMenu.Add(menuItemBrowse);
-			menuItemBrowse.Activated += delegate { 
-				BrowserLauncher.OpenWebClient(_appHost.UserManager, _appHost.ServerConfigurationManager, _appHost, _logger);
-			};
-
-			var menuItemConfigure = new ImageMenuItem ("Configure Media Browser");
-			menuItemConfigure.Image = new Gtk.Image(Stock.Edit, IconSize.Menu);
-			popupMenu.Add(menuItemConfigure);
-			menuItemConfigure.Activated += delegate { 
-				BrowserLauncher.OpenDashboard(_appHost.UserManager, _appHost.ServerConfigurationManager, _appHost, _logger);
-			};
-
-			var menuItemApi = new ImageMenuItem ("View Api Docs");
-			menuItemApi.Image = new Gtk.Image(Stock.Network, IconSize.Menu);
-			popupMenu.Add(menuItemApi);
-			menuItemApi.Activated += delegate { 
-				BrowserLauncher.OpenSwagger(_appHost.ServerConfigurationManager, _appHost, _logger);
-			};
-
-			var menuItemCommunity = new ImageMenuItem ("Visit Community");
-			menuItemCommunity.Image = new Gtk.Image(Stock.Help, IconSize.Menu);
-			popupMenu.Add(menuItemCommunity);
-			menuItemCommunity.Activated += delegate { BrowserLauncher.OpenCommunity(_logger); };
-
-			var menuItemGithub = new ImageMenuItem ("Visit Github");
-			menuItemGithub.Image = new Gtk.Image(Stock.Network, IconSize.Menu);
-			popupMenu.Add(menuItemGithub);
-			menuItemGithub.Activated += delegate { BrowserLauncher.OpenGithub(_logger); };
-
-			var menuItemQuit = new ImageMenuItem ("Exit");
-			menuItemQuit.Image = new Gtk.Image(Stock.Quit, IconSize.Menu);
-			popupMenu.Add(menuItemQuit);
-			menuItemQuit.Activated += delegate { Shutdown(); };
-
-			popupMenu.ShowAll();
-			popupMenu.Popup();
+			Task.WaitAll (task);
 		}
 
 		/// <summary>
@@ -206,8 +139,6 @@ namespace MediaBrowser.Server.Mono
 
 			logger.Info("Server: {0}", Environment.MachineName);
 			logger.Info("Operating system: {0}", Environment.OSVersion.ToString());
-
-			MonoBug11817WorkAround.Apply ();
 		}
 
 		/// <summary>
@@ -237,6 +168,9 @@ namespace MediaBrowser.Server.Mono
 
 			var builder = LogHelper.GetLogMessage(ex);
 
+			Console.WriteLine ("UnhandledException");
+			Console.WriteLine (builder.ToString());
+
 			File.WriteAllText(path, builder.ToString());
 		}
 
@@ -253,19 +187,7 @@ namespace MediaBrowser.Server.Mono
 
 		public static void Shutdown()
 		{
-			if (trayIcon != null) {
-				trayIcon.Visible = false;
-				trayIcon.Dispose ();
-				trayIcon = null;
-			}
-
-			if (_mainWindow != null) {
-				_mainWindow.HideAll ();
-				_mainWindow.Dispose ();
-				_mainWindow = null;
-			}
-
-			Application.Quit ();
+			_applicationTaskCompletionSource.SetResult (true);
 		}
 
 		public static void Restart()
@@ -285,34 +207,4 @@ namespace MediaBrowser.Server.Mono
 			return true;
 		}
 	}
-
-	public class MonoBug11817WorkAround
-	{
-		public static void Apply()
-		{
-			var property = typeof(TimeZoneInfo).GetProperty("TimeZoneDirectory", BindingFlags.Static | BindingFlags.NonPublic);
-
-			if (property == null) return;
-
-			var zoneInfo = FindZoneInfoFolder();
-			property.SetValue(null, zoneInfo, new object[0]);
-		}
-
-		public static string FindZoneInfoFolder()
-		{
-			var current = new DirectoryInfo(Directory.GetCurrentDirectory());
-
-			while(current != null)
-			{
-				var zoneinfoTestPath = Path.Combine(current.FullName, "zoneinfo");
-
-				if (Directory.Exists(zoneinfoTestPath))
-					return zoneinfoTestPath;
-
-				current = current.Parent;
-			}
-
-			return null;
-		}
-	}
 }

+ 4 - 1
MediaBrowser.Server.Mono/Properties/AssemblyInfo.cs

@@ -10,4 +10,7 @@ using System.Runtime.CompilerServices;
 [assembly: AssemblyProduct ("")]
 [assembly: AssemblyCopyright ("Luke")]
 [assembly: AssemblyTrademark ("")]
-[assembly: AssemblyCulture ("")]
+[assembly: AssemblyCulture ("")]
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.

+ 2 - 2
MediaBrowser.Server.Mono/app.config

@@ -8,7 +8,7 @@
   </nlog>
   <appSettings>
     <add key="DebugProgramDataPath" value="ProgramData-Server" />
-    <add key="ReleaseProgramDataPath" value="%ApplicationData%" />
-    <add key="ProgramDataFolderName" value="MediaBrowser-Server" />
+    <add key="ReleaseProgramDataPath" value="" />
+    <add key="ProgramDataFolderName" value="ProgramData-Server" />
   </appSettings>
 </configuration>

+ 0 - 1
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -57,7 +57,6 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
-using System.Net.Http;
 using System.Reflection;
 using System.Threading;
 using System.Threading.Tasks;

+ 2 - 13
MediaBrowser.ServerApplication/EntryPoints/StartupWizard.cs

@@ -3,10 +3,8 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Model.Logging;
-using System;
-using System.Linq;
-using System.Windows.Forms;
 using MediaBrowser.ServerApplication.Native;
+using System.Linq;
 
 namespace MediaBrowser.ServerApplication.EntryPoints
 {
@@ -58,16 +56,7 @@ namespace MediaBrowser.ServerApplication.EntryPoints
         {
             var user = _userManager.Users.FirstOrDefault(u => u.Configuration.IsAdministrator);
 
-            try
-            {
-                BrowserLauncher.OpenDashboardPage("wizardstart.html", user, _configurationManager, _appHost, _logger);
-            }
-            catch (Exception ex)
-            {
-                _logger.ErrorException("Error launching startup wizard", ex);
-
-                MessageBox.Show("There was an error launching the Media Browser startup wizard. Please ensure a web browser is installed on the machine and is configured as the default browser.", "Media Browser");
-            }
+            BrowserLauncher.OpenDashboardPage("wizardstart.html", user, _configurationManager, _appHost, _logger);
         }
 
         /// <summary>

+ 0 - 2
MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj

@@ -166,8 +166,6 @@
     <Reference Include="System.Data" />
     <Reference Include="System.Drawing" />
     <Reference Include="System.Net" />
-    <Reference Include="System.Net.Http" />
-    <Reference Include="System.Net.Http.WebRequest" />
     <Reference Include="System.ServiceProcess" />
     <Reference Include="System.Windows.Forms" />
     <Reference Include="System.Xml" />

+ 12 - 8
MediaBrowser.ServerApplication/Native/BrowserLauncher.cs

@@ -6,7 +6,6 @@ using MediaBrowser.Model.Logging;
 using System;
 using System.Diagnostics;
 using System.Linq;
-using System.Windows.Forms;
 
 namespace MediaBrowser.ServerApplication.Native
 {
@@ -107,14 +106,14 @@ namespace MediaBrowser.ServerApplication.Native
         private static void OpenUrl(string url, ILogger logger)
         {
             var process = new Process
+            {
+                StartInfo = new ProcessStartInfo
                 {
-                    StartInfo = new ProcessStartInfo
-                        {
-                            FileName = url
-                        },
+                    FileName = url
+                },
 
-                    EnableRaisingEvents = true
-                };
+                EnableRaisingEvents = true,
+            };
 
             process.Exited += ProcessExited;
 
@@ -126,7 +125,12 @@ namespace MediaBrowser.ServerApplication.Native
             {
                 logger.ErrorException("Error launching url: {0}", ex, url);
 
-                MessageBox.Show("There was an error launching your web browser. Please check your default browser settings.");
+                Console.WriteLine("Error launching browser");
+                Console.WriteLine(ex.Message);
+
+#if !__MonoCS__
+                System.Windows.Forms.MessageBox.Show("There was an error launching your web browser. Please check your default browser settings.");
+#endif
             }
         }
 

+ 9 - 12
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -37,24 +37,21 @@
     <RunPostBuildEvent>Always</RunPostBuildEvent>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="ServiceStack.Common, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll</HintPath>
-    </Reference>
-    <Reference Include="ServiceStack.Interfaces, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll</HintPath>
-    </Reference>
-    <Reference Include="ServiceStack.Text, Version=3.9.70.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="System.Data" />
     <Reference Include="System.Xml" />
+    <Reference Include="ServiceStack.Common">
+      <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Common.dll</HintPath>
+    </Reference>
+    <Reference Include="ServiceStack.Interfaces">
+      <HintPath>..\packages\ServiceStack.Common.3.9.70\lib\net35\ServiceStack.Interfaces.dll</HintPath>
+    </Reference>
+    <Reference Include="ServiceStack.Text">
+      <HintPath>..\packages\ServiceStack.Text.3.9.70\lib\net35\ServiceStack.Text.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">