Skip to content

Commit

Permalink
feat(emby): ✨ Added a emby recently added sync!
Browse files Browse the repository at this point in the history
  • Loading branch information
tidusjar committed Nov 19, 2021
1 parent d9ee25b commit a0e1406
Show file tree
Hide file tree
Showing 16 changed files with 232 additions and 93 deletions.
47 changes: 41 additions & 6 deletions src/Ombi.Api.Emby/EmbyApi.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Internal;
using Newtonsoft.Json;
using Ombi.Api.Emby.Models;
using Ombi.Api.Emby.Models.Media;
using Ombi.Api.Emby.Models.Media.Tv;
using Ombi.Api.Emby.Models.Movie;
using Ombi.Helpers;
Expand Down Expand Up @@ -124,22 +120,36 @@ public async Task<List<LibraryVirtualFolders>> GetLibraries(string apiKey, strin
return response;
}


public async Task<EmbyItemContainer<EmbyMovie>> GetAllMovies(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri)
{
return await GetAll<EmbyMovie>("Movie", apiKey, userId, baseUri, true, startIndex, count, parentIdFilder);
}

public async Task<EmbyItemContainer<EmbyMovie>> RecentlyAddedMovies(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri)
{
return await RecentlyAdded<EmbyMovie>("Movie", apiKey, userId, baseUri, true, startIndex, count, parentIdFilder);
}

public async Task<EmbyItemContainer<EmbyEpisodes>> GetAllEpisodes(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri)
{
return await GetAll<EmbyEpisodes>("Episode", apiKey, userId, baseUri, false, startIndex, count, parentIdFilder);
}

public async Task<EmbyItemContainer<EmbyEpisodes>> RecentlyAddedEpisodes(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri)
{
return await RecentlyAdded<EmbyEpisodes>("Episode", apiKey, userId, baseUri, false, startIndex, count, parentIdFilder);
}

public async Task<EmbyItemContainer<EmbySeries>> GetAllShows(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri)
{
return await GetAll<EmbySeries>("Series", apiKey, userId, baseUri, false, startIndex, count, parentIdFilder);
}

public async Task<EmbyItemContainer<EmbySeries>> RecentlyAddedShows(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri)
{
return await RecentlyAdded<EmbySeries>("Series", apiKey, userId, baseUri, false, startIndex, count, parentIdFilder);
}

