Esempio n. 1
0
 public MediaItemQuery(MediaItemQuery other)
 {
     _filter = other.Filter;
     _necessaryRequestedMIATypeIDs = new HashSet <Guid>(other._necessaryRequestedMIATypeIDs);
     _optionalRequestedMIATypeIDs  = new HashSet <Guid>(other._optionalRequestedMIATypeIDs);
     _sortInformation = other._sortInformation;
 }
Esempio n. 2
0
    public bool TryGetFanArt(FanArtConstants.FanArtMediaType mediaType, FanArtConstants.FanArtType fanArtType, string name, int maxWidth, int maxHeight, bool singleRandom, out IList<FanArtImage> result)
    {
      result = null;
      if (mediaType != FanArtConstants.FanArtMediaType.Album || fanArtType != FanArtConstants.FanArtType.Poster || string.IsNullOrEmpty(name))
        return false;

      IMediaLibrary mediaLibrary = ServiceRegistration.Get<IMediaLibrary>(false);
      if (mediaLibrary == null)
        return false;

      IFilter filter = new RelationalFilter(AudioAspect.ATTR_ALBUM, RelationalOperator.EQ, name);
      MediaItemQuery query = new MediaItemQuery(NECESSARY_MIAS, filter)
        {
          Limit = 1, // Only one needed
          SortInformation = new List<SortInformation> { new SortInformation(AudioAspect.ATTR_ALBUM, SortDirection.Ascending) }
        };


      var items = mediaLibrary.Search(query, false);
      result = new List<FanArtImage>();
      foreach (var mediaItem in items)
      {
        byte[] textureData;
        if (!MediaItemAspect.TryGetAttribute(mediaItem.Aspects, ThumbnailLargeAspect.ATTR_THUMBNAIL, out textureData))
          continue;

        // Only one record required
        result.Add(new FanArtImage(name, textureData));
        return true;
      }
      return true;
    }
 public MediaLibraryQueryViewSpecification(string viewDisplayName, IFilter filter,
     IEnumerable<Guid> necessaryMIATypeIDs, IEnumerable<Guid> optionalMIATypeIDs, bool onlyOnline) :
     base(viewDisplayName, necessaryMIATypeIDs, optionalMIATypeIDs)
 {
   _filter = filter;
   _query = new MediaItemQuery(necessaryMIATypeIDs, optionalMIATypeIDs, filter);
   _onlyOnline = onlyOnline;
 }
