UserData bound to ExternalId, Resume play seems still not working. Rename music to concerts.
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2024-12-07 04:07:06 +01:00
parent 1bf5fc675e
commit f973f23443
16 changed files with 194 additions and 143 deletions

1
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1 @@
{}

View File

@@ -11,13 +11,13 @@ public sealed class CinemaConcertFolder : CinemaRootFolder
{ {
} }
public override CollectionType? CollectionType => Data.Enums.CollectionType.music; public override CollectionType? CollectionType => Data.Enums.CollectionType.musicvideos;
public override BaseItemKind ClientType => BaseItemKind.MusicAlbum; public override BaseItemKind ClientType => BaseItemKind.MusicVideo;
public override ItemType ItemType => ItemType.Concert; public override ItemType ItemType => ItemType.Concert;
internal override string ImageName => "music.png"; internal override string ImageName => "concert.png";
protected override IEnumerable<BaseItem> GetFilterItems() protected override IEnumerable<BaseItem> GetFilterItems()
{ {
@@ -28,6 +28,6 @@ public sealed class CinemaConcertFolder : CinemaRootFolder
public override bool TryCreateMediaItem(MediaSource? media, string csId, BaseItem parentFolder, [NotNullWhen(true)] out BaseItem? item) public override bool TryCreateMediaItem(MediaSource? media, string csId, BaseItem parentFolder, [NotNullWhen(true)] out BaseItem? item)
{ {
return media.TryCreateMediaItem<CinemaMusicAlbum>(csId, parentFolder, true, out item); return media.TryCreateMediaItem<Folder>(csId, parentFolder, true, out item);
} }
} }

View File