public async Task<SeriesInformation> GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl)
{
return await GetInformation<SeriesInformation>(mediaId, apiKey, userId, baseUrl);
Expand All @@ -154,6 +164,31 @@ public async Task<EpisodeInformation> GetEpisodeInformation(string mediaId, stri
return await GetInformation<EpisodeInformation>(mediaId, apiKey, userId, baseUrl);
}

private async Task<EmbyItemContainer<T>> RecentlyAdded<T>(string type, string apiKey, string userId, string baseUri, bool includeOverview, int startIndex, int count, string parentIdFilder = default)
{
var request = new Request($"emby/users/{userId}/items", baseUri, HttpMethod.Get);

request.AddQueryString("Recursive", true.ToString());
request.AddQueryString("IncludeItemTypes", type);
request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds");
request.AddQueryString("startIndex", startIndex.ToString());
request.AddQueryString("limit", count.ToString());
request.AddQueryString("sortBy", "DateCreated");
request.AddQueryString("SortOrder", "Descending");
if (!string.IsNullOrEmpty(parentIdFilder))
{
request.AddQueryString("ParentId", parentIdFilder);
}

request.AddQueryString("IsVirtualItem", "False");

AddHeaders(request, apiKey);


var obj = await Api.Request<EmbyItemContainer<T>>(request);
return obj;
}

private async Task<T> GetInformation<T>(string mediaId, string apiKey, string userId, string baseUrl)
{
var request = new Request($"emby/users/{userId}/items/{mediaId}", baseUrl, HttpMethod.Get);
Expand Down
3 changes: 3 additions & 0 deletions src/Ombi.Api.Emby/IBaseEmbyApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@ Task<EmbyItemContainer<EmbyMovie>> GetCollection(string mediaId,
Task<MovieInformation> GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<EpisodeInformation> GetEpisodeInformation(string mediaId, string apiKey, string userId, string baseUrl);
Task<PublicInfo> GetPublicInformation(string baseUrl);
Task<EmbyItemContainer<EmbyMovie>> RecentlyAddedMovies(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
Task<EmbyItemContainer<EmbyEpisodes>> RecentlyAddedEpisodes(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
Task<EmbyItemContainer<EmbySeries>> RecentlyAddedShows(string apiKey, string parentIdFilder, int startIndex, int count, string userId, string baseUri);
}
}
1 change: 1 addition & 0 deletions src/Ombi.Helpers/JobDataKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public class JobDataKeys
{
public const string RecentlyAddedSearch = "recentlyAddedSearch";
public const string EmbyRecentlyAddedSearch = nameof(EmbyRecentlyAddedSearch);
public const string NotificationOptions = nameof(NotificationOptions);
}
}
141 changes: 88 additions & 53 deletions src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using Ombi.Api.Emby;
using Ombi.Api.Emby.Models;
using Ombi.Api.Emby.Models.Media.Tv;
using Ombi.Api.Emby.Models.Movie;
using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External;
Expand Down Expand Up @@ -36,24 +38,33 @@ public EmbyContentSync(ISettingsService<EmbySettings> settings, IEmbyApiFactory
private readonly IEmbyContentRepository _repo;
private readonly IHubContext<NotificationHub> _notification;

private const int AmountToTake = 100;

private IEmbyApi Api { get; set; }

public async Task Execute(IJobExecutionContext job)
public async Task Execute(IJobExecutionContext context)
{
JobDataMap dataMap = context.JobDetail.JobDataMap;
var recentlyAddedSearch = false;
if (dataMap.TryGetValue(JobDataKeys.EmbyRecentlyAddedSearch, out var recentlyAddedObj))
{
recentlyAddedSearch = Convert.ToBoolean(recentlyAddedObj);
}

var embySettings = await _settings.GetSettingsAsync();
if (!embySettings.Enable)
return;

Api = _apiFactory.CreateClient(embySettings);

await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
.SendAsync(NotificationHub.NotificationEvent, "Emby Content Sync Started");
.SendAsync(NotificationHub.NotificationEvent, recentlyAddedSearch ? "Emby Recently Added Started" : "Emby Content Sync Started");

foreach (var server in embySettings.Servers)
{
try
{
await StartServerCache(server, embySettings);
await StartServerCache(server, recentlyAddedSearch);
}
catch (Exception e)
{
Expand All @@ -67,11 +78,12 @@ await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
.SendAsync(NotificationHub.NotificationEvent, "Emby Content Sync Finished");
// Episodes

await OmbiQuartz.TriggerJob(nameof(IEmbyEpisodeSync), "Emby");

await OmbiQuartz.Scheduler.TriggerJob(new JobKey(nameof(IEmbyEpisodeSync), "Emby"), new JobDataMap(new Dictionary<string, string> { { JobDataKeys.EmbyRecentlyAddedSearch, "true" } }));
}


private async Task StartServerCache(EmbyServers server, EmbySettings settings)
private async Task StartServerCache(EmbyServers server, bool recentlyAdded)
{
if (!ValidateSettings(server))
return;
Expand All @@ -86,83 +98,89 @@ private async Task StartServerCache(EmbyServers server, EmbySettings settings)
foreach (var movieParentIdFilder in movieLibsToFilter)
{
_logger.LogInformation($"Scanning Lib '{movieParentIdFilder.Title}'");
await ProcessMovies(server, movieParentIdFilder.Key);
await ProcessMovies(server, recentlyAdded, movieParentIdFilder.Key);
}

var tvLibsToFilter = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "tvshows");
foreach (var tvParentIdFilter in tvLibsToFilter)
{
_logger.LogInformation($"Scanning Lib '{tvParentIdFilter.Title}'");
await ProcessTv(server, tvParentIdFilter.Key);
await ProcessTv(server, recentlyAdded, tvParentIdFilter.Key);
}


var mixedLibs = server.EmbySelectedLibraries.Where(x => x.Enabled && x.CollectionType == "mixed");
foreach (var m in mixedLibs)
{
_logger.LogInformation($"Scanning Lib '{m.Title}'");
await ProcessTv(server, m.Key);
await ProcessMovies(server, m.Key);
await ProcessTv(server, recentlyAdded, m.Key);
await ProcessMovies(server, recentlyAdded, m.Key);
}
}
else
{
await ProcessMovies(server);
await ProcessTv(server);
await ProcessMovies(server, recentlyAdded);
await ProcessTv(server, recentlyAdded);
}
}

private async Task ProcessTv(EmbyServers server, string parentId = default)
private async Task ProcessTv(EmbyServers server, bool recentlyAdded, string parentId = default)
{
// TV Time
var mediaToAdd = new HashSet<EmbyContent>();
var tv = await Api.GetAllShows(server.ApiKey, parentId, 0, 200, server.AdministratorId, server.FullUri);
EmbyItemContainer<EmbySeries> tv;
if (recentlyAdded)
{
var recentlyAddedAmountToTake = AmountToTake / 2;
tv = await Api.RecentlyAddedShows(server.ApiKey, parentId, 0, recentlyAddedAmountToTake, server.AdministratorId, server.FullUri);
if (tv.TotalRecordCount > recentlyAddedAmountToTake)
{
tv.TotalRecordCount = recentlyAddedAmountToTake;
}
}
else
{
tv = await Api.GetAllShows(server.ApiKey, parentId, 0, AmountToTake, server.AdministratorId, server.FullUri);
}
var totalTv = tv.TotalRecordCount;
var processed = 1;
while (processed < totalTv)
{
foreach (var tvShow in tv.Items)
{
try
processed++;
if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb))
{
_logger.LogInformation("Provider Id on tv {0} is null", tvShow.Name);
continue;
}

processed++;
if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb))
{
_logger.LogInformation("Provider Id on tv {0} is null", tvShow.Name);
continue;
}

var existingTv = await _repo.GetByEmbyId(tvShow.Id);
if (existingTv == null)
{
_logger.LogDebug("Adding new TV Show {0}", tvShow.Name);
mediaToAdd.Add(new EmbyContent
{
TvDbId = tvShow.ProviderIds?.Tvdb,
ImdbId = tvShow.ProviderIds?.Imdb,
TheMovieDbId = tvShow.ProviderIds?.Tmdb,
Title = tvShow.Name,
Type = EmbyMediaType.Series,
EmbyId = tvShow.Id,
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname),
AddedAt = DateTime.UtcNow
});
}
else
var existingTv = await _repo.GetByEmbyId(tvShow.Id);
if (existingTv == null)
{
_logger.LogDebug("Adding new TV Show {0}", tvShow.Name);
mediaToAdd.Add(new EmbyContent
{
_logger.LogDebug("We already have TV Show {0}", tvShow.Name);
}

TvDbId = tvShow.ProviderIds?.Tvdb,
ImdbId = tvShow.ProviderIds?.Imdb,
TheMovieDbId = tvShow.ProviderIds?.Tmdb,
Title = tvShow.Name,
Type = EmbyMediaType.Series,
EmbyId = tvShow.Id,
Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id, server?.ServerId, server.ServerHostname),
AddedAt = DateTime.UtcNow
});
}
catch (Exception)
else
{

throw;
_logger.LogDebug("We already have TV Show {0}", tvShow.Name);
}
}
// Get the next batch
tv = await Api.GetAllShows(server.ApiKey, parentId, processed, 200, server.AdministratorId, server.FullUri);
if (!recentlyAdded)
{
tv = await Api.GetAllShows(server.ApiKey, parentId, processed, AmountToTake, server.AdministratorId, server.FullUri);
}
await _repo.AddRange(mediaToAdd);
mediaToAdd.Clear();
}
Expand All @@ -171,9 +189,23 @@ private async Task ProcessTv(EmbyServers server, string parentId = default)
await _repo.AddRange(mediaToAdd);
}