Esempio n. 4
0
 public MediaItemQuery(MediaItemQuery other)
 {
     _filter         = other.Filter;
     _subqueryFilter = other.SubqueryFilter;
     _necessaryRequestedMIATypeIDs = new HashSet <Guid>(other._necessaryRequestedMIATypeIDs);
     _optionalRequestedMIATypeIDs  = new HashSet <Guid>(other._optionalRequestedMIATypeIDs);
     _sortInformation = other._sortInformation;
     _limit           = other.Limit;
     _offset          = other.Offset;
 }
    public static CompiledMediaItemQuery Compile(MIA_Management miaManagement, MediaItemQuery query)
    {
      IDictionary<Guid, MediaItemAspectMetadata> availableMIATypes = miaManagement.ManagedMediaItemAspectTypes;
      ICollection<MediaItemAspectMetadata> necessaryMIATypes = new List<MediaItemAspectMetadata>();
      ICollection<MediaItemAspectMetadata> optionalMIATypes = new List<MediaItemAspectMetadata>();
      // Raise exception if necessary MIA types are not present
      foreach (Guid miaTypeID in query.NecessaryRequestedMIATypeIDs)
      {
        MediaItemAspectMetadata miam;
        if (!availableMIATypes.TryGetValue(miaTypeID, out miam))
          throw new InvalidDataException("Necessary requested MIA type '{0}' is not present in the media library", miaTypeID);
        necessaryMIATypes.Add(miam);
      }
      // For optional MIA types, we don't raise an exception if the type is not present
      foreach (Guid miaTypeID in query.OptionalRequestedMIATypeIDs)
      {
        MediaItemAspectMetadata miam;
        if (!availableMIATypes.TryGetValue(miaTypeID, out miam))
          continue;
        optionalMIATypes.Add(miam);
      }

      // Maps (all selected main) MIAM.Attributes to QueryAttributes
      IDictionary<MediaItemAspectMetadata.AttributeSpecification, QueryAttribute> mainSelectedAttributes =
          new Dictionary<MediaItemAspectMetadata.AttributeSpecification, QueryAttribute>();

      // Attributes selected in explicit queries
      ICollection<MediaItemAspectMetadata.AttributeSpecification> explicitSelectAttributes =
          new List<MediaItemAspectMetadata.AttributeSpecification>();

      // Allocate selected attributes to main query and explicit selects
      ICollection<Guid> requestedMIATypeIDs = CollectionUtils.UnionSet(
          query.NecessaryRequestedMIATypeIDs, query.OptionalRequestedMIATypeIDs);
      foreach (Guid miaTypeID in requestedMIATypeIDs)
      {
        MediaItemAspectMetadata miam;
        if (!availableMIATypes.TryGetValue(miaTypeID, out miam))
          // If one of the necessary MIA types is not available, an exception was raised above. So we only
          // come to here if an optional MIA type is not present - simply ignore that.
          continue;
        foreach (MediaItemAspectMetadata.AttributeSpecification attr in miam.AttributeSpecifications.Values)
        {
          if (attr.Cardinality == Cardinality.Inline || attr.Cardinality == Cardinality.ManyToOne)
            mainSelectedAttributes[attr] = new QueryAttribute(attr);
          else
            explicitSelectAttributes.Add(attr);
        }
      }

      return new CompiledMediaItemQuery(miaManagement, necessaryMIATypes, optionalMIATypes,
          mainSelectedAttributes, explicitSelectAttributes, query.Filter, query.SortInformation);
    }
Esempio n. 6
0
    protected void FillList(IContentDirectory contentDirectory, Guid[] necessaryMIAs, ItemsList list, MediaItemToListItemAction converterAction)
    {
      MediaItemQuery query = new MediaItemQuery(necessaryMIAs, null)
      {
        Limit = (uint)QueryLimit, // Last 5 imported items
        SortInformation = new List<SortInformation> { new SortInformation(ImporterAspect.ATTR_DATEADDED, SortDirection.Descending) }
      };

      var items = contentDirectory.Search(query, false);
      list.Clear();
      foreach (MediaItem mediaItem in items)
      {
        PlayableMediaItem listItem = converterAction(mediaItem);
        listItem.Command = new MethodDelegateCommand(() => PlayItemsModel.CheckQueryPlayAction(listItem.MediaItem));
        list.Add(listItem);
      }
      list.FireChange();
    }
 protected internal override void ReLoadItemsAndSubViewSpecifications(out IList<MediaItem> mediaItems, out IList<ViewSpecification> subViewSpecifications)
 {
   mediaItems = null;
   subViewSpecifications = new List<ViewSpecification>();
   IContentDirectory cd = ServiceRegistration.Get<IServerConnectionManager>().ContentDirectory;
   if (cd == null)
     return;
   try
   {
     MediaItemQuery query = new MediaItemQuery(_necessaryMIATypeIds, _optionalMIATypeIds, _filter)
     {
       Limit = Consts.MAX_NUM_ITEMS_VISIBLE
     };
     mediaItems = cd.Search(query, true);
   }
   catch (UPnPRemoteException e)
   {
     ServiceRegistration.Get<ILogger>().Error("SimpleTextSearchViewSpecification.ReLoadItemsAndSubViewSpecifications: Error requesting server", e);
     mediaItems = new List<MediaItem>();
   }
 }
 public override IEnumerable<MediaItem> GetAllMediaItems()
 {
   IContentDirectory cd = ServiceRegistration.Get<IServerConnectionManager>().ContentDirectory;
   if (cd == null)
     return new List<MediaItem>();
   MediaItemQuery query = new MediaItemQuery(
       _necessaryMIATypeIds,
       _optionalMIATypeIds,
       new BooleanCombinationFilter(BooleanOperator.And,
           new IFilter[]
           {
             new RelationalFilter(ProviderResourceAspect.ATTR_SYSTEM_ID, RelationalOperator.EQ, _systemId),
             new LikeFilter(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, SqlUtils.LikeEscape(_basePath.Serialize(), '\\') + "%", '\\', true)
           }));
   return cd.Search(query, false);
 }
