All checks were successful
continuous-integration/drone/push Build is passing
201 lines
6.5 KiB
C#
201 lines
6.5 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using Jellyfin.Data.Enums;
|
|
using MediaBrowser.Controller.Entities;
|
|
using MediaBrowser.Controller.Entities.Audio;
|
|
using MediaBrowser.Controller.Providers;
|
|
using MediaBrowser.Model.Entities;
|
|
using MediaBrowser.Model.Querying;
|
|
using CinemaLib.API;
|
|
|
|
namespace Jellyfin.Plugin.Cinema;
|
|
|
|
/// <summary>
|
|
/// Stream Cinema folder that also implicitly represents a filter (minimally filter by <see cref="ItemType"/>).
|
|
/// </summary>
|
|
public abstract class CinemaFilterFolder : Folder
|
|
{
|
|
/// <summary>
|
|
/// Filtering folders can never be deleted.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public sealed override bool CanDelete()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Type of the collection as reported to the web client so it knows what feaures (mostly filtering ones) to render.
|
|
/// </summary>
|
|
public abstract BaseItemKind ClientType { get; }
|
|
|
|
/// <summary>
|
|
/// Forwards <see cref="ClientType"/>.
|
|
/// </summary>
|
|
public sealed override string GetClientTypeName() => ClientType.ToString();
|
|
|
|
/// <summary>
|
|
/// Stream Cinema item type in this folder. Shall follow <see cref="ClientType"/> as faithfully
|
|
/// as possible. Used for filtering.
|
|
/// </summary>
|
|
public abstract ItemType ItemType { get; }
|
|
|
|
/// <summary>
|
|
/// Information for <see cref="CinemaImageProvider"/> to render icon for us.
|
|
/// </summary>
|
|
internal abstract string ImageName { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the static filter folders.
|
|
/// </summary>
|
|
protected abstract IEnumerable<BaseItem> GetFilterItems();
|
|
|
|
/// <summary>
|
|
/// Tries to create a Jellyfin media item from the provided Cinema media item.
|
|
/// </summary>
|
|
/// <param name="csId">Cinema media identifier.</param>
|
|
/// <param name="media">Cinema metadata.</param>
|
|
/// <param name="parentFolder">Jellyfin parent folder.</param>
|
|
/// <param name="item">On success the created item.</param>
|
|
/// <returns>True on success, false otherwise.</returns>
|
|
/// <remarks>Implementation shall just choose proper container type and call <see cref="CinemaQueryExtensions.TryCreateMediaItem"/></remarks>
|
|
public abstract bool TryCreateMediaItem(MediaSource? media, string csId, BaseItem parentFolder, [NotNullWhen(true)] out BaseItem? item);
|
|
|
|
/// <summary>
|
|
/// Gets static as well as dynamic items from Stream Cinema database.
|
|
/// </summary>
|
|
protected sealed override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
|
|
{
|
|
if ((query.IncludeItemTypes != null && !query.IncludeItemTypes.Contains(ClientType))
|
|
|| (query.ExcludeItemTypes != null && query.ExcludeItemTypes.Contains(ClientType)))
|
|
// None of our items can match
|
|
return new QueryResult<BaseItem>() { Items = new List<BaseItem>() };
|
|
|
|
int offset = query.StartIndex ?? 0;
|
|
int limit = query.Limit ?? 0;
|
|
|
|
List<BaseItem> items = new List<BaseItem>();
|
|
QueryResult<BaseItem> result = new QueryResult<BaseItem>() { Items = items, StartIndex = offset };
|
|
|
|
// Static items
|
|
if (string.IsNullOrEmpty(query.SearchTerm))
|
|
{
|
|
foreach (BaseItem i in GetFilterItems())
|
|
items.Add(i);
|
|
int staticCount = items.Count;
|
|
if (offset <= items.Count)
|
|
{
|
|
items.RemoveRange(0, offset);
|
|
limit -= items.Count;
|
|
offset = 0;
|
|
}
|
|
else
|
|
{
|
|
items.Clear();
|
|
offset -= staticCount;
|
|
}
|
|
}
|
|
|
|
// Filtered content items
|
|
FilterSortBy sortBy;
|
|
ItemOrder sortDir;
|
|
if (query.OrderBy.Count == 0)
|
|
{
|
|
sortBy = FilterSortBy.Title;
|
|
sortDir = ItemOrder.Ascending;
|
|
}
|
|
else
|
|
{
|
|
(ItemSortBy sortByJ, SortOrder sortDirJ) = query.OrderBy.First();
|
|
sortBy = sortByJ.ToCinema();
|
|
sortDir = sortDirJ == SortOrder.Ascending ? ItemOrder.Ascending : ItemOrder.Descending;
|
|
}
|
|
|
|
FilterResponse? filterRes = Metadata.SearchAsync(query.SearchTerm ?? "", order: sortDir, sort: sortBy, type: ItemType, offset: offset, limit: limit).GetAwaiter().GetResult();
|
|
if (filterRes != null && filterRes.hits != null && filterRes.hits.hits != null)
|
|
{
|
|
if (filterRes.hits.total != null)
|
|
result.TotalRecordCount = (int)filterRes.hits.total.value;
|
|
foreach (var i in filterRes.hits.hits)
|
|
{
|
|
if (TryCreateMediaItem(i._source, i._id, this, out BaseItem? a))
|
|
items.Add(a);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an already persisted instance or creates a new instance of the given child filter folder.
|
|
/// </summary>
|
|
/// <typeparam name="T">Type of the folder.</typeparam>
|
|
/// <param name="localizedName">Culture localized name of the folder.</param>
|
|
protected T CreateChildFolder<T>(string localizedName) where T : CinemaFilterFolder, new()
|
|
{
|
|
return CreateFilterFolderInternal<T>(this, localizedName);
|
|
}
|
|
/// <summary>
|
|
/// Gets an already persisted instance or creates a new instance of the given filter folder.
|
|
/// </summary>
|
|
/// <typeparam name="T">Type of the folder.</typeparam>
|
|
/// <param name="parent">Parent folder.</param>
|
|
/// <param name="localizedName">Culture localized name of the folder.</param>
|
|
internal static T CreateRootFilterFolder<T>(string localizedName) where T : CinemaRootFolder, new()
|
|
{
|
|
return CreateFilterFolderInternal<T>(null, localizedName);
|
|
}
|
|
|
|
private static T CreateFilterFolderInternal<T>(CinemaFilterFolder? parent, string localizedName) where T : CinemaFilterFolder, new()
|
|
{
|
|
Guid folderId = CinemaHost.LibraryManager.GetNewItemId("folder", typeof(T));
|
|
string folderPath = GetInternalMetadataPath(CinemaHost.InternalMetadataPath!, folderId);
|
|
Directory.CreateDirectory(folderPath);
|
|
|
|
T? folder = CinemaHost.LibraryManager.GetItemById(folderId) as T;
|
|
bool isNew;
|
|
bool forceUpdate = false;
|
|
if (isNew = folder == null)
|
|
{
|
|
folder = new T
|
|
{
|
|
Id = folderId,
|
|
DateCreated = CinemaHost.FileSystem.GetCreationTimeUtc(folderPath),
|
|
DateModified = CinemaHost.FileSystem.GetLastWriteTimeUtc(folderPath)
|
|
};
|
|
}
|
|
|
|
folder.Id = folderId;
|
|
folder.Name = localizedName;
|
|
folder.Path = folderPath;
|
|
if (parent == null)
|
|
{
|
|
Folder parentItem = LibraryManager.GetUserRootFolder();
|
|
folder.ParentId = parentItem.Id;
|
|
if (!parentItem.Children.Contains(folder))
|
|
parentItem.AddChild(folder);
|
|
}
|
|
else
|
|
{
|
|
folder.ParentId = parent.Id;
|
|
}
|
|
|
|
if (isNew)
|
|
{
|
|
folder.OnMetadataChanged();
|
|
CinemaHost.LibraryManager.CreateItem(folder, parent);
|
|
}
|
|
|
|
folder.RefreshMetadata(
|
|
new MetadataRefreshOptions(new DirectoryService(CinemaHost.FileSystem)) { ForceSave = !isNew && forceUpdate },
|
|
default
|
|
).GetAwaiter().GetResult();
|
|
|
|
return folder;
|
|
}
|
|
|
|
internal static string GetInternalMetadataPath(string basePath, Guid id)
|
|
{
|
|
return System.IO.Path.Combine(basePath, "cinema", id.ToString("N", CultureInfo.InvariantCulture), "metadata");
|
|
}
|
|
} |