mirror of https://github.com/jellyfin/jellyfin.git
Compare commits
6 Commits
fc406189b6
...
2b6eef4cec
Author | SHA1 | Date |
---|---|---|
Francesco Bellini | 2b6eef4cec | |
renovate[bot] | 5612cb8178 | |
renovate[bot] | ccd06bc547 | |
Bond-009 | d29b85a134 | |
doc-code | 4b122facd4 | |
doc-code | 687753c516 |
|
@ -20,7 +20,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
|
||||
with:
|
||||
|
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
permissions: read-all
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
|
@ -39,7 +39,7 @@ jobs:
|
|||
permissions: read-all
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
|
@ -78,12 +78,12 @@ jobs:
|
|||
- openapi-base
|
||||
steps:
|
||||
- name: Download openapi-head
|
||||
uses: actions/download-artifact@9c19ed7fe5d278cd354c7dfd5d3b88589c7e2395 # v4.1.6
|
||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
||||
with:
|
||||
name: openapi-head
|
||||
path: openapi-head
|
||||
- name: Download openapi-base
|
||||
uses: actions/download-artifact@9c19ed7fe5d278cd354c7dfd5d3b88589c7e2395 # v4.1.6
|
||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
||||
with:
|
||||
name: openapi-base
|
||||
path: openapi-base
|
||||
|
@ -152,7 +152,7 @@ jobs:
|
|||
run: |-
|
||||
echo "JELLYFIN_VERSION=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV
|
||||
- name: Download openapi-head
|
||||
uses: actions/download-artifact@9c19ed7fe5d278cd354c7dfd5d3b88589c7e2395 # v4.1.6
|
||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
||||
with:
|
||||
name: openapi-head
|
||||
path: openapi-head
|
||||
|
|
|
@ -19,7 +19,7 @@ jobs:
|
|||
|
||||
runs-on: "${{ matrix.os }}"
|
||||
steps:
|
||||
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
|
||||
- uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
|
||||
with:
|
||||
|
|
|
@ -24,7 +24,7 @@ jobs:
|
|||
reactions: '+1'
|
||||
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
with:
|
||||
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
@ -51,7 +51,7 @@ jobs:
|
|||
reactions: eyes
|
||||
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
with:
|
||||
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
@ -128,7 +128,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: pull in script
|
||||
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
with:
|
||||
repository: jellyfin/jellyfin-triage-script
|
||||
- name: install python
|
||||
|
|
|
@ -10,7 +10,7 @@ jobs:
|
|||
issues: write
|
||||
steps:
|
||||
- name: pull in script
|
||||
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
with:
|
||||
repository: jellyfin/jellyfin-triage-script
|
||||
- name: install python
|
||||
|
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
yq-version: v4.9.8
|
||||
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
with:
|
||||
ref: ${{ env.TAG_BRANCH }}
|
||||
|
||||
|
@ -66,7 +66,7 @@ jobs:
|
|||
NEXT_VERSION: ${{ github.event.inputs.NEXT_VERSION }}
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
|
||||
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
with:
|
||||
ref: ${{ env.TAG_BRANCH }}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<PackageVersion Include="BlurHashSharp" Version="1.3.2" />
|
||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
|
||||
<PackageVersion Include="Diacritics" Version="3.3.27" />
|
||||
<PackageVersion Include="Diacritics" Version="3.3.29" />
|
||||
<PackageVersion Include="DiscUtils.Udf" Version="0.16.13" />
|
||||
<PackageVersion Include="DotNet.Glob" Version="3.1.3" />
|
||||
<PackageVersion Include="EFCoreSecondLevelCacheInterceptor" Version="4.4.3" />
|
||||
|
|
|
@ -9,6 +9,7 @@ using MediaBrowser.Common.Configuration;
|
|||
using MediaBrowser.Controller.Collections;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
|
@ -210,6 +211,7 @@ namespace Emby.Server.Implementations.Collections
|
|||
List<BaseItem>? itemList = null;
|
||||
|
||||
var linkedChildrenList = collection.GetLinkedChildren();
|
||||
var itemListForRunTimeTicks = collection.GetLinkedChildren();
|
||||
var currentLinkedChildrenIds = linkedChildrenList.Select(i => i.Id).ToList();
|
||||
|
||||
foreach (var id in ids)
|
||||
|
@ -226,6 +228,8 @@ namespace Emby.Server.Implementations.Collections
|
|||
(itemList ??= new()).Add(item);
|
||||
|
||||
linkedChildrenList.Add(item);
|
||||
|
||||
GetItemsForRunTimeTicksCount(item).ToList<BaseItem>().ForEach((i) => itemListForRunTimeTicks.Add(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,6 +247,8 @@ namespace Emby.Server.Implementations.Collections
|
|||
collection.LinkedChildren = newChildren;
|
||||
collection.UpdateRatingToItems(linkedChildrenList);
|
||||
|
||||
collection.UpdateRunTimeTicksToItems(itemListForRunTimeTicks);
|
||||
|
||||
await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
refreshOptions.ForceSave = true;
|
||||
|
@ -291,6 +297,30 @@ namespace Emby.Server.Implementations.Collections
|
|||
collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray();
|
||||
}
|
||||
|
||||
var actualItems = new List<BaseItem>();
|
||||
|
||||
foreach (var child in collection.LinkedChildren)
|
||||
{
|
||||
if (child.ItemId.HasValue)
|
||||
{
|
||||
var item = _libraryManager.GetItemById((Guid)child.ItemId);
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
GetItemsForRunTimeTicksCount(item).ToList<BaseItem>().ForEach((i) => actualItems.Add(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actualItems.Count > 0)
|
||||
{
|
||||
collection.UpdateRunTimeTicksToItems(actualItems);
|
||||
}
|
||||
else
|
||||
{
|
||||
collection.RunTimeTicks = 0;
|
||||
}
|
||||
|
||||
await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
_providerManager.QueueRefresh(
|
||||
collection.Id,
|
||||
|
@ -363,5 +393,41 @@ namespace Emby.Server.Implementations.Collections
|
|||
|
||||
return results.Values;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<BaseItem> GetItemsForRunTimeTicksCount(BaseItem item)
|
||||
{
|
||||
var itemsForCount = new List<BaseItem>();
|
||||
|
||||
if (item.GetType() == typeof(Series))
|
||||
{
|
||||
var seasons = _libraryManager.GetItemList(
|
||||
new InternalItemsQuery()
|
||||
{
|
||||
ParentId = item.Id,
|
||||
SeriesPresentationUniqueKey = item.PresentationUniqueKey
|
||||
}).OfType<Season>();
|
||||
foreach (var season in seasons.ToList())
|
||||
{
|
||||
var episodes = _libraryManager.GetItemList(
|
||||
new InternalItemsQuery()
|
||||
{
|
||||
ParentId = season.Id
|
||||
}).OfType<Episode>();
|
||||
|
||||
foreach (var e in episodes)
|
||||
{
|
||||
var episode = _libraryManager.GetItemById(e.Id);
|
||||
itemsForCount.Add(episode);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
itemsForCount.Add(item);
|
||||
}
|
||||
|
||||
return itemsForCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ using MediaBrowser.Model.Library;
|
|||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using TMDbLib.Objects.Authentication;
|
||||
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
|
||||
using EpisodeInfo = Emby.Naming.TV.EpisodeInfo;
|
||||
using Genre = MediaBrowser.Controller.Entities.Genre;
|
||||
|
@ -1612,14 +1611,18 @@ namespace Emby.Server.Implementations.Library
|
|||
/// <returns>IEnumerable{System.String}.</returns>
|
||||
public async Task<IEnumerable<Video>> GetIntros(BaseItem item, User user)
|
||||
{
|
||||
if (IntroProviders.Length == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var tasks = IntroProviders
|
||||
.Take(1)
|
||||
.Select(i => GetIntros(i, item, user));
|
||||
|
||||
var items = await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
return items
|
||||
.SelectMany(i => i.ToArray())
|
||||
.SelectMany(i => i)
|
||||
.Select(ResolveIntro)
|
||||
.Where(i => i is not null)!; // null values got filtered out
|
||||
}
|
||||
|
|
|
@ -274,7 +274,7 @@ namespace Emby.Server.Implementations.Library
|
|||
var tasks = _providers.Select(i => GetDynamicMediaSources(item, i, cancellationToken));
|
||||
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
return results.SelectMany(i => i.ToList());
|
||||
return results.SelectMany(i => i);
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(BaseItem item, IMediaSourceProvider provider, CancellationToken cancellationToken)
|
||||
|
|
|
@ -108,6 +108,8 @@
|
|||
"TaskRefreshLibrary": "Scan Media Library",
|
||||
"TaskRefreshChapterImagesDescription": "Creates thumbnails for videos that have chapters.",
|
||||
"TaskRefreshChapterImages": "Extract Chapter Images",
|
||||
"TaskCollectionDurationDescription": "Calculate the total duration of all collections.",
|
||||
"TaskCollectionDuration": "Calculate collections duration",
|
||||
"TaskCleanCacheDescription": "Deletes cache files no longer needed by the system.",
|
||||
"TaskCleanCache": "Clean Cache Directory",
|
||||
"TasksChannelsCategory": "Internet Channels",
|
||||
|
|
|
@ -106,6 +106,8 @@
|
|||
"TaskCleanCacheDescription": "Deletes cache files no longer needed by the system.",
|
||||
"TaskRefreshChapterImages": "Extract Chapter Images",
|
||||
"TaskRefreshChapterImagesDescription": "Creates thumbnails for videos that have chapters.",
|
||||
"TaskCollectionDurationDescription": "Calculate the total duration of all collections.",
|
||||
"TaskCollectionDuration": "Calculate collections duration",
|
||||
"TaskRefreshLibrary": "Scan Media Library",
|
||||
"TaskRefreshLibraryDescription": "Scans your media library for new files and refreshes metadata.",
|
||||
"TaskCleanLogs": "Clean Log Directory",
|
||||
|
|
|
@ -108,6 +108,8 @@
|
|||
"TaskRefreshLibrary": "Scan Librerie",
|
||||
"TaskRefreshChapterImagesDescription": "Crea le thumbnail per i video che hanno capitoli.",
|
||||
"TaskRefreshChapterImages": "Estrai immagini capitolo",
|
||||
"TaskCollectionDurationDescription": "Calcola la durata totale di tutte le collezioni.",
|
||||
"TaskCollectionDuration": "Calcola durata collezioni",
|
||||
"TaskCleanCacheDescription": "Cancella i file di cache non più necessari al sistema.",
|
||||
"TaskCleanCache": "Pulisci la directory della cache",
|
||||
"TasksChannelsCategory": "Canali su Internet",
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Collections;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
/// <summary>
|
||||
/// Class CollectionDurationTask.
|
||||
/// </summary>
|
||||
public class CollectionDurationTask : IScheduledTask
|
||||
{
|
||||
/// <summary>
|
||||
/// The _library manager.
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
private readonly ICollectionManager _collectionManager;
|
||||
|
||||
private readonly ILocalizationManager _localization;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CollectionDurationTask" /> class.
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">The library manager.</param>.
|
||||
/// <param name="collectionManager">The collection manager.</param>.
|
||||
/// <param name="localization">The localization manager.</param>
|
||||
public CollectionDurationTask(
|
||||
ILibraryManager libraryManager,
|
||||
ICollectionManager collectionManager,
|
||||
ILocalizationManager localization)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_collectionManager = collectionManager;
|
||||
_localization = localization;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => _localization.GetLocalizedString("TaskCollectionDuration");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Description => _localization.GetLocalizedString("TaskCollectionDurationDescription");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Key => "CollectionDuration";
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new TaskTriggerInfo
|
||||
{
|
||||
Type = TaskTriggerInfo.TriggerDaily,
|
||||
TimeOfDayTicks = TimeSpan.FromHours(4).Ticks,
|
||||
MaxRuntimeTicks = TimeSpan.FromHours(5).Ticks
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
var collections = _libraryManager.GetItemList(new InternalItemsQuery
|
||||
{
|
||||
IsFolder = true,
|
||||
Recursive = true,
|
||||
SourceTypes = new SourceType[] { SourceType.Library },
|
||||
})
|
||||
.OfType<BoxSet>()
|
||||
.ToList();
|
||||
|
||||
var numComplete = 0;
|
||||
|
||||
foreach (var collection in collections)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
var collectionChildren = new List<BaseItem>();
|
||||
|
||||
foreach (var child in collection.LinkedChildren)
|
||||
{
|
||||
if (child.ItemId.HasValue)
|
||||
{
|
||||
var item = _libraryManager.GetItemById((Guid)child.ItemId);
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
_collectionManager.GetItemsForRunTimeTicksCount(item).ToList<BaseItem>().ForEach((i) => collectionChildren.Add(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collection.UpdateRunTimeTicksToItems(collectionChildren);
|
||||
|
||||
await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(true);
|
||||
|
||||
numComplete++;
|
||||
double percent = numComplete;
|
||||
percent /= collections.Count;
|
||||
|
||||
progress.Report(100 * percent);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -63,5 +63,12 @@ namespace MediaBrowser.Controller.Collections
|
|||
/// <param name="createIfNeeded">Will create the collection folder on the storage if set to true.</param>
|
||||
/// <returns>The folder instance referencing the collection storage.</returns>
|
||||
Task<Folder?> GetCollectionsFolder(bool createIfNeeded);
|
||||
|
||||
/// <summary>
|
||||
/// Get Items For RunTimeTicks Count.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>IList{BaseItem}.</returns>
|
||||
IEnumerable<BaseItem> GetItemsForRunTimeTicksCount(BaseItem item);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -228,5 +228,28 @@ namespace MediaBrowser.Controller.Entities.Movies
|
|||
|
||||
return new[] { item };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the RunTimeTicks based on content and returns true or false indicating if it changed.
|
||||
/// </summary>
|
||||
/// <param name="children">Media children.</param>
|
||||
/// <returns><c>true</c> if the total duration in ticks was updated; otherwise <c>false</c>.</returns>
|
||||
public bool UpdateRunTimeTicksToItems(IList<BaseItem> children)
|
||||
{
|
||||
var currentRunTimeTicks = RunTimeTicks;
|
||||
|
||||
long ticks = 0;
|
||||
foreach (var item in children)
|
||||
{
|
||||
if (item.RunTimeTicks != null && item.RunTimeTicks > 0)
|
||||
{
|
||||
ticks = ticks + (long)item.RunTimeTicks;
|
||||
}
|
||||
}
|
||||
|
||||
RunTimeTicks = ticks;
|
||||
|
||||
return currentRunTimeTicks != RunTimeTicks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -286,7 +286,7 @@ namespace MediaBrowser.Providers.Manager
|
|||
|
||||
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
return results.SelectMany(i => i.ToList());
|
||||
return results.SelectMany(i => i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
Loading…
Reference in New Issue