Esempio n. 9
0
 public MediaItemQuery(MediaItemQuery other)
 {
   _filter = other.Filter;
   _necessaryRequestedMIATypeIDs = new HashSet<Guid>(other._necessaryRequestedMIATypeIDs);
   _optionalRequestedMIATypeIDs = new HashSet<Guid>(other._optionalRequestedMIATypeIDs);
   _sortInformation = other._sortInformation;
 }
Esempio n. 10
0
    public bool SyncSeries()
    {
      try
      {
        TestStatus = "[Trakt.SyncSeries]";
        Guid[] types = { MediaAspect.ASPECT_ID, SeriesAspect.ASPECT_ID };

        MediaItemQuery mediaItemQuery = new MediaItemQuery(types, null, null);
        var contentDirectory = ServiceRegistration.Get<IServerConnectionManager>().ContentDirectory;
        if (contentDirectory == null)
        {
          TestStatus = "[Trakt.MediaLibraryNotConnected]";
          return false;
        }
        var episodes = contentDirectory.Search(mediaItemQuery, true);

        var series = episodes.ToLookup(GetSeriesKey);
        foreach (var serie in series)
        {
          var imdbId = serie.Select(episode =>
          {
            string value;
            return MediaItemAspect.TryGetAttribute(episode.Aspects, SeriesAspect.ATTR_IMDB_ID, out value) ? value : null;
          }).FirstOrDefault(value => !string.IsNullOrWhiteSpace(value));

          var tvdbId = serie.Select(episode =>
          {
            int value;
            return MediaItemAspect.TryGetAttribute(episode.Aspects, SeriesAspect.ATTR_TVDB_ID, out value) ? value : 0;
          }).FirstOrDefault(value => value != 0);

          TraktEpisodeSync syncData = new TraktEpisodeSync
          {
            UserName = Username,
            Password = Password,
            EpisodeList = new List<TraktEpisodeSync.Episode>(),
            Title = serie.Key,
            Year = serie.Min(e =>
            {
              int year;
              string seriesTitle;
              GetSeriesTitleAndYear(e, out seriesTitle, out year);
              return year;
            }).ToString()
          };

          if (!string.IsNullOrWhiteSpace(imdbId))
            syncData.IMDBID = imdbId;

          if (tvdbId > 0)
            syncData.SeriesID = tvdbId.ToString();

          HashSet<TraktEpisodeSync.Episode> uniqueEpisodes = new HashSet<TraktEpisodeSync.Episode>();
          foreach (var episode in serie)
          {
            string seriesTitle;
            int year = 0;
            if (!GetSeriesTitle /*AndYear*/(episode, out seriesTitle /*, out year*/))
              continue;

            // First send all movies to Trakt that we have so they appear in library
            CollectionUtils.AddAll(uniqueEpisodes, ToSeries(episode));
          }
          syncData.EpisodeList = uniqueEpisodes.ToList();

          TraktSyncModes traktSyncMode = TraktSyncModes.library;
          var response = TraktAPI.SyncEpisodeLibrary(syncData, traktSyncMode);
          ServiceRegistration.Get<ILogger>().Info("Trakt.tv: Series '{0}' '{1}': {2}{3}", syncData.Title, traktSyncMode, response.Message, response.Error);

          // Then send only the watched movies as "seen"
          uniqueEpisodes.Clear();
          foreach (var seenEpisode in episodes.Where(IsWatched))
            CollectionUtils.AddAll(uniqueEpisodes, ToSeries(seenEpisode));
          syncData.EpisodeList = uniqueEpisodes.ToList();

          traktSyncMode = TraktSyncModes.seen;
          response = TraktAPI.SyncEpisodeLibrary(syncData, traktSyncMode);
          ServiceRegistration.Get<ILogger>().Info("Trakt.tv: Series '{0}' '{1}': {2}{3}", syncData.Title, traktSyncMode, response.Message, response.Error);
          return true;
        }
      }
      catch (Exception ex)
      {
        ServiceRegistration.Get<ILogger>().Error("Trakt.tv: Exception while synchronizing media library.", ex);
      }
      return false;
    }
 public IList<MediaItem> Search(MediaItemQuery query, bool onlyOnline)
 {
   CpAction action = GetAction("Search");
   String onlineStateStr = SerializeOnlineState(onlyOnline);
   IList<object> inParameters = new List<object> {query, onlineStateStr};
   IList<object> outParameters = action.InvokeAction(inParameters);
   return (IList<MediaItem>) outParameters[0];
 }
