mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-04-20 09:04:42 +01:00
Merge branch 'master' into TVFix
This commit is contained in:
@@ -37,8 +37,9 @@ namespace MediaBrowser.Controller.Dlna
|
||||
/// <summary>
|
||||
/// Updates the profile.
|
||||
/// </summary>
|
||||
/// <param name="profileId">The profile id.</param>
|
||||
/// <param name="profile">The profile.</param>
|
||||
void UpdateProfile(DeviceProfile profile);
|
||||
void UpdateProfile(string profileId, DeviceProfile profile);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the profile.
|
||||
@@ -74,6 +75,6 @@ namespace MediaBrowser.Controller.Dlna
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns>DlnaIconResponse.</returns>
|
||||
ImageStream GetIcon(string filename);
|
||||
ImageStream? GetIcon(string filename);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace MediaBrowser.Controller.Drawing
|
||||
/// <returns>Guid.</returns>
|
||||
string GetImageCacheTag(BaseItem item, ItemImageInfo image);
|
||||
|
||||
string GetImageCacheTag(BaseItem item, ChapterInfo info);
|
||||
string GetImageCacheTag(BaseItem item, ChapterInfo chapter);
|
||||
|
||||
string? GetImageCacheTag(User user);
|
||||
|
||||
|
||||
@@ -8,11 +8,16 @@ namespace MediaBrowser.Controller.Drawing
|
||||
{
|
||||
public class ImageStream : IDisposable
|
||||
{
|
||||
public ImageStream(Stream stream)
|
||||
{
|
||||
Stream = stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stream.
|
||||
/// Gets the stream.
|
||||
/// </summary>
|
||||
/// <value>The stream.</value>
|
||||
public Stream? Stream { get; set; }
|
||||
public Stream Stream { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format.
|
||||
|
||||
@@ -19,6 +19,7 @@ using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
@@ -43,18 +44,10 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <summary>
|
||||
/// The trailer folder name.
|
||||
/// </summary>
|
||||
public const string TrailerFolderName = "trailers";
|
||||
public const string TrailersFolderName = "trailers";
|
||||
public const string ThemeSongsFolderName = "theme-music";
|
||||
public const string ThemeSongFilename = "theme";
|
||||
public const string ThemeSongFileName = "theme";
|
||||
public const string ThemeVideosFolderName = "backdrops";
|
||||
public const string ExtrasFolderName = "extras";
|
||||
public const string BehindTheScenesFolderName = "behind the scenes";
|
||||
public const string DeletedScenesFolderName = "deleted scenes";
|
||||
public const string InterviewFolderName = "interviews";
|
||||
public const string SceneFolderName = "scenes";
|
||||
public const string SampleFolderName = "samples";
|
||||
public const string ShortsFolderName = "shorts";
|
||||
public const string FeaturettesFolderName = "featurettes";
|
||||
|
||||
/// <summary>
|
||||
/// The supported image extensions.
|
||||
@@ -91,17 +84,19 @@ namespace MediaBrowser.Controller.Entities
|
||||
Model.Entities.ExtraType.Scene
|
||||
};
|
||||
|
||||
public static readonly char[] SlugReplaceChars = { '?', '/', '&' };
|
||||
public static readonly string[] AllExtrasTypesFolderNames =
|
||||
/// <summary>
|
||||
/// The supported extra folder names and types. See <see cref="Emby.Naming.Common.NamingOptions" />.
|
||||
/// </summary>
|
||||
public static readonly Dictionary<string, ExtraType> AllExtrasTypesFolderNames = new Dictionary<string, ExtraType>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
ExtrasFolderName,
|
||||
BehindTheScenesFolderName,
|
||||
DeletedScenesFolderName,
|
||||
InterviewFolderName,
|
||||
SceneFolderName,
|
||||
SampleFolderName,
|
||||
ShortsFolderName,
|
||||
FeaturettesFolderName
|
||||
["extras"] = MediaBrowser.Model.Entities.ExtraType.Unknown,
|
||||
["behind the scenes"] = MediaBrowser.Model.Entities.ExtraType.BehindTheScenes,
|
||||
["deleted scenes"] = MediaBrowser.Model.Entities.ExtraType.DeletedScene,
|
||||
["interviews"] = MediaBrowser.Model.Entities.ExtraType.Interview,
|
||||
["scenes"] = MediaBrowser.Model.Entities.ExtraType.Scene,
|
||||
["samples"] = MediaBrowser.Model.Entities.ExtraType.Sample,
|
||||
["shorts"] = MediaBrowser.Model.Entities.ExtraType.Clip,
|
||||
["featurettes"] = MediaBrowser.Model.Entities.ExtraType.Clip
|
||||
};
|
||||
|
||||
private string _sortName;
|
||||
@@ -357,11 +352,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
get
|
||||
{
|
||||
// if (IsOffline)
|
||||
// {
|
||||
// return LocationType.Offline;
|
||||
// }
|
||||
|
||||
var path = Path;
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
@@ -394,7 +384,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsFileProtocol => IsPathProtocol(MediaProtocol.File);
|
||||
public bool IsFileProtocol => PathProtocol == MediaProtocol.File;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool HasPathProtocol => PathProtocol.HasValue;
|
||||
@@ -586,14 +576,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual Guid DisplayParentId
|
||||
{
|
||||
get
|
||||
{
|
||||
var parentId = ParentId;
|
||||
return parentId;
|
||||
}
|
||||
}
|
||||
public virtual Guid DisplayParentId => ParentId;
|
||||
|
||||
[JsonIgnore]
|
||||
public BaseItem DisplayParent
|
||||
@@ -856,13 +839,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
return Id.ToString("N", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public bool IsPathProtocol(MediaProtocol protocol)
|
||||
{
|
||||
var current = PathProtocol;
|
||||
|
||||
return current.HasValue && current.Value == protocol;
|
||||
}
|
||||
|
||||
private List<Tuple<StringBuilder, bool>> GetSortChunks(string s1)
|
||||
{
|
||||
var list = new List<Tuple<StringBuilder, bool>>();
|
||||
@@ -990,7 +966,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
ReadOnlySpan<char> idString = Id.ToString("N", CultureInfo.InvariantCulture);
|
||||
|
||||
return System.IO.Path.Join(basePath, "library", idString.Slice(0, 2), idString);
|
||||
return System.IO.Path.Join(basePath, "library", idString[..2], idString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1305,8 +1281,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
terms.Add(item.Name);
|
||||
}
|
||||
|
||||
var video = item as Video;
|
||||
if (video != null)
|
||||
if (item is Video video)
|
||||
{
|
||||
if (video.Video3DFormat.HasValue)
|
||||
{
|
||||
@@ -1341,7 +1316,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join('/', terms.ToArray());
|
||||
return string.Join('/', terms);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1357,16 +1332,14 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
// Support plex/xbmc convention
|
||||
files.AddRange(fileSystemChildren
|
||||
.Where(i => !i.IsDirectory && System.IO.Path.GetFileNameWithoutExtension(i.FullName.AsSpan()).Equals(ThemeSongFilename, StringComparison.OrdinalIgnoreCase)));
|
||||
.Where(i => !i.IsDirectory && System.IO.Path.GetFileNameWithoutExtension(i.FullName.AsSpan()).Equals(ThemeSongFileName, StringComparison.OrdinalIgnoreCase)));
|
||||
|
||||
return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions())
|
||||
.OfType<Audio.Audio>()
|
||||
.Select(audio =>
|
||||
{
|
||||
// Try to retrieve it from the db. If we don't find it, use the resolved version
|
||||
var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
|
||||
|
||||
if (dbItem != null)
|
||||
if (LibraryManager.GetItemById(audio.Id) is Audio.Audio dbItem)
|
||||
{
|
||||
audio = dbItem;
|
||||
}
|
||||
@@ -1416,39 +1389,24 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
protected virtual BaseItem[] LoadExtras(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
|
||||
{
|
||||
var extras = new List<Video>();
|
||||
|
||||
var libraryOptions = new LibraryOptions();
|
||||
var folders = fileSystemChildren.Where(i => i.IsDirectory).ToList();
|
||||
foreach (var extraFolderName in AllExtrasTypesFolderNames)
|
||||
{
|
||||
var files = folders
|
||||
.Where(i => string.Equals(i.Name, extraFolderName, StringComparison.OrdinalIgnoreCase))
|
||||
.SelectMany(i => FileSystem.GetFiles(i.FullName));
|
||||
|
||||
// Re-using the same instance of LibraryOptions since it looks like it's never being altered.
|
||||
extras.AddRange(LibraryManager.ResolvePaths(files, directoryService, null, libraryOptions)
|
||||
return fileSystemChildren
|
||||
.Where(child => child.IsDirectory && AllExtrasTypesFolderNames.ContainsKey(child.Name))
|
||||
.SelectMany(folder => LibraryManager
|
||||
.ResolvePaths(FileSystem.GetFiles(folder.FullName), directoryService, null, new LibraryOptions())
|
||||
.OfType<Video>()
|
||||
.Select(item =>
|
||||
.Select(video =>
|
||||
{
|
||||
// Try to retrieve it from the db. If we don't find it, use the resolved version
|
||||
if (LibraryManager.GetItemById(item.Id) is Video dbItem)
|
||||
if (LibraryManager.GetItemById(video.Id) is Video dbItem)
|
||||
{
|
||||
item = dbItem;
|
||||
video = dbItem;
|
||||
}
|
||||
|
||||
// Use some hackery to get the extra type based on foldername
|
||||
item.ExtraType = Enum.TryParse(extraFolderName.Replace(" ", string.Empty, StringComparison.Ordinal), true, out ExtraType extraType)
|
||||
? extraType
|
||||
: Model.Entities.ExtraType.Unknown;
|
||||
|
||||
return item;
|
||||
|
||||
// Sort them so that the list can be easily compared for changes
|
||||
}).OrderBy(i => i.Path));
|
||||
}
|
||||
|
||||
return extras.ToArray();
|
||||
video.ExtraType = AllExtrasTypesFolderNames[folder.Name];
|
||||
return video;
|
||||
})
|
||||
.OrderBy(video => video.Path)) // Sort them so that the list can be easily compared for changes
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public Task RefreshMetadata(CancellationToken cancellationToken)
|
||||
@@ -1588,8 +1546,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
}
|
||||
|
||||
var hasTrailers = this as IHasTrailers;
|
||||
if (hasTrailers != null)
|
||||
if (this is IHasTrailers hasTrailers)
|
||||
{
|
||||
localTrailersChanged = await RefreshLocalTrailers(hasTrailers, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
@@ -2286,7 +2243,11 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
var existingImage = GetImageInfo(image.Type, index);
|
||||
|
||||
if (existingImage != null)
|
||||
if (existingImage == null)
|
||||
{
|
||||
AddImage(image);
|
||||
}
|
||||
else
|
||||
{
|
||||
existingImage.Path = image.Path;
|
||||
existingImage.DateModified = image.DateModified;
|
||||
@@ -2294,15 +2255,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
existingImage.Height = image.Height;
|
||||
existingImage.BlurHash = image.BlurHash;
|
||||
}
|
||||
else
|
||||
{
|
||||
var current = ImageInfos;
|
||||
var currentCount = current.Length;
|
||||
var newArr = new ItemImageInfo[currentCount + 1];
|
||||
current.CopyTo(newArr, 0);
|
||||
newArr[currentCount] = image;
|
||||
ImageInfos = newArr;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetImagePath(ImageType type, int index, FileSystemMetadata file)
|
||||
@@ -2316,7 +2268,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
if (image == null)
|
||||
{
|
||||
ImageInfos = ImageInfos.Concat(new[] { GetImageInfo(file, type) }).ToArray();
|
||||
AddImage(GetImageInfo(file, type));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2360,14 +2312,24 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
public void RemoveImage(ItemImageInfo image)
|
||||
{
|
||||
RemoveImages(new List<ItemImageInfo> { image });
|
||||
RemoveImages(new[] { image });
|
||||
}
|
||||
|
||||
public void RemoveImages(List<ItemImageInfo> deletedImages)
|
||||
public void RemoveImages(IEnumerable<ItemImageInfo> deletedImages)
|
||||
{
|
||||
ImageInfos = ImageInfos.Except(deletedImages).ToArray();
|
||||
}
|
||||
|
||||
public void AddImage(ItemImageInfo image)
|
||||
{
|
||||
var current = ImageInfos;
|
||||
var currentCount = current.Length;
|
||||
var newArr = new ItemImageInfo[currentCount + 1];
|
||||
current.CopyTo(newArr, 0);
|
||||
newArr[currentCount] = image;
|
||||
ImageInfos = newArr;
|
||||
}
|
||||
|
||||
public virtual Task UpdateToRepositoryAsync(ItemUpdateType updateReason, CancellationToken cancellationToken)
|
||||
=> LibraryManager.UpdateItemAsync(this, GetParent(), updateReason, cancellationToken);
|
||||
|
||||
@@ -2391,7 +2353,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
if (deletedImages.Count > 0)
|
||||
{
|
||||
ImageInfos = ImageInfos.Except(deletedImages).ToArray();
|
||||
RemoveImages(deletedImages);
|
||||
}
|
||||
|
||||
return deletedImages.Count > 0;
|
||||
@@ -2439,6 +2401,17 @@ namespace MediaBrowser.Controller.Entities
|
||||
};
|
||||
}
|
||||
|
||||
// Music albums usually don't have dedicated backdrops, so return one from the artist instead
|
||||
if (GetType() == typeof(MusicAlbum) && imageType == ImageType.Backdrop)
|
||||
{
|
||||
var artist = FindParent<MusicArtist>();
|
||||
|
||||
if (artist != null)
|
||||
{
|
||||
return artist.GetImages(imageType).ElementAtOrDefault(imageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return GetImages(imageType)
|
||||
.ElementAtOrDefault(imageIndex);
|
||||
}
|
||||
@@ -2502,11 +2475,11 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the images.
|
||||
/// Adds the images, updating metadata if they already are part of this item.
|
||||
/// </summary>
|
||||
/// <param name="imageType">Type of the image.</param>
|
||||
/// <param name="images">The images.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||
/// <returns><c>true</c> if images were added or updated, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentException">Cannot call AddImages with chapter images.</exception>
|
||||
public bool AddImages(ImageType imageType, List<FileSystemMetadata> images)
|
||||
{
|
||||
@@ -2519,7 +2492,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
.ToList();
|
||||
|
||||
var newImageList = new List<FileSystemMetadata>();
|
||||
var imageAdded = false;
|
||||
var imageUpdated = false;
|
||||
|
||||
foreach (var newImage in images)
|
||||
@@ -2535,7 +2507,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
if (existing == null)
|
||||
{
|
||||
newImageList.Add(newImage);
|
||||
imageAdded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2556,19 +2527,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
}
|
||||
|
||||
if (imageAdded || images.Count != existingImages.Count)
|
||||
{
|
||||
var newImagePaths = images.Select(i => i.FullName).ToList();
|
||||
|
||||
var deleted = existingImages
|
||||
.FindAll(i => i.IsLocalFile && !newImagePaths.Contains(i.Path.AsSpan(), StringComparison.OrdinalIgnoreCase) && !File.Exists(i.Path));
|
||||
|
||||
if (deleted.Count > 0)
|
||||
{
|
||||
ImageInfos = ImageInfos.Except(deleted).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
if (newImageList.Count > 0)
|
||||
{
|
||||
ImageInfos = ImageInfos.Concat(newImageList.Select(i => GetImageInfo(i, imageType))).ToArray();
|
||||
@@ -2619,7 +2577,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
public bool AllowsMultipleImages(ImageType type)
|
||||
{
|
||||
return type == ImageType.Backdrop || type == ImageType.Screenshot || type == ImageType.Chapter;
|
||||
return type == ImageType.Backdrop || type == ImageType.Chapter;
|
||||
}
|
||||
|
||||
public Task SwapImagesAsync(ImageType type, int index1, int index2)
|
||||
@@ -2737,7 +2695,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
protected static string GetMappedPath(BaseItem item, string path, MediaProtocol? protocol)
|
||||
{
|
||||
if (protocol.HasValue && protocol.Value == MediaProtocol.File)
|
||||
if (protocol == MediaProtocol.File)
|
||||
{
|
||||
return LibraryManager.GetPathAfterNetworkSubstitution(path, item);
|
||||
}
|
||||
@@ -2765,8 +2723,10 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
protected Task RefreshMetadataForOwnedItem(BaseItem ownedItem, bool copyTitleMetadata, MetadataRefreshOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
var newOptions = new MetadataRefreshOptions(options);
|
||||
newOptions.SearchResult = null;
|
||||
var newOptions = new MetadataRefreshOptions(options)
|
||||
{
|
||||
SearchResult = null
|
||||
};
|
||||
|
||||
var item = this;
|
||||
|
||||
@@ -2827,8 +2787,10 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
protected Task RefreshMetadataForOwnedVideo(MetadataRefreshOptions options, bool copyTitleMetadata, string path, CancellationToken cancellationToken)
|
||||
{
|
||||
var newOptions = new MetadataRefreshOptions(options);
|
||||
newOptions.SearchResult = null;
|
||||
var newOptions = new MetadataRefreshOptions(options)
|
||||
{
|
||||
SearchResult = null
|
||||
};
|
||||
|
||||
var id = LibraryManager.GetNewItemId(path, typeof(Video));
|
||||
|
||||
@@ -2842,14 +2804,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
newOptions.ForceSave = true;
|
||||
}
|
||||
|
||||
// var parentId = Id;
|
||||
// if (!video.IsOwnedItem || video.ParentId != parentId)
|
||||
// {
|
||||
// video.IsOwnedItem = true;
|
||||
// video.ParentId = parentId;
|
||||
// newOptions.ForceSave = true;
|
||||
// }
|
||||
|
||||
if (video == null)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
@@ -2933,7 +2887,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
.Select(i => i.OfficialRating)
|
||||
.Where(i => !string.IsNullOrEmpty(i))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Select(i => new Tuple<string, int?>(i, LocalizationManager.GetRatingLevel(i)))
|
||||
.Select(i => (i, LocalizationManager.GetRatingLevel(i)))
|
||||
.OrderBy(i => i.Item2 ?? 1000)
|
||||
.Select(i => i.Item1);
|
||||
|
||||
@@ -2980,18 +2934,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
.Where(i => i.ExtraType.HasValue && extraTypes.Contains(i.ExtraType.Value));
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetTrailers()
|
||||
{
|
||||
if (this is IHasTrailers)
|
||||
{
|
||||
return ((IHasTrailers)this).LocalTrailerIds.Select(LibraryManager.GetItemById).Where(i => i != null).OrderBy(i => i.SortName);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Array.Empty<BaseItem>();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual long GetRunTimeTicksForPlayState()
|
||||
{
|
||||
return RunTimeTicks ?? 0;
|
||||
@@ -3004,7 +2946,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(BaseItem other) => object.Equals(Id, other?.Id);
|
||||
public bool Equals(BaseItem other) => Equals(Id, other?.Id);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode() => HashCode.Combine(Id);
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <param name="file">The file.</param>
|
||||
public static void SetImagePath(this BaseItem item, ImageType imageType, string file)
|
||||
{
|
||||
if (file.StartsWith("http", System.StringComparison.OrdinalIgnoreCase))
|
||||
if (file.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
item.SetImage(
|
||||
new ItemImageInfo
|
||||
|
||||
@@ -1015,17 +1015,17 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
if (!string.IsNullOrEmpty(query.NameStartsWithOrGreater))
|
||||
{
|
||||
items = items.Where(i => string.Compare(query.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1);
|
||||
items = items.Where(i => string.Compare(query.NameStartsWithOrGreater, i.SortName, StringComparison.InvariantCultureIgnoreCase) < 1);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(query.NameStartsWith))
|
||||
{
|
||||
items = items.Where(i => i.SortName.StartsWith(query.NameStartsWith, StringComparison.CurrentCultureIgnoreCase));
|
||||
items = items.Where(i => i.SortName.StartsWith(query.NameStartsWith, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(query.NameLessThan))
|
||||
{
|
||||
items = items.Where(i => string.Compare(query.NameLessThan, i.SortName, StringComparison.CurrentCultureIgnoreCase) == 1);
|
||||
items = items.Where(i => string.Compare(query.NameLessThan, i.SortName, StringComparison.InvariantCultureIgnoreCase) == 1);
|
||||
}
|
||||
|
||||
// This must be the last filter
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// The item has screenshots.
|
||||
/// </summary>
|
||||
public interface IHasScreenshots
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,14 @@ namespace MediaBrowser.Controller.Entities
|
||||
private readonly object _childIdsLock = new object();
|
||||
private List<Guid> _childrenIds = null;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UserRootFolder"/> class.
|
||||
/// </summary>
|
||||
public UserRootFolder()
|
||||
{
|
||||
IsRoot = true;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public override bool SupportsInheritedParentImages => false;
|
||||
|
||||
|
||||
@@ -57,9 +57,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
public IList<BaseItem> GetTaggedItems(InternalItemsQuery query)
|
||||
{
|
||||
var usCulture = new CultureInfo("en-US");
|
||||
|
||||
if (!int.TryParse(Name, NumberStyles.Integer, usCulture, out var year))
|
||||
if (!int.TryParse(Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
|
||||
{
|
||||
return new List<BaseItem>();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using MediaBrowser.Common;
|
||||
@@ -51,11 +50,11 @@ namespace MediaBrowser.Controller
|
||||
/// <summary>
|
||||
/// Gets the system info.
|
||||
/// </summary>
|
||||
/// <param name="source">The originator of the request.</param>
|
||||
/// <param name="request">The HTTP request.</param>
|
||||
/// <returns>SystemInfo.</returns>
|
||||
SystemInfo GetSystemInfo(IPAddress source);
|
||||
SystemInfo GetSystemInfo(HttpRequest request);
|
||||
|
||||
PublicSystemInfo GetPublicSystemInfo(IPAddress address);
|
||||
PublicSystemInfo GetPublicSystemInfo(HttpRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a URL specific for the request.
|
||||
|
||||
19
MediaBrowser.Controller/Library/IDirectStreamProvider.cs
Normal file
19
MediaBrowser.Controller/Library/IDirectStreamProvider.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System.IO;
|
||||
|
||||
namespace MediaBrowser.Controller.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// The direct live TV stream provider.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Deprecated.
|
||||
/// </remarks>
|
||||
public interface IDirectStreamProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the live stream, shared streams seek to the end of the file first.
|
||||
/// </summary>
|
||||
/// <returns>The stream.</returns>
|
||||
Stream GetStream();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#pragma warning disable CA1711, CS1591
|
||||
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Dto;
|
||||
@@ -25,5 +26,7 @@ namespace MediaBrowser.Controller.Library
|
||||
Task Open(CancellationToken openCancellationToken);
|
||||
|
||||
Task Close();
|
||||
|
||||
Stream GetStream();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Data.Entities;
|
||||
@@ -110,6 +109,20 @@ namespace MediaBrowser.Controller.Library
|
||||
|
||||
Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the live stream info.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>An instance of <see cref="ILiveStream"/>.</returns>
|
||||
public ILiveStream GetLiveStreamInfo(string id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the live stream info using the stream's unique id.
|
||||
/// </summary>
|
||||
/// <param name="uniqueId">The unique identifier.</param>
|
||||
/// <returns>An instance of <see cref="ILiveStream"/>.</returns>
|
||||
public ILiveStream GetLiveStreamInfoByUniqueId(string uniqueId);
|
||||
|
||||
/// <summary>
|
||||
/// Closes the media source.
|
||||
/// </summary>
|
||||
@@ -126,14 +139,5 @@ namespace MediaBrowser.Controller.Library
|
||||
void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, User user);
|
||||
|
||||
Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, string cacheKey, bool addProbeDelay, bool isLiveStream, CancellationToken cancellationToken);
|
||||
|
||||
Task<IDirectStreamProvider> GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public interface IDirectStreamProvider
|
||||
{
|
||||
Task CopyToAsync(Stream stream, CancellationToken cancellationToken);
|
||||
|
||||
string GetFilePath();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Diacritics.Extensions;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
|
||||
namespace MediaBrowser.Controller.Library
|
||||
{
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Diacritics" Version="2.1.20036.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" />
|
||||
<PackageReference Include="Diacritics" Version="3.3.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0-rc.2*" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0-rc.2*" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="System.Threading.Tasks.Dataflow" Version="5.0.0" />
|
||||
<PackageReference Include="System.Threading.Tasks.Dataflow" Version="6.0.0-rc.2*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -32,18 +32,13 @@
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release'">
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Stability)'=='Unstable'">
|
||||
|
||||
@@ -21,8 +21,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public class EncodingHelper
|
||||
{
|
||||
private static readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
private readonly ISubtitleEncoder _subtitleEncoder;
|
||||
|
||||
@@ -37,7 +35,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
"ConstrainedHigh"
|
||||
};
|
||||
|
||||
private static readonly Version minVersionForCudaOverlay = new Version(4, 4);
|
||||
private static readonly Version _minVersionForCudaOverlay = new Version(4, 4);
|
||||
|
||||
public EncodingHelper(
|
||||
IMediaEncoder mediaEncoder,
|
||||
@@ -647,8 +645,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
}
|
||||
|
||||
if (state.IsVideoRequest
|
||||
&& ((string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
|
||||
&& (isNvdecDecoder || isCuvidHevcDecoder || isCuvidVp9Decoder || isSwDecoder))))
|
||||
&& string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
|
||||
&& (isNvdecDecoder || isCuvidHevcDecoder || isCuvidVp9Decoder || isSwDecoder))
|
||||
{
|
||||
if (!isCudaTonemappingSupported && isOpenclTonemappingSupported)
|
||||
{
|
||||
@@ -816,7 +814,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
public static string NormalizeTranscodingLevel(EncodingJobInfo state, string level)
|
||||
{
|
||||
if (double.TryParse(level, NumberStyles.Any, _usCulture, out double requestLevel))
|
||||
if (double.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out double requestLevel))
|
||||
{
|
||||
if (string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -911,7 +909,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
CultureInfo.InvariantCulture,
|
||||
"subtitles='{0}:si={1}'{2}",
|
||||
_mediaEncoder.EscapeSubtitleFilterPath(mediaPath),
|
||||
state.InternalSubtitleStreamOffset.ToString(_usCulture),
|
||||
state.InternalSubtitleStreamOffset.ToString(CultureInfo.InvariantCulture),
|
||||
// fallbackFontParam,
|
||||
setPtsParam);
|
||||
}
|
||||
@@ -1217,7 +1215,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
param += string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
" -speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}",
|
||||
profileScore.ToString(_usCulture),
|
||||
profileScore.ToString(CultureInfo.InvariantCulture),
|
||||
crf,
|
||||
qmin,
|
||||
qmax);
|
||||
@@ -1289,7 +1287,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
var framerate = GetFramerateParam(state);
|
||||
if (framerate.HasValue)
|
||||
{
|
||||
param += string.Format(CultureInfo.InvariantCulture, " -r {0}", framerate.Value.ToString(_usCulture));
|
||||
param += string.Format(CultureInfo.InvariantCulture, " -r {0}", framerate.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
var targetVideoCodec = state.ActualOutputVideoCodec;
|
||||
@@ -1393,7 +1391,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
else if (string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// hevc_qsv use -level 51 instead of -level 153.
|
||||
if (double.TryParse(level, NumberStyles.Any, _usCulture, out double hevcLevel))
|
||||
if (double.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out double hevcLevel))
|
||||
{
|
||||
param += " -level " + (hevcLevel / 3);
|
||||
}
|
||||
@@ -1555,7 +1553,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
// If a specific level was requested, the source must match or be less than
|
||||
var level = state.GetRequestedLevel(videoStream.Codec);
|
||||
if (!string.IsNullOrEmpty(level)
|
||||
&& double.TryParse(level, NumberStyles.Any, _usCulture, out var requestLevel))
|
||||
&& double.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out var requestLevel))
|
||||
{
|
||||
if (!videoStream.Level.HasValue)
|
||||
{
|
||||
@@ -1803,7 +1801,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
&& state.AudioStream.Channels.Value > 5
|
||||
&& !encodingOptions.DownMixAudioBoost.Equals(1))
|
||||
{
|
||||
filters.Add("volume=" + encodingOptions.DownMixAudioBoost.ToString(_usCulture));
|
||||
filters.Add("volume=" + encodingOptions.DownMixAudioBoost.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
var isCopyingTimestamps = state.CopyTimestamps || state.TranscodingType != TranscodingJobType.Progressive;
|
||||
@@ -2099,7 +2097,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
|
||||
|
||||
var mediaEncoderVersion = _mediaEncoder.GetMediaEncoderVersion();
|
||||
var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= minVersionForCudaOverlay;
|
||||
var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= _minVersionForCudaOverlay;
|
||||
var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.ScaleCudaFormat);
|
||||
|
||||
// Tonemapping and burn-in graphical subtitles requires overlay_vaapi.
|
||||
@@ -2380,7 +2378,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
var isCudaTonemappingSupported = IsCudaTonemappingSupported(state, options);
|
||||
var isTonemappingSupportedOnNvenc = string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase);
|
||||
var mediaEncoderVersion = _mediaEncoder.GetMediaEncoderVersion();
|
||||
var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= minVersionForCudaOverlay;
|
||||
var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= _minVersionForCudaOverlay;
|
||||
var isCudaFormatConversionSupported = _mediaEncoder.SupportsFilterWithOption(FilterOptionType.ScaleCudaFormat);
|
||||
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
|
||||
@@ -2434,8 +2432,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
if (isExynosV4L2)
|
||||
{
|
||||
var widthParam = requestedWidth.Value.ToString(_usCulture);
|
||||
var heightParam = requestedHeight.Value.ToString(_usCulture);
|
||||
var widthParam = requestedWidth.Value.ToString(CultureInfo.InvariantCulture);
|
||||
var heightParam = requestedHeight.Value.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
filters.Add(
|
||||
string.Format(
|
||||
@@ -2453,8 +2451,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
// If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
|
||||
else if (requestedMaxWidth.HasValue && requestedMaxHeight.HasValue)
|
||||
{
|
||||
var maxWidthParam = requestedMaxWidth.Value.ToString(_usCulture);
|
||||
var maxHeightParam = requestedMaxHeight.Value.ToString(_usCulture);
|
||||
var maxWidthParam = requestedMaxWidth.Value.ToString(CultureInfo.InvariantCulture);
|
||||
var maxHeightParam = requestedMaxHeight.Value.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (isExynosV4L2)
|
||||
{
|
||||
@@ -2486,7 +2484,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
}
|
||||
else
|
||||
{
|
||||
var widthParam = requestedWidth.Value.ToString(_usCulture);
|
||||
var widthParam = requestedWidth.Value.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
filters.Add(
|
||||
string.Format(
|
||||
@@ -2499,7 +2497,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
// If a fixed height was requested
|
||||
else if (requestedHeight.HasValue)
|
||||
{
|
||||
var heightParam = requestedHeight.Value.ToString(_usCulture);
|
||||
var heightParam = requestedHeight.Value.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (isExynosV4L2)
|
||||
{
|
||||
@@ -2522,7 +2520,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
// If a max width was requested
|
||||
else if (requestedMaxWidth.HasValue)
|
||||
{
|
||||
var maxWidthParam = requestedMaxWidth.Value.ToString(_usCulture);
|
||||
var maxWidthParam = requestedMaxWidth.Value.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (isExynosV4L2)
|
||||
{
|
||||
@@ -2545,7 +2543,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
// If a max height was requested
|
||||
else if (requestedMaxHeight.HasValue)
|
||||
{
|
||||
var maxHeightParam = requestedMaxHeight.Value.ToString(_usCulture);
|
||||
var maxHeightParam = requestedMaxHeight.Value.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (isExynosV4L2)
|
||||
{
|
||||
@@ -2683,7 +2681,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
var isVppTonemappingSupported = IsVppTonemappingSupported(state, options);
|
||||
var isCudaTonemappingSupported = IsCudaTonemappingSupported(state, options);
|
||||
var mediaEncoderVersion = _mediaEncoder.GetMediaEncoderVersion();
|
||||
var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= minVersionForCudaOverlay;
|
||||
var isCudaOverlaySupported = _mediaEncoder.SupportsFilter("overlay_cuda") && mediaEncoderVersion != null && mediaEncoderVersion >= _minVersionForCudaOverlay;
|
||||
|
||||
var hasSubs = state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
|
||||
@@ -4122,12 +4120,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
if (bitrate.HasValue)
|
||||
{
|
||||
args += " -ab " + bitrate.Value.ToString(_usCulture);
|
||||
args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (state.OutputAudioSampleRate.HasValue)
|
||||
{
|
||||
args += " -ar " + state.OutputAudioSampleRate.Value.ToString(_usCulture);
|
||||
args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
args += GetAudioFilterParam(state, encodingOptions);
|
||||
@@ -4143,12 +4141,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
if (bitrate.HasValue)
|
||||
{
|
||||
audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(_usCulture));
|
||||
audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
if (state.OutputAudioChannels.HasValue)
|
||||
{
|
||||
audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(_usCulture));
|
||||
audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
// opus will fail on 44100
|
||||
@@ -4156,7 +4154,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
if (state.OutputAudioSampleRate.HasValue)
|
||||
{
|
||||
audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(_usCulture));
|
||||
audioTranscodeParams.Add("-ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -541,7 +541,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
return MimeType;
|
||||
}
|
||||
|
||||
return MimeTypes.GetMimeType(outputPath, enableStreamDefault);
|
||||
if (enableStreamDefault)
|
||||
{
|
||||
return MimeTypes.GetMimeType(outputPath);
|
||||
}
|
||||
|
||||
return MimeTypes.GetMimeType(outputPath, null);
|
||||
}
|
||||
|
||||
public bool DeInterlace(string videoCodec, bool forceDeinterlaceIfSourceIsInterlaced)
|
||||
|
||||
@@ -95,9 +95,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
/// <param name="mediaSource">Media source information.</param>
|
||||
/// <param name="imageStream">Media stream information.</param>
|
||||
/// <param name="imageStreamIndex">Index of the stream to extract from.</param>
|
||||
/// <param name="outputExtension">The extension of the file to write, including the '.'.</param>
|
||||
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
|
||||
/// <returns>Location of video image.</returns>
|
||||
Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken);
|
||||
Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, string outputExtension, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the video images on interval.
|
||||
|
||||
@@ -6,7 +6,6 @@ using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@@ -14,7 +13,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public class JobLogger
|
||||
{
|
||||
private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public JobLogger(ILogger logger)
|
||||
@@ -88,7 +86,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
var rate = parts[i + 1];
|
||||
|
||||
if (float.TryParse(rate, NumberStyles.Any, _usCulture, out var val))
|
||||
if (float.TryParse(rate, NumberStyles.Any, CultureInfo.InvariantCulture, out var val))
|
||||
{
|
||||
framerate = val;
|
||||
}
|
||||
@@ -97,7 +95,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
var rate = part.Split('=', 2)[^1];
|
||||
|
||||
if (float.TryParse(rate, NumberStyles.Any, _usCulture, out var val))
|
||||
if (float.TryParse(rate, NumberStyles.Any, CultureInfo.InvariantCulture, out var val))
|
||||
{
|
||||
framerate = val;
|
||||
}
|
||||
@@ -107,7 +105,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
var time = part.Split('=', 2)[^1];
|
||||
|
||||
if (TimeSpan.TryParse(time, _usCulture, out var val))
|
||||
if (TimeSpan.TryParse(time, CultureInfo.InvariantCulture, out var val))
|
||||
{
|
||||
var currentMs = startMs + val.TotalMilliseconds;
|
||||
|
||||
@@ -121,7 +119,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
var size = part.Split('=', 2)[^1];
|
||||
|
||||
int? scale = null;
|
||||
if (size.IndexOf("kb", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
if (size.Contains("kb", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
scale = 1024;
|
||||
size = size.Replace("kb", string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
@@ -129,7 +127,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
if (scale.HasValue)
|
||||
{
|
||||
if (long.TryParse(size, NumberStyles.Any, _usCulture, out var val))
|
||||
if (long.TryParse(size, NumberStyles.Any, CultureInfo.InvariantCulture, out var val))
|
||||
{
|
||||
bytesTranscoded = val * scale.Value;
|
||||
}
|
||||
@@ -140,7 +138,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
var rate = part.Split('=', 2)[^1];
|
||||
|
||||
int? scale = null;
|
||||
if (rate.IndexOf("kbits/s", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
if (rate.Contains("kbits/s", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
scale = 1024;
|
||||
rate = rate.Replace("kbits/s", string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
@@ -148,7 +146,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
if (scale.HasValue)
|
||||
{
|
||||
if (float.TryParse(rate, NumberStyles.Any, _usCulture, out var val))
|
||||
if (float.TryParse(rate, NumberStyles.Any, CultureInfo.InvariantCulture, out var val))
|
||||
{
|
||||
bitRate = (int)Math.Ceiling(val * scale.Value);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
|
||||
@@ -35,7 +33,7 @@ namespace MediaBrowser.Controller.Providers
|
||||
/// <summary>
|
||||
/// Gets the URL format string for this id.
|
||||
/// </summary>
|
||||
string UrlFormatString { get; }
|
||||
string? UrlFormatString { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this id supports a given item type.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@ using Jellyfin.Data.Entities.Security;
|
||||
using Jellyfin.Data.Events;
|
||||
using MediaBrowser.Controller.Authentication;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Security;
|
||||
using MediaBrowser.Model.Devices;
|
||||
using MediaBrowser.Model.Session;
|
||||
using MediaBrowser.Model.SyncPlay;
|
||||
|
||||
@@ -159,21 +157,21 @@ namespace MediaBrowser.Controller.Session
|
||||
/// <summary>
|
||||
/// Sends a SyncPlayCommand to a session.
|
||||
/// </summary>
|
||||
/// <param name="session">The session.</param>
|
||||
/// <param name="sessionId">The identifier of the session.</param>
|
||||
/// <param name="command">The command.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task SendSyncPlayCommand(SessionInfo session, SendCommand command, CancellationToken cancellationToken);
|
||||
Task SendSyncPlayCommand(string sessionId, SendCommand command, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a SyncPlayGroupUpdate to a session.
|
||||
/// </summary>
|
||||
/// <param name="session">The session.</param>
|
||||
/// <param name="sessionId">The identifier of the session.</param>
|
||||
/// <param name="command">The group update.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <typeparam name="T">Type of group.</typeparam>
|
||||
/// <returns>Task.</returns>
|
||||
Task SendSyncPlayGroupUpdate<T>(SessionInfo session, GroupUpdate<T> command, CancellationToken cancellationToken);
|
||||
Task SendSyncPlayGroupUpdate<T>(string sessionId, GroupUpdate<T> command, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Sends the browse command.
|
||||
|
||||
@@ -31,12 +31,14 @@ namespace MediaBrowser.Controller.Subtitles
|
||||
/// <param name="video">The video.</param>
|
||||
/// <param name="language">Subtitle language.</param>
|
||||
/// <param name="isPerfectMatch">Require perfect match.</param>
|
||||
/// <param name="isAutomated">Request is automated.</param>
|
||||
/// <param name="cancellationToken">CancellationToken to use for the operation.</param>
|
||||
/// <returns>Subtitles, wrapped in task.</returns>
|
||||
Task<RemoteSubtitleInfo[]> SearchSubtitles(
|
||||
Video video,
|
||||
string language,
|
||||
bool? isPerfectMatch,
|
||||
bool isAutomated,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -51,5 +51,7 @@ namespace MediaBrowser.Controller.Subtitles
|
||||
public string[] DisabledSubtitleFetchers { get; set; }
|
||||
|
||||
public string[] SubtitleFetcherOrder { get; set; }
|
||||
|
||||
public bool IsAutomated { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using MediaBrowser.Controller.Session;
|
||||
|
||||
namespace MediaBrowser.Controller.SyncPlay
|
||||
@@ -15,14 +16,28 @@ namespace MediaBrowser.Controller.SyncPlay
|
||||
/// <param name="session">The session.</param>
|
||||
public GroupMember(SessionInfo session)
|
||||
{
|
||||
Session = session;
|
||||
SessionId = session.Id;
|
||||
UserId = session.UserId;
|
||||
UserName = session.UserName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the session.
|
||||
/// Gets the identifier of the session.
|
||||
/// </summary>
|
||||
/// <value>The session.</value>
|
||||
public SessionInfo Session { get; }
|
||||
/// <value>The session identifier.</value>
|
||||
public string SessionId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the identifier of the user.
|
||||
/// </summary>
|
||||
/// <value>The user identifier.</value>
|
||||
public Guid UserId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the username.
|
||||
/// </summary>
|
||||
/// <value>The username.</value>
|
||||
public string UserName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ping, in milliseconds.
|
||||
|
||||
@@ -68,7 +68,16 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates
|
||||
/// <inheritdoc />
|
||||
public virtual void HandleRequest(RemoveFromPlaylistGroupRequest request, IGroupStateContext context, GroupStateType prevState, SessionInfo session, CancellationToken cancellationToken)
|
||||
{
|
||||
var playingItemRemoved = context.RemoveFromPlayQueue(request.PlaylistItemIds);
|
||||
bool playingItemRemoved;
|
||||
if (request.ClearPlaylist)
|
||||
{
|
||||
context.ClearPlayQueue(request.ClearPlayingItem);
|
||||
playingItemRemoved = request.ClearPlayingItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
playingItemRemoved = context.RemoveFromPlayQueue(request.PlaylistItemIds);
|
||||
}
|
||||
|
||||
var playQueueUpdate = context.GetPlayQueueUpdate(PlayQueueUpdateReason.RemoveItems);
|
||||
var update = context.NewSyncPlayGroupUpdate(GroupUpdateType.PlayQueue, playQueueUpdate);
|
||||
|
||||
@@ -162,6 +162,12 @@ namespace MediaBrowser.Controller.SyncPlay
|
||||
/// <returns><c>true</c> if the play queue has been changed; <c>false</c> if something went wrong.</returns>
|
||||
bool SetPlayingItem(Guid playlistItemId);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the play queue.
|
||||
/// </summary>
|
||||
/// <param name="clearPlayingItem">Whether to remove the playing item as well.</param>
|
||||
void ClearPlayQueue(bool clearPlayingItem);
|
||||
|
||||
/// <summary>
|
||||
/// Removes items from the play queue.
|
||||
/// </summary>
|
||||
|
||||
@@ -17,9 +17,13 @@ namespace MediaBrowser.Controller.SyncPlay.PlaybackRequests
|
||||
/// Initializes a new instance of the <see cref="RemoveFromPlaylistGroupRequest"/> class.
|
||||
/// </summary>
|
||||
/// <param name="items">The playlist ids of the items to remove.</param>
|
||||
public RemoveFromPlaylistGroupRequest(IReadOnlyList<Guid> items)
|
||||
/// <param name="clearPlaylist">Whether to clear the entire playlist. The items list will be ignored.</param>
|
||||
/// <param name="clearPlayingItem">Whether to remove the playing item as well. Used only when clearing the playlist.</param>
|
||||
public RemoveFromPlaylistGroupRequest(IReadOnlyList<Guid> items, bool clearPlaylist = false, bool clearPlayingItem = false)
|
||||
{
|
||||
PlaylistItemIds = items ?? Array.Empty<Guid>();
|
||||
ClearPlaylist = clearPlaylist;
|
||||
ClearPlayingItem = clearPlayingItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,6 +32,18 @@ namespace MediaBrowser.Controller.SyncPlay.PlaybackRequests
|
||||
/// <value>The playlist identifiers ot the items.</value>
|
||||
public IReadOnlyList<Guid> PlaylistItemIds { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the entire playlist should be cleared.
|
||||
/// </summary>
|
||||
/// <value>Whether the entire playlist should be cleared.</value>
|
||||
public bool ClearPlaylist { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the playing item should be removed as well.
|
||||
/// </summary>
|
||||
/// <value>Whether the playing item should be removed as well.</value>
|
||||
public bool ClearPlayingItem { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override PlaybackRequestType Action { get; } = PlaybackRequestType.RemoveFromPlaylist;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Model.SyncPlay;
|
||||
|
||||
namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
@@ -19,10 +20,16 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
private const int NoPlayingItemIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Random number generator used to shuffle lists.
|
||||
/// The sorted playlist.
|
||||
/// </summary>
|
||||
/// <value>The random number generator.</value>
|
||||
private readonly Random _randomNumberGenerator = new Random();
|
||||
/// <value>The sorted playlist, or play queue of the group.</value>
|
||||
private List<QueueItem> _sortedPlaylist = new List<QueueItem>();
|
||||
|
||||
/// <summary>
|
||||
/// The shuffled playlist.
|
||||
/// </summary>
|
||||
/// <value>The shuffled playlist, or play queue of the group.</value>
|
||||
private List<QueueItem> _shuffledPlaylist = new List<QueueItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlayQueueManager" /> class.
|
||||
@@ -56,18 +63,6 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
/// <value>The repeat mode.</value>
|
||||
public GroupRepeatMode RepeatMode { get; private set; } = GroupRepeatMode.RepeatNone;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sorted playlist.
|
||||
/// </summary>
|
||||
/// <value>The sorted playlist, or play queue of the group.</value>
|
||||
private List<QueueItem> SortedPlaylist { get; set; } = new List<QueueItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the shuffled playlist.
|
||||
/// </summary>
|
||||
/// <value>The shuffled playlist, or play queue of the group.</value>
|
||||
private List<QueueItem> ShuffledPlaylist { get; set; } = new List<QueueItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an item is playing.
|
||||
/// </summary>
|
||||
@@ -92,14 +87,14 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
/// <param name="items">The new items of the playlist.</param>
|
||||
public void SetPlaylist(IReadOnlyList<Guid> items)
|
||||
{
|
||||
SortedPlaylist.Clear();
|
||||
ShuffledPlaylist.Clear();
|
||||
_sortedPlaylist.Clear();
|
||||
_shuffledPlaylist.Clear();
|
||||
|
||||
SortedPlaylist = CreateQueueItemsFromArray(items);
|
||||
_sortedPlaylist = CreateQueueItemsFromArray(items);
|
||||
if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
|
||||
{
|
||||
ShuffledPlaylist = new List<QueueItem>(SortedPlaylist);
|
||||
Shuffle(ShuffledPlaylist);
|
||||
_shuffledPlaylist = new List<QueueItem>(_sortedPlaylist);
|
||||
_shuffledPlaylist.Shuffle();
|
||||
}
|
||||
|
||||
PlayingItemIndex = NoPlayingItemIndex;
|
||||
@@ -114,10 +109,10 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
var newItems = CreateQueueItemsFromArray(items);
|
||||
|
||||
SortedPlaylist.AddRange(newItems);
|
||||
_sortedPlaylist.AddRange(newItems);
|
||||
if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
|
||||
{
|
||||
ShuffledPlaylist.AddRange(newItems);
|
||||
_shuffledPlaylist.AddRange(newItems);
|
||||
}
|
||||
|
||||
LastChange = DateTime.UtcNow;
|
||||
@@ -130,26 +125,26 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
if (PlayingItemIndex == NoPlayingItemIndex)
|
||||
{
|
||||
ShuffledPlaylist = new List<QueueItem>(SortedPlaylist);
|
||||
Shuffle(ShuffledPlaylist);
|
||||
_shuffledPlaylist = new List<QueueItem>(_sortedPlaylist);
|
||||
_shuffledPlaylist.Shuffle();
|
||||
}
|
||||
else if (ShuffleMode.Equals(GroupShuffleMode.Sorted))
|
||||
{
|
||||
// First time shuffle.
|
||||
var playingItem = SortedPlaylist[PlayingItemIndex];
|
||||
ShuffledPlaylist = new List<QueueItem>(SortedPlaylist);
|
||||
ShuffledPlaylist.RemoveAt(PlayingItemIndex);
|
||||
Shuffle(ShuffledPlaylist);
|
||||
ShuffledPlaylist.Insert(0, playingItem);
|
||||
var playingItem = _sortedPlaylist[PlayingItemIndex];
|
||||
_shuffledPlaylist = new List<QueueItem>(_sortedPlaylist);
|
||||
_shuffledPlaylist.RemoveAt(PlayingItemIndex);
|
||||
_shuffledPlaylist.Shuffle();
|
||||
_shuffledPlaylist.Insert(0, playingItem);
|
||||
PlayingItemIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Re-shuffle playlist.
|
||||
var playingItem = ShuffledPlaylist[PlayingItemIndex];
|
||||
ShuffledPlaylist.RemoveAt(PlayingItemIndex);
|
||||
Shuffle(ShuffledPlaylist);
|
||||
ShuffledPlaylist.Insert(0, playingItem);
|
||||
var playingItem = _shuffledPlaylist[PlayingItemIndex];
|
||||
_shuffledPlaylist.RemoveAt(PlayingItemIndex);
|
||||
_shuffledPlaylist.Shuffle();
|
||||
_shuffledPlaylist.Insert(0, playingItem);
|
||||
PlayingItemIndex = 0;
|
||||
}
|
||||
|
||||
@@ -164,11 +159,11 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
if (PlayingItemIndex != NoPlayingItemIndex)
|
||||
{
|
||||
var playingItem = ShuffledPlaylist[PlayingItemIndex];
|
||||
PlayingItemIndex = SortedPlaylist.IndexOf(playingItem);
|
||||
var playingItem = _shuffledPlaylist[PlayingItemIndex];
|
||||
PlayingItemIndex = _sortedPlaylist.IndexOf(playingItem);
|
||||
}
|
||||
|
||||
ShuffledPlaylist.Clear();
|
||||
_shuffledPlaylist.Clear();
|
||||
|
||||
ShuffleMode = GroupShuffleMode.Sorted;
|
||||
LastChange = DateTime.UtcNow;
|
||||
@@ -181,16 +176,16 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
public void ClearPlaylist(bool clearPlayingItem)
|
||||
{
|
||||
var playingItem = GetPlayingItem();
|
||||
SortedPlaylist.Clear();
|
||||
ShuffledPlaylist.Clear();
|
||||
_sortedPlaylist.Clear();
|
||||
_shuffledPlaylist.Clear();
|
||||
LastChange = DateTime.UtcNow;
|
||||
|
||||
if (!clearPlayingItem && playingItem != null)
|
||||
{
|
||||
SortedPlaylist.Add(playingItem);
|
||||
_sortedPlaylist.Add(playingItem);
|
||||
if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
|
||||
{
|
||||
ShuffledPlaylist.Add(playingItem);
|
||||
_shuffledPlaylist.Add(playingItem);
|
||||
}
|
||||
|
||||
PlayingItemIndex = 0;
|
||||
@@ -212,14 +207,14 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
|
||||
{
|
||||
var playingItem = GetPlayingItem();
|
||||
var sortedPlayingItemIndex = SortedPlaylist.IndexOf(playingItem);
|
||||
var sortedPlayingItemIndex = _sortedPlaylist.IndexOf(playingItem);
|
||||
// Append items to sorted and shuffled playlist as they are.
|
||||
SortedPlaylist.InsertRange(sortedPlayingItemIndex + 1, newItems);
|
||||
ShuffledPlaylist.InsertRange(PlayingItemIndex + 1, newItems);
|
||||
_sortedPlaylist.InsertRange(sortedPlayingItemIndex + 1, newItems);
|
||||
_shuffledPlaylist.InsertRange(PlayingItemIndex + 1, newItems);
|
||||
}
|
||||
else
|
||||
{
|
||||
SortedPlaylist.InsertRange(PlayingItemIndex + 1, newItems);
|
||||
_sortedPlaylist.InsertRange(PlayingItemIndex + 1, newItems);
|
||||
}
|
||||
|
||||
LastChange = DateTime.UtcNow;
|
||||
@@ -298,8 +293,8 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
var playingItem = GetPlayingItem();
|
||||
|
||||
SortedPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId));
|
||||
ShuffledPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId));
|
||||
_sortedPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId));
|
||||
_shuffledPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId));
|
||||
|
||||
LastChange = DateTime.UtcNow;
|
||||
|
||||
@@ -313,7 +308,7 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
// Was first element, picking next if available.
|
||||
// Default to no playing item otherwise.
|
||||
PlayingItemIndex = SortedPlaylist.Count > 0 ? 0 : NoPlayingItemIndex;
|
||||
PlayingItemIndex = _sortedPlaylist.Count > 0 ? 0 : NoPlayingItemIndex;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -363,8 +358,8 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
SortedPlaylist.Clear();
|
||||
ShuffledPlaylist.Clear();
|
||||
_sortedPlaylist.Clear();
|
||||
_shuffledPlaylist.Clear();
|
||||
PlayingItemIndex = NoPlayingItemIndex;
|
||||
ShuffleMode = GroupShuffleMode.Sorted;
|
||||
RepeatMode = GroupRepeatMode.RepeatNone;
|
||||
@@ -460,7 +455,7 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
}
|
||||
|
||||
PlayingItemIndex++;
|
||||
if (PlayingItemIndex >= SortedPlaylist.Count)
|
||||
if (PlayingItemIndex >= _sortedPlaylist.Count)
|
||||
{
|
||||
if (RepeatMode.Equals(GroupRepeatMode.RepeatAll))
|
||||
{
|
||||
@@ -468,7 +463,7 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayingItemIndex = SortedPlaylist.Count - 1;
|
||||
PlayingItemIndex = _sortedPlaylist.Count - 1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -494,7 +489,7 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
if (RepeatMode.Equals(GroupRepeatMode.RepeatAll))
|
||||
{
|
||||
PlayingItemIndex = SortedPlaylist.Count - 1;
|
||||
PlayingItemIndex = _sortedPlaylist.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -507,23 +502,6 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuffles a given list.
|
||||
/// </summary>
|
||||
/// <param name="list">The list to shuffle.</param>
|
||||
private void Shuffle<T>(IList<T> list)
|
||||
{
|
||||
int n = list.Count;
|
||||
while (n > 1)
|
||||
{
|
||||
n--;
|
||||
int k = _randomNumberGenerator.Next(n + 1);
|
||||
T value = list[k];
|
||||
list[k] = list[n];
|
||||
list[n] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a list from the array of items. Each item is given an unique playlist identifier.
|
||||
/// </summary>
|
||||
@@ -548,11 +526,11 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
|
||||
{
|
||||
return ShuffledPlaylist;
|
||||
return _shuffledPlaylist;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SortedPlaylist;
|
||||
return _sortedPlaylist;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,11 +546,11 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
}
|
||||
else if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
|
||||
{
|
||||
return ShuffledPlaylist[PlayingItemIndex];
|
||||
return _shuffledPlaylist[PlayingItemIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
return SortedPlaylist[PlayingItemIndex];
|
||||
return _sortedPlaylist[PlayingItemIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user