private async Task ProcessMovies(EmbyServers server, string parentId = default)
private async Task ProcessMovies(EmbyServers server, bool recentlyAdded, string parentId = default)
{
var movies = await Api.GetAllMovies(server.ApiKey, parentId, 0, 200, server.AdministratorId, server.FullUri);
EmbyItemContainer<EmbyMovie> movies;
if (recentlyAdded)
{
var recentlyAddedAmountToTake = AmountToTake / 2;
movies = await Api.RecentlyAddedMovies(server.ApiKey, parentId, 0, recentlyAddedAmountToTake, server.AdministratorId, server.FullUri);
// Setting this so we don't attempt to grab more than we need
if (movies.TotalRecordCount > recentlyAddedAmountToTake)
{
movies.TotalRecordCount = recentlyAddedAmountToTake;
}
}
else
{
movies = await Api.GetAllMovies(server.ApiKey, parentId, 0, AmountToTake, server.AdministratorId, server.FullUri);
}
var totalCount = movies.TotalRecordCount;
var processed = 1;
var mediaToAdd = new HashSet<EmbyContent>();
Expand All @@ -189,22 +221,25 @@ private async Task ProcessMovies(EmbyServers server, string parentId = default)
{
await ProcessMovies(item, mediaToAdd, server);
}

processed++;
}
else
{
processed++;
// Regular movie
await ProcessMovies(movie, mediaToAdd, server);
}

processed++;
}

// Get the next batch
movies = await Api.GetAllMovies(server.ApiKey, parentId, processed, 200, server.AdministratorId, server.FullUri);
// Recently Added should never be checked as the TotalRecords should equal the amount to take
if (!recentlyAdded)
{
movies = await Api.GetAllMovies(server.ApiKey, parentId, processed, AmountToTake, server.AdministratorId, server.FullUri);
}
else
await _repo.AddRange(mediaToAdd);
mediaToAdd.Clear();

}
}

Expand Down
Loading

2 comments on commit a0e1406

@horstepipe
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like the recently added sync totally replaces the full sync, but both should be in place like it is being done for Plex.
There doesn't seem to be a way doing a full sync now.

@tidusjar
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@horstepipe Nope it does not replace it. The Recently added sync now runs by default every hour, and the full sync now runs once a day.

Please # to comment.