Esempio n. 12
0
 public IList<MediaItem> LoadCustomPlaylist(IList<Guid> mediaItemIds,
     IEnumerable<Guid> necessaryMIATypes, IEnumerable<Guid> optionalMIATypes, uint? offset = null, uint? limit = null)
 {
   IFilter filter = new MediaItemIdFilter(mediaItemIds);
   MediaItemQuery query = new MediaItemQuery(necessaryMIATypes, optionalMIATypes, filter);
   query.Limit = limit;
   query.Offset = offset;
   // Sort media items
   IDictionary<Guid, MediaItem> searchResult = new Dictionary<Guid, MediaItem>();
   foreach (MediaItem item in Search(query, false))
     searchResult[item.MediaItemId] = item;
   IList<MediaItem> result = new List<MediaItem>(searchResult.Count);
   foreach (Guid mediaItemId in mediaItemIds)
   {
     MediaItem item;
     if (searchResult.TryGetValue(mediaItemId, out item))
       result.Add(item);
   }
   return result;
 }
Esempio n. 13
0
 public IList<MediaItem> Search(MediaItemQuery query, bool filterOnlyOnline)
 {
   // We add the provider resource aspect to the necessary aspect types be able to filter online systems
   MediaItemQuery executeQuery = filterOnlyOnline ? new MediaItemQuery(
           query.NecessaryRequestedMIATypeIDs.Union(new Guid[] {ProviderResourceAspect.ASPECT_ID}),
           query.OptionalRequestedMIATypeIDs, AddOnlyOnlineFilter(query.Filter)) : query;
   executeQuery.Limit = query.Limit;
   executeQuery.Offset = query.Offset;
   CompiledMediaItemQuery cmiq = CompiledMediaItemQuery.Compile(_miaManagement, executeQuery);
   IList<MediaItem> items = cmiq.QueryList();
   IList<MediaItem> result = new List<MediaItem>(items.Count);
   if (filterOnlyOnline && !query.NecessaryRequestedMIATypeIDs.Contains(ProviderResourceAspect.ASPECT_ID))
   { // The provider resource aspect was not requested and thus has to be removed from the result items
     foreach (MediaItem item in items)
     {
       item.Aspects.Remove(ProviderResourceAspect.ASPECT_ID);
       result.Add(item);
     }
   }
   else
     result = items;
   return result;
 }
        static UPnPError OnSearch(DvAction action, IList<object> inParams, out IList<object> outParams, CallContext context)
        {
            // In parameters
            var containerId = (string)inParams[0];
            var searchCriteria = inParams[1].ToString();
            var filter = inParams[2].ToString();
            var startingIndex = Convert.ToInt32(inParams[3]);
            var requestedCount = Convert.ToInt32(inParams[4]);
            var sortCriteria = (string)inParams[5];

            // Out parameters
            int numberReturned = 0;
            int totalMatches = 0;
            int containterUpdateId = 0;

            UPnPContentDirectorySearch query = new UPnPContentDirectorySearch();
            StringWriter sw = new StringWriter();
            query.Construct(searchCriteria, sw);
            query.searchCrit();
            PegNode pn = query.GetRoot();

            string xml = ParserHelper.PegNodeToXml(pn, searchCriteria);
            Logger.Debug("MediaServer - Parsed: \"{0}\" to make \"{1}\"", searchCriteria, xml);

            var parentDirectoryId = containerId == "0" ? Guid.Empty : MarshallingHelper.DeserializeGuid(containerId);
            var necessaryMIATypes = new List<Guid> {DirectoryAspect.ASPECT_ID};

            var searchQuery = new MediaItemQuery(necessaryMIATypes, null);
            //searchQuery.Filter

            var searchItems = ServiceRegistration.Get<IMediaLibrary>().Search(searchQuery, true);
            /*
            foreach (var item in browseItems)
            {

            }
            */
            outParams = new List<object>(3) { numberReturned, totalMatches, containterUpdateId };
            return null;
        }