@@ -12,6 +12,11 @@ public class CinemaEpisode : Episode
{ {
public sealed override string GetClientTypeName() => BaseItemKind.Episode.ToString(); public sealed override string GetClientTypeName() => BaseItemKind.Episode.ToString();
public override List<string> GetUserDataKeys()
{
return new List<string>() { ExternalId };
}
protected override IEnumerable<(BaseItem Item, MediaSourceType MediaSourceType)> GetAllItemsForMediaSources() protected override IEnumerable<(BaseItem Item, MediaSourceType MediaSourceType)> GetAllItemsForMediaSources()
{ {
var result = this.VideoGetAllItemsForMediaSources(); var result = this.VideoGetAllItemsForMediaSources();

View File

@@ -172,9 +172,6 @@ public abstract class CinemaFilterFolder : Folder
{ {
Folder parentItem = LibraryManager.GetUserRootFolder(); Folder parentItem = LibraryManager.GetUserRootFolder();
folder.ParentId = parentItem.Id; folder.ParentId = parentItem.Id;
//folder.IsRoot = true;
//if (!CinemaHost.LibraryManager.RootFolder.VirtualChildren.Contains(folder))
// CinemaHost.LibraryManager.RootFolder.AddVirtualChild(folder);
if (!parentItem.Children.Contains(folder)) if (!parentItem.Children.Contains(folder))
parentItem.AddChild(folder); parentItem.AddChild(folder);
} }

View File

@@ -86,8 +86,8 @@ sealed class CinemaHost : IHostedService
.Hide = config.HideSeriesFolder; .Hide = config.HideSeriesFolder;
CinemaFilterFolder.CreateRootFilterFolder<CinemaAnimeFolder>(string.IsNullOrWhiteSpace(config.AnimeFolderName) ? "Anime" : config.AnimeFolderName) CinemaFilterFolder.CreateRootFilterFolder<CinemaAnimeFolder>(string.IsNullOrWhiteSpace(config.AnimeFolderName) ? "Anime" : config.AnimeFolderName)
.Hide = config.HideAnimeFolder; .Hide = config.HideAnimeFolder;
CinemaFilterFolder.CreateRootFilterFolder<CinemaConcertFolder>(string.IsNullOrWhiteSpace(config.MusicFolderName) ? "Music" : config.MusicFolderName) CinemaFilterFolder.CreateRootFilterFolder<CinemaConcertFolder>(string.IsNullOrWhiteSpace(config.ConcertFolderName) ? "Concerts" : config.ConcertFolderName)
.Hide = config.HideMusicFolder; .Hide = config.HideConcertFolder;
} }
private void EnsureWebshareSession(CinemaPluginConfiguration config) private void EnsureWebshareSession(CinemaPluginConfiguration config)

View File

@@ -1,3 +1,4 @@
using CinemaLib.API;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
@@ -5,6 +6,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Sorting;
@@ -19,8 +21,9 @@ namespace Jellyfin.Plugin.Cinema;
sealed class CinemaLibraryManager : ILibraryManager sealed class CinemaLibraryManager : ILibraryManager
{ {
private readonly ILibraryManager _inner; private readonly ILibraryManager _inner;
private readonly IUserDataRepository _userData;
public CinemaLibraryManager(CinemaInnerLibraryManager innerLibraryManager, IServiceProvider svc) public CinemaLibraryManager(CinemaInnerLibraryManager innerLibraryManager, IUserDataRepository userData, IServiceProvider svc)
{ {
if (innerLibraryManager == null || svc == null) if (innerLibraryManager == null || svc == null)
throw new ArgumentNullException(); throw new ArgumentNullException();
@@ -29,7 +32,7 @@ sealed class CinemaLibraryManager : ILibraryManager
if (inner == null) if (inner == null)
throw new InvalidOperationException("Original LibraryManager service not found."); throw new InvalidOperationException("Original LibraryManager service not found.");
this._inner = inner; this._inner = inner;
this._userData = userData;
} }
#region ILibraryManager Members #region ILibraryManager Members
@@ -265,20 +268,46 @@ sealed class CinemaLibraryManager : ILibraryManager
public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query) public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query)
{ {
QueryResult<BaseItem> result = _inner.GetItemsResult(query); QueryResult<BaseItem> result = _inner.GetItemsResult(query);
if (query.ParentId != Guid.Empty || query.OrderBy.FirstOrDefault().OrderBy == ItemSortBy.DatePlayed) if (query.ParentId != Guid.Empty)
// Not a search at the root so do not involve our root folders
return result; return result;
List<BaseItem> resultL = new List<BaseItem>(result.Items);
List<BaseItem> a = new List<BaseItem>(result.Items); if (query.OrderBy.FirstOrDefault().OrderBy == ItemSortBy.DatePlayed && query.User != null)
foreach (BaseItem i in GetUserRootFolder().Children) {
if (i is CinemaRootFolder root) // Get Resume play items
// PERF: This may quickly become very slow
var resumePlay = _userData.GetAllUserData(query.User.InternalId)
.OrderByDescending(x => x.Played)
.Skip(query.StartIndex ?? 0)
.Take(query.Limit ?? 20);
foreach (var i in resumePlay)
{ {
var b = root.GetItemList(query); // Note: All Cinema items override GetUserDataKeys and return ExternalId
if (b != null) string? csId;
a.AddRange(b); if (!CinemaQueryExtensions.TryGetCinemaIdFromExternalId(i.Key, out csId))
} continue;
return new QueryResult<BaseItem>() { Items = a }; MediaSource? ms = Metadata.DetailAsync(csId, default).GetAwaiter().GetResult();
if (CinemaQueryExtensions.TryCreateMediaItem<Folder>(ms, csId, null, false, out BaseItem? a))
resultL.Add(a);
}
}
else
{
// Add our root folders to the search
foreach (BaseItem i in GetUserRootFolder().Children)
// Prevent duplicates as content in Anime is also elsewhere
if (i is CinemaRootFolder root && i is not CinemaAnimeFolder)
{
var b = root.GetItemList(query);
if (b != null)
resultL.AddRange(b);
}
}
return new QueryResult<BaseItem>() { Items = resultL };
} }
public LibraryOptions GetLibraryOptions(BaseItem item) public LibraryOptions GetLibraryOptions(BaseItem item)

View File

@@ -12,6 +12,11 @@ public class CinemaMovie : Movie
{ {
public sealed override string GetClientTypeName() => BaseItemKind.Movie.ToString(); public sealed override string GetClientTypeName() => BaseItemKind.Movie.ToString();
public override List<string> GetUserDataKeys()
{
return new List<string>() { ExternalId };
}
protected override IEnumerable<(BaseItem Item, MediaSourceType MediaSourceType)> GetAllItemsForMediaSources() protected override IEnumerable<(BaseItem Item, MediaSourceType MediaSourceType)> GetAllItemsForMediaSources()
{ {
var result = this.VideoGetAllItemsForMediaSources(); var result = this.VideoGetAllItemsForMediaSources();

View File

@@ -1,68 +0,0 @@
using CinemaLib.API;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Model.Querying;
namespace Jellyfin.Plugin.Cinema;
/// <summary>
/// Music album folder item from Cinema.
/// </summary>
public sealed class CinemaMusicAlbum : MusicAlbum
{
public sealed override string GetClientTypeName() => BaseItemKind.Series.ToString();
public override int GetChildCount(User user)
{
return base.GetChildCount(user);
}
public override int GetRecursiveChildCount(User user)
{
return base.GetRecursiveChildCount(user);
}
public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
{
return (List<BaseItem>)GetItemsInternal(query).Items;
}
protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
{
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 };
FilterSortBy sortBy;
ItemOrder sortDir;
if (query.OrderBy.Count == 0)
{
sortBy = FilterSortBy.Episode;
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.ChildrenAsync(query.SearchTerm ?? "", order: sortDir, sort: sortBy, 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 (i._source.TryCreateMediaItem<Folder>(i._id, this, false, out BaseItem? a))
items.Add(a);
}
}
return result;
}
}

View File

@@ -0,0 +1,36 @@
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
namespace Jellyfin.Plugin.Cinema;
/// <summary>
/// Concerts folder item from Cinema.
/// </summary>
public sealed class CinemaMusicVideo : MusicVideo
{
public sealed override string GetClientTypeName() => BaseItemKind.MusicVideo.ToString();
public override List<string> GetUserDataKeys()
{
return new List<string>() { ExternalId };
}
protected override IEnumerable<(BaseItem Item, MediaSourceType MediaSourceType)> GetAllItemsForMediaSources()
{
var result = this.VideoGetAllItemsForMediaSources();
if (result == null)
return base.GetAllItemsForMediaSources();
else
return result;
}
public override List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{
var result = this.VideoGetMediaSources(enablePathSubstitution);
if (result == null)
return base.GetMediaSources(enablePathSubstitution);
else
return result;
}
}

View File

@@ -61,7 +61,7 @@ static class CinemaQueryExtensions
/// <param name="parentFolder">Jellyfin parent folder.</param> /// <param name="parentFolder">Jellyfin parent folder.</param>
/// <param name="item">On success the created item.</param> /// <param name="item">On success the created item.</param>
/// <returns>True on success, false otherwise.</returns> /// <returns>True on success, false otherwise.</returns>
public static bool TryCreateMediaItem<TContainerType>(this MediaSource? media, string csId, BaseItem parentFolder, bool allowContainer, [NotNullWhen(true)] out BaseItem? item) public static bool TryCreateMediaItem<TContainerType>(this MediaSource? media, string csId, BaseItem? parentFolder, bool allowContainer, [NotNullWhen(true)] out BaseItem? item)
where TContainerType : Folder, new() where TContainerType : Folder, new()
{ {
if (media == null) if (media == null)
@@ -70,12 +70,10 @@ static class CinemaQueryExtensions
return false; return false;
} }
var parentFolderId = parentFolder.Id;
bool isNew; bool isNew;
bool forceUpdate = false; bool forceUpdate = false;
bool isAudio = media.is_concert ?? false; bool isConcert = media.is_concert ?? false;
if (media.children_count != 0) if (media.children_count != 0)
{ {
// Container // Container
@@ -87,10 +85,9 @@ static class CinemaQueryExtensions
item = GetMediaItemById<TContainerType>(csId, null, out isNew); item = GetMediaItemById<TContainerType>(csId, null, out isNew);
} }
else if (isAudio) else if (isConcert)
{ {
// TODO determine if this is an AudioBook od Audio item = GetMediaItemById<CinemaMusicVideo>(csId, null, out isNew);
item = GetMediaItemById<MediaBrowser.Controller.Entities.Audio.Audio>(csId, null, out isNew);
} }
else if (media.info_labels?.mediatype == "episode") else if (media.info_labels?.mediatype == "episode")
@@ -164,7 +161,8 @@ static class CinemaQueryExtensions
hasAlbumArtists.AlbumArtists = media.info_labels?.director; hasAlbumArtists.AlbumArtists = media.info_labels?.director;
} }
item.ParentId = parentFolderId; if (parentFolder != null)
item.ParentId = parentFolder.Id;
/* /*
if (item is IHasSeries hasSeries) if (item is IHasSeries hasSeries)

View File

@@ -15,10 +15,16 @@ public sealed class CinemaSeason : Season, ICinemaChildrenCount
{ {
private CinemaSubfolderHelper _helper; private CinemaSubfolderHelper _helper;
public CinemaSeason() { public CinemaSeason()
{
this._helper = new CinemaSubfolderHelper(this, FilterSortBy.Episode); this._helper = new CinemaSubfolderHelper(this, FilterSortBy.Episode);
} }
public override List<string> GetUserDataKeys()
{
return new List<string>() { ExternalId };
}
#region ICinemaChildrenCountMembers #region ICinemaChildrenCountMembers
public int ChildrenCount public int ChildrenCount
@@ -27,9 +33,10 @@ public sealed class CinemaSeason : Season, ICinemaChildrenCount
set => _helper.ChildrenCount = value; set => _helper.ChildrenCount = value;
} }
public int TotalChildrenCount { public int TotalChildrenCount
get => _helper.TotalChildrenCount; {
set => _helper.TotalChildrenCount = value; get => _helper.TotalChildrenCount;
set => _helper.TotalChildrenCount = value;
} }
#endregion #endregion

View File

@@ -15,10 +15,16 @@ public sealed class CinemaTvSeries : Series, ICinemaChildrenCount
{ {
private CinemaSubfolderHelper _helper; private CinemaSubfolderHelper _helper;
public CinemaTvSeries() { public CinemaTvSeries()
{
this._helper = new CinemaSubfolderHelper(this, FilterSortBy.Episode); this._helper = new CinemaSubfolderHelper(this, FilterSortBy.Episode);
} }
public override List<string> GetUserDataKeys()
{
return new List<string>() { ExternalId };
}
#region ICinemaChildrenCountMembers #region ICinemaChildrenCountMembers
public int ChildrenCount public int ChildrenCount
@@ -27,9 +33,10 @@ public sealed class CinemaTvSeries : Series, ICinemaChildrenCount
set => _helper.ChildrenCount = value; set => _helper.ChildrenCount = value;
} }
public int TotalChildrenCount { public int TotalChildrenCount
get => _helper.TotalChildrenCount; {
set => _helper.TotalChildrenCount = value; get => _helper.TotalChildrenCount;
set => _helper.TotalChildrenCount = value;
} }
#endregion #endregion
@@ -56,7 +63,7 @@ public sealed class CinemaTvSeries : Series, ICinemaChildrenCount
/// Let the hack in <see cref="CinemaLibraryManager"/> find us. /// Let the hack in <see cref="CinemaLibraryManager"/> find us.
/// </summary> /// </summary>
public override string CreatePresentationUniqueKey() => Id.ToString("N", CultureInfo.InvariantCulture); public override string CreatePresentationUniqueKey() => Id.ToString("N", CultureInfo.InvariantCulture);
public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query) public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
{ {
return (List<BaseItem>)GetItemsInternal(query).Items; return (List<BaseItem>)GetItemsInternal(query).Items;
@@ -68,7 +75,8 @@ public sealed class CinemaTvSeries : Series, ICinemaChildrenCount
// Set series identifier on all seasons // Set series identifier on all seasons
foreach (var i in result.Items) foreach (var i in result.Items)
if (i is CinemaSeason season) { if (i is CinemaSeason season)
{
season.SeriesId = Id; season.SeriesId = Id;
season.SeriesName = Name; season.SeriesName = Name;
} }

View File

@@ -20,8 +20,8 @@ public class CinemaPluginConfiguration : BasePluginConfiguration
public string AnimeFolderName { get; set; } public string AnimeFolderName { get; set; }
public bool HideAnimeFolder { get; set; } public bool HideAnimeFolder { get; set; }
public string MusicFolderName { get; set; } public string ConcertFolderName { get; set; }
public bool HideMusicFolder { get; set; } public bool HideConcertFolder { get; set; }
public string? WebshareUser { get; set; } public string? WebshareUser { get; set; }
public string? WebsharePassword { get; set; } public string? WebsharePassword { get; set; }

View File

@@ -40,12 +40,12 @@
<br /> <br />
<div class="inputContainer"> <div class="inputContainer">
<input is="emby-input" type="text" id="musicFolderName" label="Music Folder Name" /> <input is="emby-input" type="text" id="concertFolderName" label="Concerts Folder Name" />
<div class="fieldDescription">Custom name for the Cinema Music root folder.</div> <div class="fieldDescription">Custom name for the Cinema Concerts root folder.</div>
</div> </div>
<label class="checkboxContainer"> <label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" id="hideMusicFolder" /> <input is="emby-checkbox" type="checkbox" id="hideConcertFolder" />
<span>Hide the Cinema Music root folder.</span> <span>Hide the Cinema Concerts root folder.</span>
</label> </label>
<div class="verticalSection verticalSection-extrabottompadding"> <div class="verticalSection verticalSection-extrabottompadding">
<h2>Webshare Account</h2> <h2>Webshare Account</h2>
@@ -101,14 +101,14 @@
})); }));
document.querySelector('#hideAnimeFolder').checked = config.HideAnimeFolder; document.querySelector('#hideAnimeFolder').checked = config.HideAnimeFolder;
var musicFolderName = document.querySelector('#musicFolderName'); var concertFolderName = document.querySelector('#concertFolderName');
if (config.MusicFolderName) if (config.ConcertFolderName)
musicFolderName.value = config.MusicFolderName; concertFolderName.value = config.ConcertFolderName;
musicFolderName.dispatchEvent(new Event('change', { concertFolderName.dispatchEvent(new Event('change', {
bubbles: true, bubbles: true,
cancelable: false cancelable: false
})); }));
document.querySelector('#hideMusicFolder').checked = config.HideMusicFolder; document.querySelector('#hideConcertFolder').checked = config.HideConcertFolder;
var webshareUser = document.querySelector('#webshareUser'); var webshareUser = document.querySelector('#webshareUser');
if (config.WebshareUser) if (config.WebshareUser)
@@ -140,8 +140,8 @@
config.HideSeriesFolder = document.querySelector('#hideSeriesFolder').checked; config.HideSeriesFolder = document.querySelector('#hideSeriesFolder').checked;
config.AnimeFolderName = document.querySelector('#animeFolderName').value; config.AnimeFolderName = document.querySelector('#animeFolderName').value;
config.HideAnimeFolder = document.querySelector('#hideAnimeFolder').checked; config.HideAnimeFolder = document.querySelector('#hideAnimeFolder').checked;
config.MusicFolderName = document.querySelector('#musicFolderName').value; config.ConcertFolderName = document.querySelector('#concertFolderName').value;
config.HideMusicFolder = document.querySelector('#hideMusicFolder').checked; config.HideConcertFolder = document.querySelector('#hideConcertFolder').checked;
config.WebshareUser = document.querySelector('#webshareUser').value; config.WebshareUser = document.querySelector('#webshareUser').value;
config.WebsharePassword = document.querySelector('#websharePassword').value; config.WebsharePassword = document.querySelector('#websharePassword').value;

View File

@@ -37,22 +37,27 @@ public class Metadata
if (limit == 0 || limit > MaxPageLimit) if (limit == 0 || limit > MaxPageLimit)
limit = MaxPageLimit; limit = MaxPageLimit;
bool noSearchTerms = expression.Trim().Length == 0; bool noSearchTerms = expression.Trim().Length == 0;
string filterName; string filterName;
string sortS = ToString(sort); string sortS = ToString(sort);
if (sort == FilterSortBy.Title) { if (sort == FilterSortBy.Title)
filterName = "startsWithSimple"; {
sortS = ""; filterName = "startsWithSimple";
} else if (noSearchTerms) { sortS = "";
filterName = "all"; }
} else { else if (noSearchTerms)
filterName = "search"; {
} filterName = "all";
}
else
{
filterName = "search";
}
UriBuilder uri = new UriBuilder(new Uri(ApiFilter, filterName)); UriBuilder uri = new UriBuilder(new Uri(ApiFilter, filterName));
string orderS = order == ItemOrder.Ascending ? "asc" : "desc"; string orderS = order == ItemOrder.Ascending ? "asc" : "desc";
uri.Query = $"?access_token={AccessToken}&value={Uri.EscapeDataString(expression)}&type={ToString(type)}&from={offset.ToString()}&size={limit.ToString()}" uri.Query = $"?access_token={AccessToken}&value={Uri.EscapeDataString(expression)}&type={ToString(type)}&from={offset.ToString()}&size={limit.ToString()}"
+ (sortS.Length != 0 ? $"&order={ToString(order)}&sort={sortS}" : ""); + (sortS.Length != 0 ? $"&order={ToString(order)}&sort={sortS}" : "");
FilterResponse? result = await Program._http.GetFromJsonAsync<FilterResponse>(uri.Uri, CreateFilterJsonOptions(), cancel); FilterResponse? result = await Program._http.GetFromJsonAsync<FilterResponse>(uri.Uri, CreateFilterJsonOptions(), cancel);
if (result != null) if (result != null)
@@ -81,6 +86,24 @@ public class Metadata
return result; return result;
} }
/// <summary>
/// Gets detail info about in item.
/// </summary>
/// <param name="id">Item identifier.</param>
/// <param name="cancel">Asynchronous cancellation.</param>
/// <returns>Media info.</returns>
public async static Task<MediaSource?> DetailAsync(string id, CancellationToken cancel)
{
if (id == null || id.Length == 0)
throw new ArgumentNullException();
Uri uri = new Uri(new Uri(ApiRoot, "media/"), id);
MediaSource? result = await Program._http.GetFromJsonAsync<MediaSource>(uri, CreateFilterJsonOptions(), cancel);
if (result != null)
result = FixDetailResponse(result);
return result;
}
/// <summary> /// <summary>
/// Gets available streams for the given media. /// Gets available streams for the given media.
/// </summary> /// </summary>
@@ -160,17 +183,26 @@ public class Metadata
if (i._source == null) if (i._source == null)
continue; continue;
if (i._source.cast != null) FixDetailResponse(i._source);
foreach (Cast j in i._source.cast) }
j.thumbnail = FixImageUrl(j.thumbnail);
if (i._source.i18n_info_labels != null) return res;
foreach (InfoLabelI18n j in i._source.i18n_info_labels) { }
if (j.art != null) {
j.art.poster = FixImageUrl(j.art.poster); private static MediaSource FixDetailResponse(MediaSource res)
j.art.fanart = FixImageUrl(j.art.fanart); {
} if (res.cast != null)
} foreach (Cast j in res.cast)
j.thumbnail = FixImageUrl(j.thumbnail);
if (res.i18n_info_labels != null)
foreach (InfoLabelI18n j in res.i18n_info_labels)
{
if (j.art != null)
{
j.art.poster = FixImageUrl(j.art.poster);
j.art.fanart = FixImageUrl(j.art.fanart);
}
} }
return res; return res;
@@ -178,7 +210,8 @@ public class Metadata
private static string FixImageUrl(string url) private static string FixImageUrl(string url)
{ {
if (url != null && !url.StartsWith("http")) { if (url != null && !url.StartsWith("http"))
{
if (url.StartsWith("//")) if (url.StartsWith("//"))
url = "https:" + url; url = "https:" + url;
else else