Esempio n. 15
0
    public bool SyncSeries()
    {

      TraktLogger.Info("Series Library Starting Sync");

      // store list of series ids so we can update the episode counts
      // of any series that syncback watched flags
      var seriesToUpdateEpisodeCounts = new HashSet<int>();

      #region Get online data from cache

      #region UnWatched / Watched

      List<TraktCache.EpisodeWatched> traktWatchedEpisodes = null;

      // get all episodes on trakt that are marked as 'unseen'
      var traktUnWatchedEpisodes = TraktCache.GetUnWatchedEpisodesFromTrakt().ToNullableList();
      if (traktUnWatchedEpisodes == null)
      {
        TraktLogger.Error("Error getting tv shows unwatched from trakt.tv server, unwatched and watched sync will be skipped");
      }
      else
      {
        TraktLogger.Info("Found {0} unwatched tv episodes in trakt.tv library", traktUnWatchedEpisodes.Count());

        // now get all episodes on trakt that are marked as 'seen' or 'watched' (this will be cached already when working out unwatched)
        traktWatchedEpisodes = TraktCache.GetWatchedEpisodesFromTrakt().ToNullableList();
        if (traktWatchedEpisodes == null)
        {
          TraktLogger.Error("Error getting tv shows watched from trakt.tv server, watched sync will be skipped");
        }
        else
        {
          TraktLogger.Info("Found {0} watched tv episodes in trakt.tv library", traktWatchedEpisodes.Count());
        }
      }

      #endregion

      #region Collection

      // get all episodes on trakt that are marked as in 'collection'
      var traktCollectedEpisodes = TraktCache.GetCollectedEpisodesFromTrakt().ToNullableList();
      if (traktCollectedEpisodes == null)
      {
        TraktLogger.Error("Error getting tv episode collection from trakt.tv server");
      }
      else
      {
        TraktLogger.Info("Found {0} tv episodes in trakt.tv collection", traktCollectedEpisodes.Count());
      }

      #endregion

      #region Ratings

      #region Episodes

      //var traktRatedEpisodes = TraktCache.GetRatedEpisodesFromTrakt().ToNullableList();
      //if (traktRatedEpisodes == null)
      //{
      //  TraktLogger.Error("Error getting rated episodes from trakt.tv server");
      //}
      //else
      //{
      //  TraktLogger.Info("Found {0} rated tv episodes in trakt.tv library", traktRatedEpisodes.Count());
      //}

      #endregion

      #region Shows

      //var traktRatedShows = TraktCache.GetRatedShowsFromTrakt().ToNullableList();
      //if (traktRatedShows == null)
      //{
      //  TraktLogger.Error("Error getting rated shows from trakt.tv server");
      //}
      //else
      //{
      //  TraktLogger.Info("Found {0} rated tv shows in trakt.tv library", traktRatedShows.Count());
      //}

      #endregion

      #region Seasons

      //var traktRatedSeasons = TraktCache.GetRatedSeasonsFromTrakt().ToNullableList();
      //if (traktRatedSeasons == null)
      //{
      //  TraktLogger.Error("Error getting rated seasons from trakt.tv server");
      //}
      //else
      //{
      //  TraktLogger.Info("Found {0} rated tv seasons in trakt.tv library", traktRatedSeasons.Count());
      //}

      #endregion

      #endregion

      #region Watchlist

      #region Shows

      //var traktWatchlistedShows = TraktCache.GetWatchlistedShowsFromTrakt();
      //if (traktWatchlistedShows == null)
      //{
      //  TraktLogger.Error("Error getting watchlisted shows from trakt.tv server");
      //}
      //else
      //{
      //  TraktLogger.Info("Found {0} watchlisted tv shows in trakt.tv library", traktWatchlistedShows.Count());
      //}

      #endregion

      #region Seasons

      //var traktWatchlistedSeasons = TraktCache.GetWatchlistedSeasonsFromTrakt();
      //if (traktWatchlistedSeasons == null)
      //{
      //  TraktLogger.Error("Error getting watchlisted seasons from trakt.tv server");
      //}
      //else
      //{
      //  TraktLogger.Info("Found {0} watchlisted tv seasons in trakt.tv library", traktWatchlistedSeasons.Count());
      //}

      #endregion

      #region Episodes

      //var traktWatchlistedEpisodes = TraktCache.GetWatchlistedEpisodesFromTrakt();
      //if (traktWatchlistedEpisodes == null)
      //{
      //  TraktLogger.Error("Error getting watchlisted episodes from trakt.tv server");
      //}
      //else
      //{
      //  TraktLogger.Info("Found {0} watchlisted tv episodes in trakt.tv library", traktWatchlistedEpisodes.Count());
      //}

      #endregion

      #endregion

      #endregion

      if (traktCollectedEpisodes != null)
      {
        try
        {
          TestStatus = "[Trakt.SyncSeries]";
          Guid[] types = { MediaAspect.ASPECT_ID, SeriesAspect.ASPECT_ID, VideoAspect.ASPECT_ID, ImporterAspect.ASPECT_ID};
          MediaItemQuery mediaItemQuery = new MediaItemQuery(types, null, null);
          var contentDirectory = ServiceRegistration.Get<IServerConnectionManager>().ContentDirectory;
          if (contentDirectory == null)
          {
            TestStatus = "[Trakt.MediaLibraryNotConnected]";
            return false;
          }

          #region Get data from local database

          var localEpisodes = contentDirectory.Search(mediaItemQuery, true);
          int episodeCount = localEpisodes.Count;

          TraktLogger.Info("Found {0} total episodes in local database", episodeCount);

          // get the episodes that we have watched
          var localWatchedEpisodes = localEpisodes.Where(IsWatched).ToList();

          TraktLogger.Info("Found {0} episodes watched in tvseries database", localWatchedEpisodes.Count);

          #endregion

          #region Add episodes to watched history at trakt.tv
          int showCount = 0;
          int iSyncCounter = 0;
          if (traktWatchedEpisodes != null)
          {
            var syncWatchedShows = GetWatchedShowsForSyncEx(localWatchedEpisodes, traktWatchedEpisodes);

            TraktLogger.Info("Found {0} local tv show(s) with {1} watched episode(s) to add to trakt.tv watched history", syncWatchedShows.Shows.Count, syncWatchedShows.Shows.Sum(sh => sh.Seasons.Sum(se => se.Episodes.Count())));

            showCount = syncWatchedShows.Shows.Count;
            foreach (var show in syncWatchedShows.Shows)
            {
              int showEpisodeCount = show.Seasons.Sum(s => s.Episodes.Count());
              TraktLogger.Info("Adding tv show [{0}/{1}] to trakt.tv episode watched history, Episode Count = '{2}', Show Title = '{3}', Show Year = '{4}', Show TVDb ID = '{5}', Show IMDb ID = '{6}'",
                                  ++iSyncCounter, showCount, showEpisodeCount, show.Title, show.Year.HasValue ? show.Year.ToString() : "<empty>", show.Ids.Tvdb, show.Ids.Imdb ?? "<empty>");

              show.Seasons.ForEach(s => s.Episodes.ForEach(e =>
              {
                TraktLogger.Info("Adding episode to trakt.tv watched history, Title = '{0} - {1}x{2}', Watched At = '{3}'", show.Title, s.Number, e.Number, e.WatchedAt.ToLogString());
              }));

              // only sync one show at a time regardless of batch size in settings
              var pagedShows = new List<TraktSyncShowWatchedEx>();
              pagedShows.Add(show);

              var response = TraktAPI.AddShowsToWatchedHistoryEx(new TraktSyncShowsWatchedEx { Shows = pagedShows });
              TraktLogger.LogTraktResponse<TraktSyncResponse>(response);

              // only add to cache if it was a success
              // note: we don't get back the same object type so makes it hard to figure out what failed
              if (response != null && response.Added != null && response.Added.Episodes == showEpisodeCount)
              {
                // update local cache
                TraktCache.AddEpisodesToWatchHistory(show);
              }
            }
          }
          #endregion

          #region Add episodes to collection at trakt.tv

          if (traktCollectedEpisodes != null)
          {
            var syncCollectedShows = GetCollectedShowsForSyncEx(localEpisodes, traktCollectedEpisodes);

            TraktLogger.Info("Found {0} local tv show(s) with {1} collected episode(s) to add to trakt.tv collection", syncCollectedShows.Shows.Count, syncCollectedShows.Shows.Sum(sh => sh.Seasons.Sum(se => se.Episodes.Count())));

            iSyncCounter = 0;
            showCount = syncCollectedShows.Shows.Count;
            foreach (var show in syncCollectedShows.Shows)
            {
              int showEpisodeCount = show.Seasons.Sum(s => s.Episodes.Count());
              TraktLogger.Info("Adding tv show [{0}/{1}] to trakt.tv episode collection, Episode Count = '{2}', Show Title = '{3}', Show Year = '{4}', Show TVDb ID = '{5}', Show IMDb ID = '{6}'",
                ++iSyncCounter, showCount, showEpisodeCount, show.Title, show.Year.HasValue ? show.Year.ToString() : "<empty>", show.Ids.Tvdb, show.Ids.Imdb ?? "<empty>");

              show.Seasons.ForEach(s => s.Episodes.ForEach(e =>
              {
                TraktLogger.Info("Adding episode to trakt.tv collection, Title = '{0} - {1}x{2}', Collected At = '{3}', Audio Channels = '{4}', Audio Codec = '{5}', Resolution = '{6}', Media Type = '{7}', Is 3D = '{8}'", show.Title, s.Number, e.Number, e.CollectedAt.ToLogString(), e.AudioChannels.ToLogString(), e.AudioCodec.ToLogString(), e.Resolution.ToLogString(), e.MediaType.ToLogString(), e.Is3D);
              }));

              // only sync one show at a time regardless of batch size in settings
              var pagedShows = new List<TraktSyncShowCollectedEx>();
              pagedShows.Add(show);

              var response = TraktAPI.AddShowsToCollectonEx(new TraktSyncShowsCollectedEx { Shows = pagedShows });
              TraktLogger.LogTraktResponse<TraktSyncResponse>(response);

              // only add to cache if it was a success
              if (response != null && response.Added != null && response.Added.Episodes == showEpisodeCount)
              {
                // update local cache
                TraktCache.AddEpisodesToCollection(show);
              }
            }
          }
          #endregion
          return true;
        }
        catch (Exception ex)
        {
          ServiceRegistration.Get<ILogger>().Error("Trakt.tv: Exception while synchronizing media library.", ex);
        }
      }
      return false;
    }