Merge remote-tracking branch 'remotes/jellyfin/api-migration' into api-audio

# Conflicts:
#	Emby.Server.Implementations/ApplicationHost.cs
#	Jellyfin.Api/Helpers/TranscodingJobHelper.cs
This commit is contained in:
David
2020-07-22 11:02:44 +02:00
724 changed files with 8326 additions and 5490 deletions

View File

@@ -43,6 +43,7 @@ using Emby.Server.Implementations.Security;
using Emby.Server.Implementations.Serialization;
using Emby.Server.Implementations.Services;
using Emby.Server.Implementations.Session;
using Emby.Server.Implementations.SyncPlay;
using Emby.Server.Implementations.TV;
using Emby.Server.Implementations.Updates;
using Emby.Server.Implementations.SyncPlay;
@@ -79,8 +80,8 @@ using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Controller.TV;
using MediaBrowser.Controller.SyncPlay;
using MediaBrowser.Controller.TV;
using MediaBrowser.LocalMetadata.Savers;
using MediaBrowser.MediaEncoding.BdInfo;
using MediaBrowser.Model.Configuration;
@@ -484,12 +485,10 @@ namespace Emby.Server.Implementations
foreach (var plugin in Plugins)
{
pluginBuilder.AppendLine(
string.Format(
CultureInfo.InvariantCulture,
"{0} {1}",
plugin.Name,
plugin.Version));
pluginBuilder.Append(plugin.Name)
.Append(' ')
.Append(plugin.Version)
.AppendLine();
}
Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString());
@@ -566,10 +565,8 @@ namespace Emby.Server.Implementations
serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
// TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation
serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
serviceCollection.AddSingleton<IMediaEncoder>(provider =>
ActivatorUtilities.CreateInstance<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(provider, _startupOptions.FFmpegPath ?? string.Empty));
serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
// TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
@@ -875,6 +872,11 @@ namespace Emby.Server.Implementations
Logger.LogError(ex, "Error getting exported types from {Assembly}", ass.FullName);
continue;
}
catch (TypeLoadException ex)
{
Logger.LogError(ex, "Error loading types from {Assembly}.", ass.FullName);
continue;
}
foreach (Type type in exportedTypes)
{
@@ -958,7 +960,7 @@ namespace Emby.Server.Implementations
}
/// <summary>
/// Notifies that the kernel that a change has been made that requires a restart
/// Notifies that the kernel that a change has been made that requires a restart.
/// </summary>
public void NotifyPendingRestart()
{
@@ -1154,7 +1156,7 @@ namespace Emby.Server.Implementations
return null;
}
return GetLocalApiUrl(addresses.First());
return GetLocalApiUrl(addresses[0]);
}
catch (Exception ex)
{
@@ -1227,13 +1229,13 @@ namespace Emby.Server.Implementations
var addresses = ServerConfigurationManager
.Configuration
.LocalNetworkAddresses
.Select(NormalizeConfiguredLocalAddress)
.Select(x => NormalizeConfiguredLocalAddress(x))
.Where(i => i != null)
.ToList();
if (addresses.Count == 0)
{
addresses.AddRange(_networkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces));
addresses.AddRange(_networkManager.GetLocalIpAddresses());
}
var resultList = new List<IPAddress>();
@@ -1248,8 +1250,7 @@ namespace Emby.Server.Implementations
}
}
var valid = await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false);
if (valid)
if (await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false))
{
resultList.Add(address);
@@ -1263,13 +1264,12 @@ namespace Emby.Server.Implementations
return resultList;
}
public IPAddress NormalizeConfiguredLocalAddress(string address)
public IPAddress NormalizeConfiguredLocalAddress(ReadOnlySpan<char> address)
{
var index = address.Trim('/').IndexOf('/');
if (index != -1)
{
address = address.Substring(index + 1);
address = address.Slice(index + 1);
}
if (IPAddress.TryParse(address.Trim('/'), out IPAddress result))

View File

@@ -1072,7 +1072,7 @@ namespace Emby.Server.Implementations.Channels
}
// was used for status
//if (!string.Equals(item.ExternalEtag ?? string.Empty, info.Etag ?? string.Empty, StringComparison.Ordinal))
// if (!string.Equals(item.ExternalEtag ?? string.Empty, info.Etag ?? string.Empty, StringComparison.Ordinal))
//{
// item.ExternalEtag = info.Etag;
// forceUpdate = true;

View File

@@ -363,60 +363,4 @@ namespace Emby.Server.Implementations.Collections
return results.Values;
}
}
/// <summary>
/// The collection manager entry point.
/// </summary>
public sealed class CollectionManagerEntryPoint : IServerEntryPoint
{
private readonly CollectionManager _collectionManager;
private readonly IServerConfigurationManager _config;
private readonly ILogger<CollectionManagerEntryPoint> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="CollectionManagerEntryPoint"/> class.
/// </summary>
/// <param name="collectionManager">The collection manager.</param>
/// <param name="config">The server configuration manager.</param>
/// <param name="logger">The logger.</param>
public CollectionManagerEntryPoint(
ICollectionManager collectionManager,
IServerConfigurationManager config,
ILogger<CollectionManagerEntryPoint> logger)
{
_collectionManager = (CollectionManager)collectionManager;
_config = config;
_logger = logger;
}
/// <inheritdoc />
public async Task RunAsync()
{
if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted)
{
var path = _collectionManager.GetCollectionsFolderPath();
if (Directory.Exists(path))
{
try
{
await _collectionManager.EnsureLibraryFolder(path, true).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating camera uploads library");
}
_config.Configuration.CollectionsUpgraded = true;
_config.SaveConfiguration();
}
}
}
/// <inheritdoc />
public void Dispose()
{
// Nothing to dispose
}
}
}

View File

@@ -109,7 +109,6 @@ namespace Emby.Server.Implementations.Configuration
if (!string.IsNullOrWhiteSpace(newPath)
&& !string.Equals(Configuration.CertificatePath, newPath, StringComparison.Ordinal))
{
// Validate
if (!File.Exists(newPath))
{
throw new FileNotFoundException(
@@ -133,7 +132,6 @@ namespace Emby.Server.Implementations.Configuration
if (!string.IsNullOrWhiteSpace(newPath)
&& !string.Equals(Configuration.MetadataPath, newPath, StringComparison.Ordinal))
{
// Validate
if (!Directory.Exists(newPath))
{
throw new DirectoryNotFoundException(
@@ -146,60 +144,5 @@ namespace Emby.Server.Implementations.Configuration
EnsureWriteAccess(newPath);
}
}
/// <summary>
/// Sets all configuration values to their optimal values.
/// </summary>
/// <returns>If the configuration changed.</returns>
public bool SetOptimalValues()
{
var config = Configuration;
var changed = false;
if (!config.EnableCaseSensitiveItemIds)
{
config.EnableCaseSensitiveItemIds = true;
changed = true;
}
if (!config.SkipDeserializationForBasicTypes)
{
config.SkipDeserializationForBasicTypes = true;
changed = true;
}
if (!config.EnableSimpleArtistDetection)
{
config.EnableSimpleArtistDetection = true;
changed = true;
}
if (!config.EnableNormalizedItemByNameIds)
{
config.EnableNormalizedItemByNameIds = true;
changed = true;
}
if (!config.DisableLiveTvChannelUserDataName)
{
config.DisableLiveTvChannelUserDataName = true;
changed = true;
}
if (!config.EnableNewOmdbSupport)
{
config.EnableNewOmdbSupport = true;
changed = true;
}
if (!config.CollectionsUpgraded)
{
config.CollectionsUpgraded = true;
changed = true;
}
return changed;
}
}
}

View File

@@ -17,7 +17,6 @@ namespace Emby.Server.Implementations
{
{ HostWebClientKey, bool.TrueString },
{ HttpListenerHost.DefaultRedirectKey, "web/index.html" },
{ InstallationManager.PluginManifestUrlKey, "https://repo.jellyfin.org/releases/plugin/manifest-stable.json" },
{ FfmpegProbeSizeKey, "1G" },
{ FfmpegAnalyzeDurationKey, "200M" },
{ PlaylistsAllowDuplicatesKey, bool.TrueString }

View File

@@ -162,7 +162,6 @@ namespace Emby.Server.Implementations.Data
}
return false;
}, ReadTransactionMode);
}
@@ -248,12 +247,12 @@ namespace Emby.Server.Implementations.Data
public enum SynchronousMode
{
/// <summary>
/// SQLite continues without syncing as soon as it has handed data off to the operating system
/// SQLite continues without syncing as soon as it has handed data off to the operating system.
/// </summary>
Off = 0,
/// <summary>
/// SQLite database engine will still sync at the most critical moments
/// SQLite database engine will still sync at the most critical moments.
/// </summary>
Normal = 1,

View File

@@ -51,7 +51,6 @@ namespace Emby.Server.Implementations.Data
_libraryManager.DeleteItem(item, new DeleteOptions
{
DeleteFileLocation = false
});
}

View File

@@ -59,7 +59,7 @@ namespace Emby.Server.Implementations.Data
}
/// <summary>
/// Opens the connection to the database
/// Opens the connection to the database.
/// </summary>
/// <returns>Task.</returns>
private void InitializeInternal()
@@ -77,7 +77,7 @@ namespace Emby.Server.Implementations.Data
}
/// <summary>
/// Save the display preferences associated with an item in the repo
/// Save the display preferences associated with an item in the repo.
/// </summary>
/// <param name="displayPreferences">The display preferences.</param>
/// <param name="userId">The user id.</param>
@@ -122,7 +122,7 @@ namespace Emby.Server.Implementations.Data
}
/// <summary>
/// Save all display preferences associated with a user in the repo
/// Save all display preferences associated with a user in the repo.
/// </summary>
/// <param name="displayPreferences">The display preferences.</param>
/// <param name="userId">The user id.</param>

View File

@@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Data
protected override TempStoreMode TempStore => TempStoreMode.Memory;
/// <summary>
/// Opens the connection to the database
/// Opens the connection to the database.
/// </summary>
public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager)
{
@@ -321,7 +321,6 @@ namespace Emby.Server.Implementations.Data
AddColumn(db, "MediaStreams", "ColorPrimaries", "TEXT", existingColumnNames);
AddColumn(db, "MediaStreams", "ColorSpace", "TEXT", existingColumnNames);
AddColumn(db, "MediaStreams", "ColorTransfer", "TEXT", existingColumnNames);
}, TransactionMode);
connection.RunQueries(postQueries);
@@ -549,7 +548,7 @@ namespace Emby.Server.Implementations.Data
}
/// <summary>
/// Save a standard item in the repo
/// Save a standard item in the repo.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
@@ -794,6 +793,7 @@ namespace Emby.Server.Implementations.Data
{
saveItemStatement.TryBindNull("@Width");
}
if (item.Height > 0)
{
saveItemStatement.TryBind("@Height", item.Height);
@@ -933,6 +933,7 @@ namespace Emby.Server.Implementations.Data
{
saveItemStatement.TryBindNull("@SeriesName");
}
if (string.IsNullOrWhiteSpace(userDataKey))
{
saveItemStatement.TryBindNull("@UserDataKey");
@@ -1008,6 +1009,7 @@ namespace Emby.Server.Implementations.Data
{
artists = string.Join("|", hasArtists.Artists);
}
saveItemStatement.TryBind("@Artists", artists);
string albumArtists = null;
@@ -1107,6 +1109,7 @@ namespace Emby.Server.Implementations.Data
{
continue;
}
str.Append(ToValueString(i) + "|");
}
@@ -1205,7 +1208,7 @@ namespace Emby.Server.Implementations.Data
}
/// <summary>
/// Internal retrieve from items or users table
/// Internal retrieve from items or users table.
/// </summary>
/// <param name="id">The id.</param>
/// <returns>BaseItem.</returns>
@@ -1367,6 +1370,7 @@ namespace Emby.Server.Implementations.Data
hasStartDate.StartDate = reader[index].ReadDateTime();
}
}
index++;
}
@@ -1374,12 +1378,14 @@ namespace Emby.Server.Implementations.Data
{
item.EndDate = reader[index].TryReadDateTime();
}
index++;
if (!reader.IsDBNull(index))
{
item.ChannelId = new Guid(reader.GetString(index));
}
index++;
if (enableProgramAttributes)
@@ -1390,24 +1396,28 @@ namespace Emby.Server.Implementations.Data
{
hasProgramAttributes.IsMovie = reader.GetBoolean(index);
}
index++;
if (!reader.IsDBNull(index))
{
hasProgramAttributes.IsSeries = reader.GetBoolean(index);
}
index++;
if (!reader.IsDBNull(index))
{
hasProgramAttributes.EpisodeTitle = reader.GetString(index);
}
index++;
if (!reader.IsDBNull(index))
{
hasProgramAttributes.IsRepeat = reader.GetBoolean(index);
}
index++;
}
else
@@ -1420,6 +1430,7 @@ namespace Emby.Server.Implementations.Data
{
item.CommunityRating = reader.GetFloat(index);
}
index++;
if (HasField(query, ItemFields.CustomRating))
@@ -1428,6 +1439,7 @@ namespace Emby.Server.Implementations.Data
{
item.CustomRating = reader.GetString(index);
}
index++;
}
@@ -1435,6 +1447,7 @@ namespace Emby.Server.Implementations.Data
{
item.IndexNumber = reader.GetInt32(index);
}
index++;
if (HasField(query, ItemFields.Settings))
@@ -1443,18 +1456,21 @@ namespace Emby.Server.Implementations.Data
{
item.IsLocked = reader.GetBoolean(index);
}
index++;
if (!reader.IsDBNull(index))
{
item.PreferredMetadataLanguage = reader.GetString(index);
}
index++;
if (!reader.IsDBNull(index))
{
item.PreferredMetadataCountryCode = reader.GetString(index);
}
index++;
}
@@ -1464,6 +1480,7 @@ namespace Emby.Server.Implementations.Data
{
item.Width = reader.GetInt32(index);
}
index++;
}
@@ -1473,6 +1490,7 @@ namespace Emby.Server.Implementations.Data
{
item.Height = reader.GetInt32(index);
}
index++;
}
@@ -1482,6 +1500,7 @@ namespace Emby.Server.Implementations.Data
{
item.DateLastRefreshed = reader[index].ReadDateTime();
}
index++;
}
@@ -1489,18 +1508,21 @@ namespace Emby.Server.Implementations.Data
{
item.Name = reader.GetString(index);
}
index++;
if (!reader.IsDBNull(index))
{
item.Path = RestorePath(reader.GetString(index));
}
index++;
if (!reader.IsDBNull(index))
{
item.PremiereDate = reader[index].TryReadDateTime();
}
index++;
if (HasField(query, ItemFields.Overview))
@@ -1509,6 +1531,7 @@ namespace Emby.Server.Implementations.Data
{
item.Overview = reader.GetString(index);
}
index++;
}
@@ -1516,18 +1539,21 @@ namespace Emby.Server.Implementations.Data
{
item.ParentIndexNumber = reader.GetInt32(index);
}
index++;
if (!reader.IsDBNull(index))
{
item.ProductionYear = reader.GetInt32(index);
}
index++;
if (!reader.IsDBNull(index))
{
item.OfficialRating = reader.GetString(index);
}
index++;
if (HasField(query, ItemFields.SortName))
@@ -1536,6 +1562,7 @@ namespace Emby.Server.Implementations.Data
{
item.ForcedSortName = reader.GetString(index);
}
index++;
}
@@ -1543,12 +1570,14 @@ namespace Emby.Server.Implementations.Data
{
item.RunTimeTicks = reader.GetInt64(index);
}
index++;
if (!reader.IsDBNull(index))
{
item.Size = reader.GetInt64(index);
}
index++;
if (HasField(query, ItemFields.DateCreated))
@@ -1557,6 +1586,7 @@ namespace Emby.Server.Implementations.Data
{
item.DateCreated = reader[index].ReadDateTime();
}
index++;
}
@@ -1564,6 +1594,7 @@ namespace Emby.Server.Implementations.Data
{
item.DateModified = reader[index].ReadDateTime();
}
index++;
item.Id = reader.GetGuid(index);
@@ -1575,6 +1606,7 @@ namespace Emby.Server.Implementations.Data
{
item.Genres = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
}
index++;
}
@@ -1582,6 +1614,7 @@ namespace Emby.Server.Implementations.Data
{
item.ParentId = reader.GetGuid(index);
}
index++;
if (!reader.IsDBNull(index))
@@ -1591,6 +1624,7 @@ namespace Emby.Server.Implementations.Data
item.Audio = audio;
}
}
index++;
// TODO: Even if not needed by apps, the server needs it internally
@@ -1604,6 +1638,7 @@ namespace Emby.Server.Implementations.Data
liveTvChannel.ServiceName = reader.GetString(index);
}
}
index++;
}
@@ -1611,6 +1646,7 @@ namespace Emby.Server.Implementations.Data
{
item.IsInMixedFolder = reader.GetBoolean(index);
}
index++;
if (HasField(query, ItemFields.DateLastSaved))
@@ -1619,6 +1655,7 @@ namespace Emby.Server.Implementations.Data
{
item.DateLastSaved = reader[index].ReadDateTime();
}
index++;
}
@@ -1636,8 +1673,10 @@ namespace Emby.Server.Implementations.Data
}
}
}
item.LockedFields = GetLockedFields(reader.GetString(index)).ToArray();
}
index++;
}
@@ -1647,6 +1686,7 @@ namespace Emby.Server.Implementations.Data
{
item.Studios = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
}
index++;
}
@@ -1656,6 +1696,7 @@ namespace Emby.Server.Implementations.Data
{
item.Tags = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
}
index++;
}
@@ -1675,9 +1716,11 @@ namespace Emby.Server.Implementations.Data
}
}
}
trailer.TrailerTypes = GetTrailerTypes(reader.GetString(index)).ToArray();
}
}
index++;
}
@@ -1687,6 +1730,7 @@ namespace Emby.Server.Implementations.Data
{
item.OriginalTitle = reader.GetString(index);
}
index++;
}
@@ -1697,6 +1741,7 @@ namespace Emby.Server.Implementations.Data
video.PrimaryVersionId = reader.GetString(index);
}
}
index++;
if (HasField(query, ItemFields.DateLastMediaAdded))
@@ -1705,6 +1750,7 @@ namespace Emby.Server.Implementations.Data
{
folder.DateLastMediaAdded = reader[index].TryReadDateTime();
}
index++;
}
@@ -1712,18 +1758,21 @@ namespace Emby.Server.Implementations.Data
{
item.Album = reader.GetString(index);
}
index++;
if (!reader.IsDBNull(index))
{
item.CriticRating = reader.GetFloat(index);
}
index++;
if (!reader.IsDBNull(index))
{
item.IsVirtualItem = reader.GetBoolean(index);
}
index++;
if (item is IHasSeries hasSeriesName)
@@ -1733,6 +1782,7 @@ namespace Emby.Server.Implementations.Data
hasSeriesName.SeriesName = reader.GetString(index);
}
}
index++;
if (hasEpisodeAttributes)
@@ -1743,6 +1793,7 @@ namespace Emby.Server.Implementations.Data
{
episode.SeasonName = reader.GetString(index);
}
index++;
if (!reader.IsDBNull(index))
{
@@ -1753,6 +1804,7 @@ namespace Emby.Server.Implementations.Data
{
index++;
}
index++;
}
@@ -1766,6 +1818,7 @@ namespace Emby.Server.Implementations.Data
hasSeries.SeriesId = reader.GetGuid(index);
}
}
index++;
}
@@ -1775,6 +1828,7 @@ namespace Emby.Server.Implementations.Data
{
item.PresentationUniqueKey = reader.GetString(index);
}
index++;
}
@@ -1784,6 +1838,7 @@ namespace Emby.Server.Implementations.Data
{
item.InheritedParentalRatingValue = reader.GetInt32(index);
}
index++;
}
@@ -1793,6 +1848,7 @@ namespace Emby.Server.Implementations.Data
{
item.ExternalSeriesId = reader.GetString(index);
}
index++;
}
@@ -1802,6 +1858,7 @@ namespace Emby.Server.Implementations.Data
{
item.Tagline = reader.GetString(index);
}
index++;
}
@@ -1809,6 +1866,7 @@ namespace Emby.Server.Implementations.Data
{
DeserializeProviderIds(reader.GetString(index), item);
}
index++;
if (query.DtoOptions.EnableImages)
@@ -1817,6 +1875,7 @@ namespace Emby.Server.Implementations.Data
{
DeserializeImages(reader.GetString(index), item);
}
index++;
}
@@ -1826,6 +1885,7 @@ namespace Emby.Server.Implementations.Data
{
item.ProductionLocations = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).ToArray();
}
index++;
}
@@ -1835,6 +1895,7 @@ namespace Emby.Server.Implementations.Data
{
item.ExtraIds = SplitToGuids(reader.GetString(index));
}
index++;
}
@@ -1842,6 +1903,7 @@ namespace Emby.Server.Implementations.Data
{
item.TotalBitrate = reader.GetInt32(index);
}
index++;
if (!reader.IsDBNull(index))
@@ -1851,6 +1913,7 @@ namespace Emby.Server.Implementations.Data
item.ExtraType = extraType;
}
}
index++;
if (hasArtistFields)
@@ -1859,12 +1922,14 @@ namespace Emby.Server.Implementations.Data
{
hasArtists.Artists = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
}
index++;
if (item is IHasAlbumArtist hasAlbumArtists && !reader.IsDBNull(index))
{
hasAlbumArtists.AlbumArtists = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
}
index++;
}
@@ -1872,6 +1937,7 @@ namespace Emby.Server.Implementations.Data
{
item.ExternalId = reader.GetString(index);
}
index++;
if (HasField(query, ItemFields.SeriesPresentationUniqueKey))
@@ -1883,6 +1949,7 @@ namespace Emby.Server.Implementations.Data
hasSeries.SeriesPresentationUniqueKey = reader.GetString(index);
}
}
index++;
}
@@ -1892,6 +1959,7 @@ namespace Emby.Server.Implementations.Data
{
program.ShowId = reader.GetString(index);
}
index++;
}
@@ -1899,6 +1967,7 @@ namespace Emby.Server.Implementations.Data
{
item.OwnerId = reader.GetGuid(index);
}
index++;
return item;
@@ -1919,7 +1988,7 @@ namespace Emby.Server.Implementations.Data
}
/// <summary>
/// Gets chapters for an item
/// Gets chapters for an item.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>IEnumerable{ChapterInfo}.</returns>
@@ -1947,7 +2016,7 @@ namespace Emby.Server.Implementations.Data
}
/// <summary>
/// Gets a single chapter for an item
/// Gets a single chapter for an item.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="index">The index.</param>
@@ -2044,7 +2113,6 @@ namespace Emby.Server.Implementations.Data
db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob);
InsertChapters(idBlob, chapters, db);
}, TransactionMode);
}
}
@@ -2475,6 +2543,7 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind("@SearchTermStartsWith", searchTerm + "%");
}
if (commandText.IndexOf("@SearchTermContains", StringComparison.OrdinalIgnoreCase) != -1)
{
statement.TryBind("@SearchTermContains", "%" + searchTerm + "%");
@@ -2706,22 +2775,85 @@ namespace Emby.Server.Implementations.Data
private string FixUnicodeChars(string buffer)
{
if (buffer.IndexOf('\u2013') > -1) buffer = buffer.Replace('\u2013', '-'); // en dash
if (buffer.IndexOf('\u2014') > -1) buffer = buffer.Replace('\u2014', '-'); // em dash
if (buffer.IndexOf('\u2015') > -1) buffer = buffer.Replace('\u2015', '-'); // horizontal bar
if (buffer.IndexOf('\u2017') > -1) buffer = buffer.Replace('\u2017', '_'); // double low line
if (buffer.IndexOf('\u2018') > -1) buffer = buffer.Replace('\u2018', '\''); // left single quotation mark
if (buffer.IndexOf('\u2019') > -1) buffer = buffer.Replace('\u2019', '\''); // right single quotation mark
if (buffer.IndexOf('\u201a') > -1) buffer = buffer.Replace('\u201a', ','); // single low-9 quotation mark
if (buffer.IndexOf('\u201b') > -1) buffer = buffer.Replace('\u201b', '\''); // single high-reversed-9 quotation mark
if (buffer.IndexOf('\u201c') > -1) buffer = buffer.Replace('\u201c', '\"'); // left double quotation mark
if (buffer.IndexOf('\u201d') > -1) buffer = buffer.Replace('\u201d', '\"'); // right double quotation mark
if (buffer.IndexOf('\u201e') > -1) buffer = buffer.Replace('\u201e', '\"'); // double low-9 quotation mark
if (buffer.IndexOf('\u2026') > -1) buffer = buffer.Replace("\u2026", "..."); // horizontal ellipsis
if (buffer.IndexOf('\u2032') > -1) buffer = buffer.Replace('\u2032', '\''); // prime
if (buffer.IndexOf('\u2033') > -1) buffer = buffer.Replace('\u2033', '\"'); // double prime
if (buffer.IndexOf('\u0060') > -1) buffer = buffer.Replace('\u0060', '\''); // grave accent
if (buffer.IndexOf('\u00B4') > -1) buffer = buffer.Replace('\u00B4', '\''); // acute accent
if (buffer.IndexOf('\u2013') > -1)
{
buffer = buffer.Replace('\u2013', '-'); // en dash
}
if (buffer.IndexOf('\u2014') > -1)
{
buffer = buffer.Replace('\u2014', '-'); // em dash
}
if (buffer.IndexOf('\u2015') > -1)
{
buffer = buffer.Replace('\u2015', '-'); // horizontal bar
}
if (buffer.IndexOf('\u2017') > -1)
{
buffer = buffer.Replace('\u2017', '_'); // double low line
}
if (buffer.IndexOf('\u2018') > -1)
{
buffer = buffer.Replace('\u2018', '\''); // left single quotation mark
}
if (buffer.IndexOf('\u2019') > -1)
{
buffer = buffer.Replace('\u2019', '\''); // right single quotation mark
}
if (buffer.IndexOf('\u201a') > -1)
{
buffer = buffer.Replace('\u201a', ','); // single low-9 quotation mark
}
if (buffer.IndexOf('\u201b') > -1)
{
buffer = buffer.Replace('\u201b', '\''); // single high-reversed-9 quotation mark
}
if (buffer.IndexOf('\u201c') > -1)
{
buffer = buffer.Replace('\u201c', '\"'); // left double quotation mark
}
if (buffer.IndexOf('\u201d') > -1)
{
buffer = buffer.Replace('\u201d', '\"'); // right double quotation mark
}
if (buffer.IndexOf('\u201e') > -1)
{
buffer = buffer.Replace('\u201e', '\"'); // double low-9 quotation mark
}
if (buffer.IndexOf('\u2026') > -1)
{
buffer = buffer.Replace("\u2026", "..."); // horizontal ellipsis
}
if (buffer.IndexOf('\u2032') > -1)
{
buffer = buffer.Replace('\u2032', '\''); // prime
}
if (buffer.IndexOf('\u2033') > -1)
{
buffer = buffer.Replace('\u2033', '\"'); // double prime
}
if (buffer.IndexOf('\u0060') > -1)
{
buffer = buffer.Replace('\u0060', '\''); // grave accent
}
if (buffer.IndexOf('\u00B4') > -1)
{
buffer = buffer.Replace('\u00B4', '\''); // acute accent
}
return buffer;
}
@@ -2745,6 +2877,7 @@ namespace Emby.Server.Implementations.Data
{
items[i] = newItem;
}
return;
}
}
@@ -2837,6 +2970,7 @@ namespace Emby.Server.Implementations.Data
{
statementTexts.Add(commandText);
}
if (query.EnableTotalRecordCount)
{
commandText = string.Empty;
@@ -3241,6 +3375,7 @@ namespace Emby.Server.Implementations.Data
{
statementTexts.Add(commandText);
}
if (query.EnableTotalRecordCount)
{
commandText = string.Empty;
@@ -3594,11 +3729,13 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("IndexNumber=@IndexNumber");
statement?.TryBind("@IndexNumber", query.IndexNumber.Value);
}
if (query.ParentIndexNumber.HasValue)
{
whereClauses.Add("ParentIndexNumber=@ParentIndexNumber");
statement?.TryBind("@ParentIndexNumber", query.ParentIndexNumber.Value);
}
if (query.ParentIndexNumberNotEquals.HasValue)
{
whereClauses.Add("(ParentIndexNumber<>@ParentIndexNumberNotEquals or ParentIndexNumber is null)");
@@ -3884,6 +4021,7 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind(paramName, artistId.ToByteArray());
}
index++;
}
@@ -3904,6 +4042,7 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind(paramName, artistId.ToByteArray());
}
index++;
}
@@ -3924,8 +4063,10 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind(paramName, artistId.ToByteArray());
}
index++;
}
var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause);
}
@@ -3943,8 +4084,10 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind(paramName, albumId.ToByteArray());
}
index++;
}
var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause);
}
@@ -3962,8 +4105,10 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind(paramName, artistId.ToByteArray());
}
index++;
}
var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause);
}
@@ -3981,8 +4126,10 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind(paramName, genreId.ToByteArray());
}
index++;
}
var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause);
}
@@ -3998,8 +4145,10 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind("@Genre" + index, GetCleanValue(item));
}
index++;
}
var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause);
}
@@ -4015,8 +4164,10 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind("@Tag" + index, GetCleanValue(item));
}
index++;
}
var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause);
}
@@ -4032,8 +4183,10 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind("@ExcludeTag" + index, GetCleanValue(item));
}
index++;
}
var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause);
}
@@ -4052,8 +4205,10 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind(paramName, studioId.ToByteArray());
}
index++;
}
var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause);
}
@@ -4069,8 +4224,10 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind("@OfficialRating" + index, item);
}
index++;
}
var clause = "(" + string.Join(" OR ", clauses) + ")";
whereClauses.Add(clause);
}
@@ -4245,6 +4402,7 @@ namespace Emby.Server.Implementations.Data
statement.TryBind("@IsVirtualItem", isVirtualItem.Value);
}
}
if (query.IsSpecialSeason.HasValue)
{
if (query.IsSpecialSeason.Value)
@@ -4256,6 +4414,7 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("IndexNumber <> 0");
}
}
if (query.IsUnaired.HasValue)
{
if (query.IsUnaired.Value)
@@ -4267,6 +4426,7 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("PremiereDate < DATETIME('now')");
}
}
var queryMediaTypes = query.MediaTypes.Where(IsValidMediaType).ToArray();
if (queryMediaTypes.Length == 1)
{
@@ -4282,6 +4442,7 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add("MediaType in (" + val + ")");
}
if (query.ItemIds.Length > 0)
{
var includeIds = new List<string>();
@@ -4294,11 +4455,13 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind("@IncludeId" + index, id);
}
index++;
}
whereClauses.Add("(" + string.Join(" OR ", includeIds) + ")");
}
if (query.ExcludeItemIds.Length > 0)
{
var excludeIds = new List<string>();
@@ -4311,6 +4474,7 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind("@ExcludeId" + index, id);
}
index++;
}
@@ -4335,6 +4499,7 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%");
}
index++;
break;
@@ -4360,7 +4525,7 @@ namespace Emby.Server.Implementations.Data
// TODO this seems to be an idea for a better schema where ProviderIds are their own table
// buut this is not implemented
//hasProviderIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")");
// hasProviderIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")");
// TODO this is a really BAD way to do it since the pair:
// Tmdb, 1234 matches Tmdb=1234 but also Tmdb=1234567
@@ -4377,6 +4542,7 @@ namespace Emby.Server.Implementations.Data
{
statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%");
}
index++;
break;
@@ -4427,6 +4593,7 @@ namespace Emby.Server.Implementations.Data
{
whereClauses.Add("(TopParentId=@TopParentId)");
}
if (statement != null)
{
statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N", CultureInfo.InvariantCulture));
@@ -4464,11 +4631,13 @@ namespace Emby.Server.Implementations.Data
statement.TryBind("@AncestorId", query.AncestorIds[0]);
}
}
if (query.AncestorIds.Length > 1)
{
var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
}
if (!string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
{
var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey";
@@ -4497,6 +4666,7 @@ namespace Emby.Server.Implementations.Data
statement.TryBind("@UnratedType", query.BlockUnratedItems[0].ToString());
}
}
if (query.BlockUnratedItems.Length > 1)
{
var inClause = string.Join(",", query.BlockUnratedItems.Select(i => "'" + i.ToString() + "'"));
@@ -4789,7 +4959,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
connection.RunInTransaction(db =>
{
connection.ExecuteAll(sql);
}, TransactionMode);
}
}
@@ -4972,6 +5141,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statement.TryBind("@ItemId", query.ItemId.ToByteArray());
}
}
if (!query.AppearsInItemId.Equals(Guid.Empty))
{
whereClauses.Add("Name in (Select Name from People where ItemId=@AppearsInItemId)");
@@ -4980,6 +5150,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToByteArray());
}
}
var queryPersonTypes = query.PersonTypes.Where(IsValidPersonType).ToList();
if (queryPersonTypes.Count == 1)
@@ -4996,6 +5167,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
whereClauses.Add("PersonType in (" + val + ")");
}
var queryExcludePersonTypes = query.ExcludePersonTypes.Where(IsValidPersonType).ToList();
if (queryExcludePersonTypes.Count == 1)
@@ -5012,6 +5184,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
whereClauses.Add("PersonType not in (" + val + ")");
}
if (query.MaxListOrder.HasValue)
{
whereClauses.Add("ListOrder<=@MaxListOrder");
@@ -5020,6 +5193,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statement.TryBind("@MaxListOrder", query.MaxListOrder.Value);
}
}
if (!string.IsNullOrWhiteSpace(query.NameContains))
{
whereClauses.Add("Name like @NameContains");
@@ -5159,6 +5333,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
var typeString = string.Join(",", withItemTypes.Select(i => "'" + i + "'"));
commandText += " AND ItemId In (select guid from typedbaseitems where type in (" + typeString + "))";
}
if (excludeItemTypes.Count > 0)
{
var typeString = string.Join(",", excludeItemTypes.Select(i => "'" + i + "'"));
@@ -5180,7 +5355,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
}
}
}
LogQueryTime("GetItemValueNames", commandText, now);
@@ -5631,7 +5805,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
db.Execute("delete from People where ItemId=@ItemId", itemIdBlob);
InsertPeople(itemIdBlob, people, db);
}, TransactionMode);
}
}
@@ -5788,7 +5961,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob);
InsertMediaStreams(itemIdBlob, streams, db);
}, TransactionMode);
}
}
@@ -6134,7 +6306,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob);
InsertMediaAttachments(itemIdBlob, attachments, db, cancellationToken);
}, TransactionMode);
}
}
@@ -6200,7 +6371,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
/// Gets the attachment.
/// </summary>
/// <param name="reader">The reader.</param>
/// <returns>MediaAttachment</returns>
/// <returns>MediaAttachment.</returns>
private MediaAttachment GetMediaAttachment(IReadOnlyList<IResultSetValue> reader)
{
var item = new MediaAttachment

View File

@@ -135,10 +135,12 @@ namespace Emby.Server.Implementations.Data
{
throw new ArgumentNullException(nameof(userData));
}
if (internalUserId <= 0)
{
throw new ArgumentNullException(nameof(internalUserId));
}
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException(nameof(key));
@@ -153,6 +155,7 @@ namespace Emby.Server.Implementations.Data
{
throw new ArgumentNullException(nameof(userData));
}
if (internalUserId <= 0)
{
throw new ArgumentNullException(nameof(internalUserId));
@@ -235,7 +238,7 @@ namespace Emby.Server.Implementations.Data
}
/// <summary>
/// Persist all user data for the specified user
/// Persist all user data for the specified user.
/// </summary>
private void PersistAllUserData(long internalUserId, UserItemData[] userDataList, CancellationToken cancellationToken)
{
@@ -309,7 +312,7 @@ namespace Emby.Server.Implementations.Data
}
/// <summary>
/// Return all user-data associated with the given user
/// Return all user-data associated with the given user.
/// </summary>
/// <param name="internalUserId"></param>
/// <returns></returns>
@@ -339,7 +342,7 @@ namespace Emby.Server.Implementations.Data
}
/// <summary>
/// Read a row from the specified reader into the provided userData object
/// Read a row from the specified reader into the provided userData object.
/// </summary>
/// <param name="reader"></param>
private UserItemData ReadRow(IReadOnlyList<IResultSetValue> reader)
@@ -347,7 +350,7 @@ namespace Emby.Server.Implementations.Data
var userData = new UserItemData();
userData.Key = reader[0].ToString();
//userData.UserId = reader[1].ReadGuidFromBlob();
// userData.UserId = reader[1].ReadGuidFromBlob();
if (reader[2].SQLiteType != SQLiteType.Null)
{

View File

@@ -112,7 +112,7 @@ namespace Emby.Server.Implementations.Devices
{
IEnumerable<AuthenticationInfo> sessions = _authRepo.Get(new AuthenticationInfoQuery
{
//UserId = query.UserId
// UserId = query.UserId
HasUser = true
}).Items;
@@ -169,6 +169,7 @@ namespace Emby.Server.Implementations.Devices
{
throw new ArgumentException("user not found");
}
if (string.IsNullOrEmpty(deviceId))
{
throw new ArgumentNullException(nameof(deviceId));

View File

@@ -74,7 +74,7 @@ namespace Emby.Server.Implementations.Dto
}
/// <summary>
/// Converts a BaseItem to a DTOBaseItem
/// Converts a BaseItem to a DTOBaseItem.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="fields">The fields.</param>
@@ -277,6 +277,7 @@ namespace Emby.Server.Implementations.Dto
dto.EpisodeTitle = dto.Name;
dto.Name = dto.SeriesName;
}
liveTvManager.AddInfoToRecordingDto(item, dto, activeRecording, user);
}
@@ -292,6 +293,7 @@ namespace Emby.Server.Implementations.Dto
{
continue;
}
var containers = container.Split(new[] { ',' });
if (containers.Length < 2)
{
@@ -406,7 +408,6 @@ namespace Emby.Server.Implementations.Dto
dto.DateLastMediaAdded = folder.DateLastMediaAdded;
}
}
else
{
if (options.EnableUserData)
@@ -443,7 +444,7 @@ namespace Emby.Server.Implementations.Dto
}
/// <summary>
/// Gets client-side Id of a server-side BaseItem
/// Gets client-side Id of a server-side BaseItem.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
@@ -457,6 +458,7 @@ namespace Emby.Server.Implementations.Dto
{
dto.SeriesName = item.SeriesName;
}
private static void SetPhotoProperties(BaseItemDto dto, Photo item)
{
dto.CameraMake = item.CameraMake;
@@ -538,7 +540,7 @@ namespace Emby.Server.Implementations.Dto
}
/// <summary>
/// Attaches People DTO's to a DTOBaseItem
/// Attaches People DTO's to a DTOBaseItem.
/// </summary>
/// <param name="dto">The dto.</param>
/// <param name="item">The item.</param>
@@ -555,22 +557,27 @@ namespace Emby.Server.Implementations.Dto
{
return 0;
}
if (i.IsType(PersonType.GuestStar))
{
return 1;
}
if (i.IsType(PersonType.Director))
{
return 2;
}
if (i.IsType(PersonType.Writer))
{
return 3;
}
if (i.IsType(PersonType.Producer))
{
return 4;
}
if (i.IsType(PersonType.Composer))
{
return 4;
@@ -594,7 +601,6 @@ namespace Emby.Server.Implementations.Dto
_logger.LogError(ex, "Error getting person {Name}", c);
return null;
}
}).Where(i => i != null)
.GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
.Select(x => x.First())
@@ -615,6 +621,7 @@ namespace Emby.Server.Implementations.Dto
{
baseItemPerson.PrimaryImageTag = GetTagAndFillBlurhash(dto, entity, ImageType.Primary);
baseItemPerson.Id = entity.Id.ToString("N", CultureInfo.InvariantCulture);
baseItemPerson.ImageBlurHashes = dto.ImageBlurHashes;
list.Add(baseItemPerson);
}
}
@@ -727,7 +734,7 @@ namespace Emby.Server.Implementations.Dto
}
/// <summary>
/// Sets simple property values on a DTOBaseItem
/// Sets simple property values on a DTOBaseItem.
/// </summary>
/// <param name="dto">The dto.</param>
/// <param name="item">The item.</param>
@@ -947,7 +954,7 @@ namespace Emby.Server.Implementations.Dto
dto.AlbumPrimaryImageTag = GetTagAndFillBlurhash(dto, albumParent, ImageType.Primary);
}
//if (options.ContainsField(ItemFields.MediaSourceCount))
// if (options.ContainsField(ItemFields.MediaSourceCount))
//{
// Songs always have one
//}
@@ -957,13 +964,13 @@ namespace Emby.Server.Implementations.Dto
{
dto.Artists = hasArtist.Artists;
//var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
// var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
//{
// EnableTotalRecordCount = false,
// ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
//});
//dto.ArtistItems = artistItems.Items
// dto.ArtistItems = artistItems.Items
// .Select(i =>
// {
// var artist = i.Item1;
@@ -976,7 +983,7 @@ namespace Emby.Server.Implementations.Dto
// .ToList();
// Include artists that are not in the database yet, e.g., just added via metadata editor
//var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
// var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
dto.ArtistItems = hasArtist.Artists
//.Except(foundArtists, new DistinctNameComparer())
.Select(i =>
@@ -1001,7 +1008,6 @@ namespace Emby.Server.Implementations.Dto
}
return null;
}).Where(i => i != null).ToArray();
}
@@ -1010,13 +1016,13 @@ namespace Emby.Server.Implementations.Dto
{
dto.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
//var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
// var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
//{
// EnableTotalRecordCount = false,
// ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
//});
//dto.AlbumArtists = artistItems.Items
// dto.AlbumArtists = artistItems.Items
// .Select(i =>
// {
// var artist = i.Item1;
@@ -1052,7 +1058,6 @@ namespace Emby.Server.Implementations.Dto
}
return null;
}).Where(i => i != null).ToArray();
}
@@ -1166,7 +1171,7 @@ namespace Emby.Server.Implementations.Dto
// this block will add the series poster for episodes without a poster
// TODO maybe remove the if statement entirely
//if (options.ContainsField(ItemFields.SeriesPrimaryImage))
// if (options.ContainsField(ItemFields.SeriesPrimaryImage))
{
episodeSeries = episodeSeries ?? episode.Series;
if (episodeSeries != null)
@@ -1212,7 +1217,7 @@ namespace Emby.Server.Implementations.Dto
// this block will add the series poster for seasons without a poster
// TODO maybe remove the if statement entirely
//if (options.ContainsField(ItemFields.SeriesPrimaryImage))
// if (options.ContainsField(ItemFields.SeriesPrimaryImage))
{
series = series ?? season.Series;
if (series != null)
@@ -1350,6 +1355,7 @@ namespace Emby.Server.Implementations.Dto
dto.ParentLogoImageTag = GetTagAndFillBlurhash(dto, parent, image);
}
}
if (artLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && dto.ParentArtItemId == null)
{
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Art);
@@ -1360,6 +1366,7 @@ namespace Emby.Server.Implementations.Dto
dto.ParentArtImageTag = GetTagAndFillBlurhash(dto, parent, image);
}
}
if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && !(parent is ICollectionFolder) && !(parent is UserView))
{
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Thumb);
@@ -1370,6 +1377,7 @@ namespace Emby.Server.Implementations.Dto
dto.ParentThumbImageTag = GetTagAndFillBlurhash(dto, parent, image);
}
}
if (backdropLimit > 0 && !((dto.BackdropImageTags != null && dto.BackdropImageTags.Length > 0) || (dto.ParentBackdropImageTags != null && dto.ParentBackdropImageTags.Length > 0)))
{
var images = allImages.Where(i => i.Type == ImageType.Backdrop).Take(backdropLimit).ToList();

View File

@@ -23,8 +23,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="IPNetwork2" Version="2.4.0.126" />
<PackageReference Include="Jellyfin.XmlTv" Version="10.4.3" />
<PackageReference Include="IPNetwork2" Version="2.5.211" />
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.0-pre1" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" Version="2.2.0" />
@@ -33,14 +33,14 @@
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.6" />
<PackageReference Include="Mono.Nat" Version="2.0.1" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" />
<PackageReference Include="ServiceStack.Text.Core" Version="5.9.0" />
<PackageReference Include="sharpcompress" Version="0.25.0" />
<PackageReference Include="sharpcompress" Version="0.25.1" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
<PackageReference Include="DotNet.Glob" Version="3.0.9" />
</ItemGroup>

View File

@@ -132,7 +132,6 @@ namespace Emby.Server.Implementations.EntryPoints
}
catch
{
}
}
}

View File

@@ -159,7 +159,6 @@ namespace Emby.Server.Implementations.EntryPoints
}
catch (Exception)
{
}
}
@@ -175,7 +174,6 @@ namespace Emby.Server.Implementations.EntryPoints
}
catch (Exception)
{
}
}

View File

@@ -1,3 +1,4 @@
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Udp;
@@ -43,14 +44,21 @@ namespace Emby.Server.Implementations.EntryPoints
_logger = logger;
_appHost = appHost;
_config = configuration;
}
/// <inheritdoc />
public Task RunAsync()
{
_udpServer = new UdpServer(_logger, _appHost, _config);
_udpServer.Start(PortNumber, _cancellationTokenSource.Token);
try
{
_udpServer = new UdpServer(_logger, _appHost, _config);
_udpServer.Start(PortNumber, _cancellationTokenSource.Token);
}
catch (SocketException ex)
{
_logger.LogWarning(ex, "Unable to start AutoDiscovery listener on UDP port {PortNumber}", PortNumber);
}
return Task.CompletedTask;
}

View File

@@ -140,7 +140,7 @@ namespace Emby.Server.Implementations.HttpClientManager
=> SendAsync(options, HttpMethod.Get);
/// <summary>
/// Performs a GET request and returns the resulting stream
/// Performs a GET request and returns the resulting stream.
/// </summary>
/// <param name="options">The options.</param>
/// <returns>Task{Stream}.</returns>

View File

@@ -32,12 +32,12 @@ namespace Emby.Server.Implementations.HttpServer
private readonly IFileSystem _fileSystem;
/// <summary>
/// The _options
/// The _options.
/// </summary>
private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
/// <summary>
/// The _requested ranges
/// The _requested ranges.
/// </summary>
private List<KeyValuePair<long, long?>> _requestedRanges;

View File

@@ -453,6 +453,7 @@ namespace Emby.Server.Implementations.HttpServer
{
httpRes.Headers.Add(key, value);
}
httpRes.ContentType = "text/plain";
await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false);
return;
@@ -591,7 +592,7 @@ namespace Emby.Server.Implementations.HttpServer
}
/// <summary>
/// Get the default CORS headers
/// Get the default CORS headers.
/// </summary>
/// <param name="req"></param>
/// <returns></returns>

View File

@@ -426,7 +426,7 @@ namespace Emby.Server.Implementations.HttpServer
/// </summary>
private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, StaticResultOptions options)
{
bool noCache = (requestContext.Headers[HeaderNames.CacheControl].ToString()).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
bool noCache = requestContext.Headers[HeaderNames.CacheControl].ToString().IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
AddCachingHeaders(responseHeaders, options.CacheDuration, noCache, options.DateLastModified);
if (!noCache)
@@ -580,13 +580,12 @@ namespace Emby.Server.Implementations.HttpServer
}
catch (NotSupportedException)
{
}
}
if (!string.IsNullOrWhiteSpace(rangeHeader) && totalContentLength.HasValue)
{
var hasHeaders = new RangeRequestWriter(rangeHeader, totalContentLength.Value, stream, contentType, isHeadRequest, _logger)
var hasHeaders = new RangeRequestWriter(rangeHeader, totalContentLength.Value, stream, contentType, isHeadRequest)
{
OnComplete = options.OnComplete
};
@@ -623,8 +622,11 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// Adds the caching responseHeaders.
/// </summary>
private void AddCachingHeaders(IDictionary<string, string> responseHeaders, TimeSpan? cacheDuration,
bool noCache, DateTime? lastModifiedDate)
private void AddCachingHeaders(
IDictionary<string, string> responseHeaders,
TimeSpan? cacheDuration,
bool noCache,
DateTime? lastModifiedDate)
{
if (noCache)
{
@@ -693,7 +695,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that
/// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that.
/// </summary>
/// <param name="date">The date.</param>
/// <returns>DateTime.</returns>

View File

@@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -8,13 +9,45 @@ using System.Net;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
namespace Emby.Server.Implementations.HttpServer
{
public class RangeRequestWriter : IAsyncStreamWriter, IHttpResult
{
private const int BufferSize = 81920;
private readonly Dictionary<string, string> _options = new Dictionary<string, string>();
private List<KeyValuePair<long, long?>> _requestedRanges;
/// <summary>
/// Initializes a new instance of the <see cref="RangeRequestWriter" /> class.
/// </summary>
/// <param name="rangeHeader">The range header.</param>
/// <param name="contentLength">The content length.</param>
/// <param name="source">The source.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
public RangeRequestWriter(string rangeHeader, long contentLength, Stream source, string contentType, bool isHeadRequest)
{
if (string.IsNullOrEmpty(contentType))
{
throw new ArgumentNullException(nameof(contentType));
}
RangeHeader = rangeHeader;
SourceStream = source;
IsHeadRequest = isHeadRequest;
ContentType = contentType;
Headers[HeaderNames.ContentType] = contentType;
Headers[HeaderNames.AcceptRanges] = "bytes";
StatusCode = HttpStatusCode.PartialContent;
SetRangeValues(contentLength);
}
/// <summary>
/// Gets or sets the source stream.
/// </summary>
@@ -29,19 +62,6 @@ namespace Emby.Server.Implementations.HttpServer
private long TotalContentLength { get; set; }
public Action OnComplete { get; set; }
private readonly ILogger _logger;
private const int BufferSize = 81920;
/// <summary>
/// The _options
/// </summary>
private readonly Dictionary<string, string> _options = new Dictionary<string, string>();
/// <summary>
/// The us culture
/// </summary>
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
/// <summary>
/// Additional HTTP Headers
@@ -50,32 +70,57 @@ namespace Emby.Server.Implementations.HttpServer
public IDictionary<string, string> Headers => _options;
/// <summary>
/// Initializes a new instance of the <see cref="RangeRequestWriter" /> class.
/// Gets the requested ranges.
/// </summary>
/// <param name="rangeHeader">The range header.</param>
/// <param name="contentLength">The content length.</param>
/// <param name="source">The source.</param>
/// <param name="contentType">Type of the content.</param>
/// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
/// <param name="logger">The logger instance.</param>
public RangeRequestWriter(string rangeHeader, long contentLength, Stream source, string contentType, bool isHeadRequest, ILogger logger)
/// <value>The requested ranges.</value>
protected List<KeyValuePair<long, long?>> RequestedRanges
{
if (string.IsNullOrEmpty(contentType))
get
{
throw new ArgumentNullException(nameof(contentType));
if (_requestedRanges == null)
{
_requestedRanges = new List<KeyValuePair<long, long?>>();
// Example: bytes=0-,32-63
var ranges = RangeHeader.Split('=')[1].Split(',');
foreach (var range in ranges)
{
var vals = range.Split('-');
long start = 0;
long? end = null;
if (!string.IsNullOrEmpty(vals[0]))
{
start = long.Parse(vals[0], CultureInfo.InvariantCulture);
}
if (!string.IsNullOrEmpty(vals[1]))
{
end = long.Parse(vals[1], CultureInfo.InvariantCulture);
}
_requestedRanges.Add(new KeyValuePair<long, long?>(start, end));
}
}
return _requestedRanges;
}
}
RangeHeader = rangeHeader;
SourceStream = source;
IsHeadRequest = isHeadRequest;
this._logger = logger;
public string ContentType { get; set; }
ContentType = contentType;
Headers[HeaderNames.ContentType] = contentType;
Headers[HeaderNames.AcceptRanges] = "bytes";
StatusCode = HttpStatusCode.PartialContent;
public IRequest RequestContext { get; set; }
SetRangeValues(contentLength);
public object Response { get; set; }
public int Status { get; set; }
public HttpStatusCode StatusCode
{
get => (HttpStatusCode)Status;
set => Status = (int)value;
}
/// <summary>
@@ -109,49 +154,6 @@ namespace Emby.Server.Implementations.HttpServer
}
}
/// <summary>
/// The _requested ranges
/// </summary>
private List<KeyValuePair<long, long?>> _requestedRanges;
/// <summary>
/// Gets the requested ranges.
/// </summary>
/// <value>The requested ranges.</value>
protected List<KeyValuePair<long, long?>> RequestedRanges
{
get
{
if (_requestedRanges == null)
{
_requestedRanges = new List<KeyValuePair<long, long?>>();
// Example: bytes=0-,32-63
var ranges = RangeHeader.Split('=')[1].Split(',');
foreach (var range in ranges)
{
var vals = range.Split('-');
long start = 0;
long? end = null;
if (!string.IsNullOrEmpty(vals[0]))
{
start = long.Parse(vals[0], UsCulture);
}
if (!string.IsNullOrEmpty(vals[1]))
{
end = long.Parse(vals[1], UsCulture);
}
_requestedRanges.Add(new KeyValuePair<long, long?>(start, end));
}
}
return _requestedRanges;
}
}
public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
{
try
@@ -167,59 +169,44 @@ namespace Emby.Server.Implementations.HttpServer
// If the requested range is "0-", we can optimize by just doing a stream copy
if (RangeEnd >= TotalContentLength - 1)
{
await source.CopyToAsync(responseStream, BufferSize).ConfigureAwait(false);
await source.CopyToAsync(responseStream, BufferSize, cancellationToken).ConfigureAwait(false);
}
else
{
await CopyToInternalAsync(source, responseStream, RangeLength).ConfigureAwait(false);
await CopyToInternalAsync(source, responseStream, RangeLength, cancellationToken).ConfigureAwait(false);
}
}
}
finally
{
if (OnComplete != null)
{
OnComplete();
}
OnComplete?.Invoke();
}
}
private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength)
private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
{
var array = new byte[BufferSize];
int bytesRead;
while ((bytesRead = await source.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0)
var array = ArrayPool<byte>.Shared.Rent(BufferSize);
try
{
if (bytesRead == 0)
int bytesRead;
while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
{
break;
}
var bytesToCopy = Math.Min(bytesRead, copyLength);
var bytesToCopy = Math.Min(bytesRead, copyLength);
await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy), cancellationToken).ConfigureAwait(false);
await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy)).ConfigureAwait(false);
copyLength -= bytesToCopy;
copyLength -= bytesToCopy;
if (copyLength <= 0)
{
break;
if (copyLength <= 0)
{
break;
}
}
}
}
public string ContentType { get; set; }
public IRequest RequestContext { get; set; }
public object Response { get; set; }
public int Status { get; set; }
public HttpStatusCode StatusCode
{
get => (HttpStatusCode)Status;
set => Status = (int)value;
finally
{
ArrayPool<byte>.Shared.Return(array);
}
}
}
}

View File

@@ -41,11 +41,11 @@ namespace Emby.Server.Implementations.HttpServer
res.Headers.Add(key, value);
}
// Try to prevent compatibility view
res.Headers["Access-Control-Allow-Headers"] = ("Accept, Accept-Language, Authorization, Cache-Control, " +
res.Headers["Access-Control-Allow-Headers"] = "Accept, Accept-Language, Authorization, Cache-Control, " +
"Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, " +
"Content-Type, Cookie, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, " +
"Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, " +
"X-Emby-Authorization");
"X-Emby-Authorization";
if (dto is Exception exception)
{

View File

@@ -266,7 +266,6 @@ namespace Emby.Server.Implementations.IO
{
DisposeWatcher(newWatcher, false);
}
}
catch (Exception ex)
{
@@ -393,7 +392,6 @@ namespace Emby.Server.Implementations.IO
}
return false;
}))
{
monitorPath = false;

View File

@@ -237,7 +237,7 @@ namespace Emby.Server.Implementations.IO
{
result.IsDirectory = info is DirectoryInfo || (info.Attributes & FileAttributes.Directory) == FileAttributes.Directory;
//if (!result.IsDirectory)
// if (!result.IsDirectory)
//{
// result.IsHidden = (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
//}
@@ -245,6 +245,16 @@ namespace Emby.Server.Implementations.IO
if (info is FileInfo fileInfo)
{
result.Length = fileInfo.Length;
// Issue #2354 get the size of files behind symbolic links
if (fileInfo.Attributes.HasFlag(FileAttributes.ReparsePoint))
{
using (Stream thisFileStream = File.OpenRead(fileInfo.FullName))
{
result.Length = thisFileStream.Length;
}
}
result.DirectoryName = fileInfo.DirectoryName;
}
@@ -628,6 +638,7 @@ namespace Emby.Server.Implementations.IO
{
return false;
}
return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
});
}
@@ -682,6 +693,7 @@ namespace Emby.Server.Implementations.IO
{
return false;
}
return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
});
}

View File

@@ -36,11 +36,6 @@ namespace Emby.Server.Implementations
/// </summary>
string RestartArgs { get; }
/// <summary>
/// Gets the value of the --plugin-manifest-url command line option.
/// </summary>
string PluginManifestUrl { get; }
/// <summary>
/// Gets the value of the --published-server-url command line option.
/// </summary>

View File

@@ -71,7 +71,6 @@ namespace Emby.Server.Implementations.Images
new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending)
},
IncludeItemTypes = includeItemTypes
});
}

View File

@@ -78,7 +78,6 @@ namespace Emby.Server.Implementations.Images
}
return i;
}).GroupBy(x => x.Id)
.Select(x => x.First());

View File

@@ -1,5 +1,6 @@
using System;
using System.IO;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
@@ -8,24 +9,33 @@ using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Library
{
/// <summary>
/// Provides the core resolver ignore rules
/// Provides the core resolver ignore rules.
/// </summary>
public class CoreResolutionIgnoreRule : IResolverIgnoreRule
{
private readonly ILibraryManager _libraryManager;
private readonly IServerApplicationPaths _serverApplicationPaths;
/// <summary>
/// Initializes a new instance of the <see cref="CoreResolutionIgnoreRule"/> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
public CoreResolutionIgnoreRule(ILibraryManager libraryManager)
/// <param name="serverApplicationPaths">The server application paths.</param>
public CoreResolutionIgnoreRule(ILibraryManager libraryManager, IServerApplicationPaths serverApplicationPaths)
{
_libraryManager = libraryManager;
_serverApplicationPaths = serverApplicationPaths;
}
/// <inheritdoc />
public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent)
{
// Don't ignore application folders
if (fileInfo.FullName.Contains(_serverApplicationPaths.RootFolderPath, StringComparison.InvariantCulture))
{
return false;
}
// Don't ignore top level folders
if (fileInfo.IsDirectory && parent is AggregateFolder)
{

View File

@@ -12,11 +12,13 @@ namespace Emby.Server.Implementations.Library
public class ExclusiveLiveStream : ILiveStream
{
public int ConsumerCount { get; set; }
public string OriginalStreamId { get; set; }
public string TunerHostId => null;
public bool EnableStreamSharing { get; set; }
public MediaSourceInfo MediaSource { get; set; }
public string UniqueId { get; private set; }

View File

@@ -1,17 +1,20 @@
#nullable enable
using System;
using System.Linq;
using DotNet.Globbing;
namespace Emby.Server.Implementations.Library
{
/// <summary>
/// Glob patterns for files to ignore
/// Glob patterns for files to ignore.
/// </summary>
public static class IgnorePatterns
{
/// <summary>
/// Files matching these glob patterns will be ignored
/// Files matching these glob patterns will be ignored.
/// </summary>
public static readonly string[] Patterns = new string[]
private static readonly string[] _patterns =
{
"**/small.jpg",
"**/albumart.jpg",
@@ -19,32 +22,51 @@ namespace Emby.Server.Implementations.Library
// Directories
"**/metadata/**",
"**/metadata",
"**/ps3_update/**",
"**/ps3_update",
"**/ps3_vprm/**",
"**/ps3_vprm",
"**/extrafanart/**",
"**/extrafanart",
"**/extrathumbs/**",
"**/extrathumbs",
"**/.actors/**",
"**/.actors",
"**/.wd_tv/**",
"**/.wd_tv",
"**/lost+found/**",
"**/lost+found",
// WMC temp recording directories that will constantly be written to
"**/TempRec/**",
"**/TempRec",
"**/TempSBE/**",
"**/TempSBE",
// Synology
"**/eaDir/**",
"**/eaDir",
"**/@eaDir/**",
"**/@eaDir",
"**/#recycle/**",
"**/#recycle",
// Qnap
"**/@Recycle/**",
"**/@Recycle",
"**/.@__thumb/**",
"**/.@__thumb",
"**/$RECYCLE.BIN/**",
"**/$RECYCLE.BIN",
"**/System Volume Information/**",
"**/System Volume Information",
"**/.grab/**",
"**/.grab",
// Unix hidden files and directories
"**/.*/**",
"**/.*",
// thumbs.db
"**/thumbs.db",
@@ -56,19 +78,31 @@ namespace Emby.Server.Implementations.Library
private static readonly GlobOptions _globOptions = new GlobOptions
{
Evaluation = {
Evaluation =
{
CaseInsensitive = true
}
};
private static readonly Glob[] _globs = Patterns.Select(p => Glob.Parse(p, _globOptions)).ToArray();
private static readonly Glob[] _globs = _patterns.Select(p => Glob.Parse(p, _globOptions)).ToArray();
/// <summary>
/// Returns true if the supplied path should be ignored
/// Returns true if the supplied path should be ignored.
/// </summary>
public static bool ShouldIgnore(string path)
/// <param name="path">The path to test.</param>
/// <returns>Whether to ignore the path.</returns>
public static bool ShouldIgnore(ReadOnlySpan<char> path)
{
return _globs.Any(g => g.IsMatch(path));
int len = _globs.Length;
for (int i = 0; i < len; i++)
{
if (_globs[i].IsMatch(path))
{
return true;
}
}
return false;
}
}
}

View File

@@ -97,13 +97,13 @@ namespace Emby.Server.Implementations.Library
private IIntroProvider[] IntroProviders { get; set; }
/// <summary>
/// Gets or sets the list of entity resolution ignore rules
/// Gets or sets the list of entity resolution ignore rules.
/// </summary>
/// <value>The entity resolution ignore rules.</value>
private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; }
/// <summary>
/// Gets or sets the list of currently registered entity resolvers
/// Gets or sets the list of currently registered entity resolvers.
/// </summary>
/// <value>The entity resolvers enumerable.</value>
private IItemResolver[] EntityResolvers { get; set; }
@@ -136,7 +136,7 @@ namespace Emby.Server.Implementations.Library
/// <summary>
/// Initializes a new instance of the <see cref="LibraryManager" /> class.
/// </summary>
/// <param name="appHost">The application host</param>
/// <param name="appHost">The application host.</param>
/// <param name="logger">The logger.</param>
/// <param name="taskManager">The task manager.</param>
/// <param name="userManager">The user manager.</param>
@@ -209,12 +209,12 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
/// The _root folder
/// The _root folder.
/// </summary>
private volatile AggregateFolder _rootFolder;
/// <summary>
/// The _root folder sync lock
/// The _root folder sync lock.
/// </summary>
private readonly object _rootFolderSyncLock = new object();
@@ -341,7 +341,7 @@ namespace Emby.Server.Implementations.Library
if (item is LiveTvProgram)
{
_logger.LogDebug(
"Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
"Removing item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
item.GetType().Name,
item.Name ?? "Unknown name",
item.Path ?? string.Empty,
@@ -350,7 +350,7 @@ namespace Emby.Server.Implementations.Library
else
{
_logger.LogInformation(
"Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
"Removing item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
item.GetType().Name,
item.Name ?? "Unknown name",
item.Path ?? string.Empty,
@@ -368,7 +368,12 @@ namespace Emby.Server.Implementations.Library
continue;
}
_logger.LogDebug("Deleting path {MetadataPath}", metadataPath);
_logger.LogDebug(
"Deleting metadata path, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
item.GetType().Name,
item.Name ?? "Unknown name",
metadataPath,
item.Id);
try
{
@@ -392,7 +397,13 @@ namespace Emby.Server.Implementations.Library
{
try
{
_logger.LogDebug("Deleting path {path}", fileSystemInfo.FullName);
_logger.LogInformation(
"Deleting item path, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
item.GetType().Name,
item.Name ?? "Unknown name",
fileSystemInfo.FullName,
item.Id);
if (fileSystemInfo.IsDirectory)
{
Directory.Delete(fileSystemInfo.FullName, true);
@@ -514,8 +525,8 @@ namespace Emby.Server.Implementations.Library
return key.GetMD5();
}
public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null, bool allowIgnorePath = true)
=> ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent, allowIgnorePath: allowIgnorePath);
public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null)
=> ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent);
private BaseItem ResolvePath(
FileSystemMetadata fileInfo,
@@ -523,8 +534,7 @@ namespace Emby.Server.Implementations.Library
IItemResolver[] resolvers,
Folder parent = null,
string collectionType = null,
LibraryOptions libraryOptions = null,
bool allowIgnorePath = true)
LibraryOptions libraryOptions = null)
{
if (fileInfo == null)
{
@@ -548,7 +558,7 @@ namespace Emby.Server.Implementations.Library
};
// Return null if ignore rules deem that we should do so
if (allowIgnorePath && IgnoreFile(args.FileInfo, args.Parent))
if (IgnoreFile(args.FileInfo, args.Parent))
{
return null;
}
@@ -627,7 +637,7 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
/// Determines whether a path should be ignored based on its contents - called after the contents have been read
/// Determines whether a path should be ignored based on its contents - called after the contents have been read.
/// </summary>
/// <param name="args">The args.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
@@ -713,7 +723,7 @@ namespace Emby.Server.Implementations.Library
Directory.CreateDirectory(rootFolderPath);
var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ??
((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath), allowIgnorePath: false))
((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)))
.DeepCopy<Folder, AggregateFolder>();
// In case program data folder was moved
@@ -795,7 +805,7 @@ namespace Emby.Server.Implementations.Library
if (tmpItem == null)
{
_logger.LogDebug("Creating new userRootFolder with DeepCopy");
tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath), allowIgnorePath: false)).DeepCopy<Folder, UserRootFolder>();
tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath))).DeepCopy<Folder, UserRootFolder>();
}
// In case program data folder was moved
@@ -909,7 +919,7 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
/// Gets a Genre
/// Gets a Genre.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>Task{Genre}.</returns>
@@ -990,7 +1000,7 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
/// Reloads the root media folder
/// Reloads the root media folder.
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
@@ -1793,7 +1803,7 @@ namespace Emby.Server.Implementations.Library
/// Creates the items.
/// </summary>
/// <param name="items">The items.</param>
/// <param name="parent">The parent item</param>
/// <param name="parent">The parent item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public void CreateItems(IEnumerable<BaseItem> items, BaseItem parent, CancellationToken cancellationToken)
{
@@ -1894,9 +1904,19 @@ namespace Emby.Server.Implementations.Library
}
}
ImageDimensions size = _imageProcessor.GetImageDimensions(item, image);
image.Width = size.Width;
image.Height = size.Height;
try
{
ImageDimensions size = _imageProcessor.GetImageDimensions(item, image);
image.Width = size.Width;
image.Height = size.Height;
}
catch (Exception ex)
{
_logger.LogError(ex, "Cannnot get image dimensions for {0}", image.Path);
image.Width = 0;
image.Height = 0;
continue;
}
try
{
@@ -2595,7 +2615,7 @@ namespace Emby.Server.Implementations.Library
Anime series don't generally have a season in their file name, however,
tvdb needs a season to correctly get the metadata.
Hence, a null season needs to be filled with something. */
//FIXME perhaps this would be better for tvdb parser to ask for season 1 if no season is specified
// FIXME perhaps this would be better for tvdb parser to ask for season 1 if no season is specified
episode.ParentIndexNumber = 1;
}
@@ -2784,10 +2804,12 @@ namespace Emby.Server.Implementations.Library
{
throw new ArgumentNullException(nameof(path));
}
if (string.IsNullOrWhiteSpace(from))
{
throw new ArgumentNullException(nameof(from));
}
if (string.IsNullOrWhiteSpace(to))
{
throw new ArgumentNullException(nameof(to));
@@ -2861,7 +2883,6 @@ namespace Emby.Server.Implementations.Library
_logger.LogError(ex, "Error getting person");
return null;
}
}).Where(i => i != null).ToList();
}
@@ -2896,7 +2917,8 @@ namespace Emby.Server.Implementations.Library
}
catch (HttpException ex)
{
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
if (ex.StatusCode.HasValue
&& (ex.StatusCode.Value == HttpStatusCode.NotFound || ex.StatusCode.Value == HttpStatusCode.Forbidden))
{
continue;
}
@@ -2989,21 +3011,6 @@ namespace Emby.Server.Implementations.Library
});
}
private static bool ValidateNetworkPath(string path)
{
//if (Environment.OSVersion.Platform == PlatformID.Win32NT)
//{
// // We can't validate protocol-based paths, so just allow them
// if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1)
// {
// return Directory.Exists(path);
// }
//}
// Without native support for unc, we cannot validate this when running under mono
return true;
}
private const string ShortcutFileExtension = ".mblink";
public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
@@ -3030,11 +3037,6 @@ namespace Emby.Server.Implementations.Library
throw new FileNotFoundException("The path does not exist.");
}
if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
{
throw new FileNotFoundException("The network path does not exist.");
}
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
@@ -3073,11 +3075,6 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(pathInfo));
}
if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
{
throw new FileNotFoundException("The network path does not exist.");
}
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);

View File

@@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.Library
{
mediaInfo = _json.DeserializeFromFile<MediaInfo>(cacheFilePath);
//_logger.LogDebug("Found cached media info");
// _logger.LogDebug("Found cached media info");
}
catch
{
@@ -85,7 +85,7 @@ namespace Emby.Server.Implementations.Library
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
_json.SerializeToFile(mediaInfo, cacheFilePath);
//_logger.LogDebug("Saved media info to {0}", cacheFilePath);
// _logger.LogDebug("Saved media info to {0}", cacheFilePath);
}
}
@@ -148,17 +148,14 @@ namespace Emby.Server.Implementations.Library
{
videoStream.BitRate = 30000000;
}
else if (width >= 1900)
{
videoStream.BitRate = 20000000;
}
else if (width >= 1200)
{
videoStream.BitRate = 8000000;
}
else if (width >= 700)
{
videoStream.BitRate = 2000000;

View File

@@ -205,22 +205,27 @@ namespace Emby.Server.Implementations.Library
{
return MediaProtocol.Rtsp;
}
if (path.StartsWith("Rtmp", StringComparison.OrdinalIgnoreCase))
{
return MediaProtocol.Rtmp;
}
if (path.StartsWith("Http", StringComparison.OrdinalIgnoreCase))
{
return MediaProtocol.Http;
}
if (path.StartsWith("rtp", StringComparison.OrdinalIgnoreCase))
{
return MediaProtocol.Rtp;
}
if (path.StartsWith("ftp", StringComparison.OrdinalIgnoreCase))
{
return MediaProtocol.Ftp;
}
if (path.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
{
return MediaProtocol.Udp;
@@ -436,7 +441,6 @@ namespace Emby.Server.Implementations.Library
}
return 1;
}).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0)
.ThenByDescending(i =>
{
@@ -620,7 +624,6 @@ namespace Emby.Server.Implementations.Library
MediaSource = mediaSource,
ExtractChapters = false,
MediaType = DlnaProfileType.Video
}, cancellationToken).ConfigureAwait(false);
mediaSource.MediaStreams = info.MediaStreams;
@@ -646,7 +649,7 @@ namespace Emby.Server.Implementations.Library
{
mediaInfo = _jsonSerializer.DeserializeFromFile<MediaInfo>(cacheFilePath);
//_logger.LogDebug("Found cached media info");
// _logger.LogDebug("Found cached media info");
}
catch (Exception ex)
{
@@ -682,7 +685,7 @@ namespace Emby.Server.Implementations.Library
Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
_jsonSerializer.SerializeToFile(mediaInfo, cacheFilePath);
//_logger.LogDebug("Saved media info to {0}", cacheFilePath);
// _logger.LogDebug("Saved media info to {0}", cacheFilePath);
}
}
@@ -748,17 +751,14 @@ namespace Emby.Server.Implementations.Library
{
videoStream.BitRate = 30000000;
}
else if (width >= 1900)
{
videoStream.BitRate = 20000000;
}
else if (width >= 1200)
{
videoStream.BitRate = 8000000;
}
else if (width >= 700)
{
videoStream.BitRate = 2000000;

View File

@@ -107,7 +107,7 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
/// Ensures DateCreated and DateModified have values
/// Ensures DateCreated and DateModified have values.
/// </summary>
/// <param name="fileSystem">The file system.</param>
/// <param name="item">The item.</param>

View File

@@ -209,8 +209,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
Name = parseName ?
resolvedItem.Name :
Path.GetFileNameWithoutExtension(firstMedia.Path),
//AdditionalParts = resolvedItem.Files.Skip(1).Select(i => i.Path).ToArray(),
//LocalAlternateVersions = resolvedItem.AlternateVersions.Select(i => i.Path).ToArray()
// AdditionalParts = resolvedItem.Files.Skip(1).Select(i => i.Path).ToArray(),
// LocalAlternateVersions = resolvedItem.AlternateVersions.Select(i => i.Path).ToArray()
};
result.Items.Add(libraryItem);

View File

@@ -92,7 +92,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
// Args points to an album if parent is an Artist folder or it directly contains music
if (args.IsDirectory)
{
// if (args.Parent is MusicArtist) return true; //saves us from testing children twice
// if (args.Parent is MusicArtist) return true; // saves us from testing children twice
if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, _libraryManager))
{
return true;

View File

@@ -292,7 +292,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
}
return true;
//var blurayExtensions = new[]
// var blurayExtensions = new[]
//{
// ".mts",
// ".m2ts",
@@ -300,7 +300,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
// ".mpls"
//};
//return directoryService.GetFiles(fullPath).Any(i => blurayExtensions.Contains(i.Extension ?? string.Empty, StringComparer.OrdinalIgnoreCase));
// return directoryService.GetFiles(fullPath).Any(i => blurayExtensions.Contains(i.Extension ?? string.Empty, StringComparer.OrdinalIgnoreCase));
}
}
}

View File

@@ -19,7 +19,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
// Only process items that are in a collection folder containing books
if (!string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase))
{
return null;
}
if (args.IsDirectory)
{
@@ -55,7 +57,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
// Don't return a Book if there is more (or less) than one document in the directory
if (bookFiles.Count != 1)
{
return null;
}
return new Book
{

View File

@@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
public virtual ResolverPriority Priority => ResolverPriority.First;
/// <summary>
/// Sets initial values on the newly resolved item
/// Sets initial values on the newly resolved item.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="args">The args.</param>

View File

@@ -41,10 +41,12 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
return new AggregateFolder();
}
if (string.Equals(args.Path, _appPaths.DefaultUserViewsPath, StringComparison.OrdinalIgnoreCase))
{
return new UserRootFolder(); //if we got here and still a root - must be user root
return new UserRootFolder(); // if we got here and still a root - must be user root
}
if (args.IsVf)
{
return new CollectionFolder
@@ -73,7 +75,6 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
return false;
}
})
.Select(i => _fileSystem.GetFileNameWithoutExtension(i))
.FirstOrDefault();

View File

@@ -55,6 +55,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
episode.SeriesId = series.Id;
episode.SeriesName = series.Name;
}
if (season != null)
{
episode.SeasonId = season.Id;

View File

@@ -23,8 +23,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// </summary>
/// <param name="config">The config.</param>
/// <param name="libraryManager">The library manager.</param>
/// <param name="localization">The localization</param>
/// <param name="logger">The logger</param>
/// <param name="localization">The localization.</param>
/// <param name="logger">The logger.</param>
public SeasonResolver(
IServerConfigurationManager config,
ILibraryManager libraryManager,
@@ -94,7 +94,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
_localization.GetLocalizedString("NameSeasonNumber"),
seasonNumber,
args.GetLibraryOptions().PreferredMetadataLanguage);
}
return season;

View File

@@ -59,7 +59,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
var collectionType = args.GetCollectionType();
if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
{
//if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
// if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
//{
// return new Series
// {

View File

@@ -194,6 +194,7 @@ namespace Emby.Server.Implementations.Library
{
searchQuery.AncestorIds = new[] { searchQuery.ParentId };
}
searchQuery.ParentId = Guid.Empty;
searchQuery.IncludeItemsByName = true;
searchQuery.IncludeItemTypes = Array.Empty<string>();
@@ -207,7 +208,6 @@ namespace Emby.Server.Implementations.Library
return mediaItems.Select(i => new SearchHintInfo
{
Item = i
}).ToList();
}
}

View File

@@ -103,7 +103,7 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
/// Retrieve all user data for the given user
/// Retrieve all user data for the given user.
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
@@ -188,7 +188,7 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
/// Converts a UserItemData to a DTOUserItemData
/// Converts a UserItemData to a DTOUserItemData.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>DtoUserItemData.</returns>

View File

@@ -98,7 +98,6 @@ namespace Emby.Server.Implementations.Library.Validators
_libraryManager.DeleteItem(item, new DeleteOptions
{
DeleteFileLocation = false
}, false);
}

View File

@@ -92,7 +92,6 @@ namespace Emby.Server.Implementations.Library.Validators
_libraryManager.DeleteItem(item, new DeleteOptions
{
DeleteFileLocation = false
}, false);
}

View File

@@ -1547,7 +1547,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
IsFolder = false,
Recursive = true,
DtoOptions = new DtoOptions(true)
})
.Where(i => i.IsFileProtocol && File.Exists(i.Path))
.Skip(seriesTimer.KeepUpTo - 1)

View File

@@ -183,7 +183,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var subtitleArgs = CopySubtitles ? " -codec:s copy" : " -sn";
//var outputParam = string.Equals(Path.GetExtension(targetFile), ".mp4", StringComparison.OrdinalIgnoreCase) ?
// var outputParam = string.Equals(Path.GetExtension(targetFile), ".mp4", StringComparison.OrdinalIgnoreCase) ?
// " -f mp4 -movflags frag_keyframe+empty_moov" :
// string.Empty;
@@ -206,13 +206,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
return "-codec:a:0 copy";
//var audioChannels = 2;
//var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
//if (audioStream != null)
// var audioChannels = 2;
// var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
// if (audioStream != null)
//{
// audioChannels = audioStream.Channels ?? audioChannels;
//}
//return "-codec:a:0 aac -strict experimental -ab 320000";
// return "-codec:a:0 aac -strict experimental -ab 320000";
}
private static bool EncodeVideo(MediaSourceInfo mediaSource)

View File

@@ -56,7 +56,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
name += " " + info.EpisodeTitle;
}
}
else if (info.IsMovie && info.ProductionYear != null)
{
name += " (" + info.ProductionYear + ")";

View File

@@ -145,7 +145,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var programsInfo = new List<ProgramInfo>();
foreach (ScheduleDirect.Program schedule in dailySchedules.SelectMany(d => d.programs))
{
//_logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
// _logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
// " which corresponds to channel " + channelNumber + " and program id " +
// schedule.programID + " which says it has images? " +
// programDict[schedule.programID].hasImageArtwork);
@@ -178,7 +178,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programEntry.backdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
//programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
// programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
// GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
// GetProgramImage(ApiUrl, data, "Banner-LO", false) ??
// GetProgramImage(ApiUrl, data, "Banner-LOT", false);
@@ -212,6 +212,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
channelNumber = map.channel;
}
if (string.IsNullOrWhiteSpace(channelNumber))
{
channelNumber = map.atscMajor + "." + map.atscMinor;
@@ -276,7 +277,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
CommunityRating = null,
EpisodeTitle = episodeTitle,
Audio = audioType,
//IsNew = programInfo.@new ?? false,
// IsNew = programInfo.@new ?? false,
IsRepeat = programInfo.@new == null,
IsSeries = string.Equals(details.entityType, "episode", StringComparison.OrdinalIgnoreCase),
ImageUrl = details.primaryImage,
@@ -400,6 +401,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
}
return date;
}
@@ -622,6 +624,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
_lastErrorResponse = DateTime.UtcNow;
}
}
throw;
}
finally
@@ -701,7 +704,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
CancellationToken = cancellationToken,
LogErrorResponseBody = true
};
//_logger.LogInformation("Obtaining token from Schedules Direct from addres: " + httpOptions.Url + " with body " +
// _logger.LogInformation("Obtaining token from Schedules Direct from addres: " + httpOptions.Url + " with body " +
// httpOptions.RequestContent);
using (var response = await Post(httpOptions, false, null).ConfigureAwait(false))
@@ -805,11 +808,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
throw new ArgumentException("Username is required");
}
if (string.IsNullOrEmpty(info.Password))
{
throw new ArgumentException("Password is required");
}
}
if (validateListings)
{
if (string.IsNullOrEmpty(info.ListingsId))
@@ -932,24 +937,35 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public class Token
{
public int code { get; set; }
public string message { get; set; }
public string serverID { get; set; }
public string token { get; set; }
}
public class Lineup
{
public string lineup { get; set; }
public string name { get; set; }
public string transport { get; set; }
public string location { get; set; }
public string uri { get; set; }
}
public class Lineups
{
public int code { get; set; }
public string serverID { get; set; }
public string datetime { get; set; }
public List<Lineup> lineups { get; set; }
}
@@ -957,8 +973,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public class Headends
{
public string headend { get; set; }
public string transport { get; set; }
public string location { get; set; }
public List<Lineup> lineups { get; set; }
}
@@ -967,59 +986,83 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public class Map
{
public string stationID { get; set; }
public string channel { get; set; }
public string logicalChannelNumber { get; set; }
public int uhfVhf { get; set; }
public int atscMajor { get; set; }
public int atscMinor { get; set; }
}
public class Broadcaster
{
public string city { get; set; }
public string state { get; set; }
public string postalcode { get; set; }
public string country { get; set; }
}
public class Logo
{
public string URL { get; set; }
public int height { get; set; }
public int width { get; set; }
public string md5 { get; set; }
}
public class Station
{
public string stationID { get; set; }
public string name { get; set; }
public string callsign { get; set; }
public List<string> broadcastLanguage { get; set; }
public List<string> descriptionLanguage { get; set; }
public Broadcaster broadcaster { get; set; }
public string affiliate { get; set; }
public Logo logo { get; set; }
public bool? isCommercialFree { get; set; }
}
public class Metadata
{
public string lineup { get; set; }
public string modified { get; set; }
public string transport { get; set; }
}
public class Channel
{
public List<Map> map { get; set; }
public List<Station> stations { get; set; }
public Metadata metadata { get; set; }
}
public class RequestScheduleForChannel
{
public string stationID { get; set; }
public List<string> date { get; set; }
}
@@ -1029,29 +1072,43 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public class Rating
{
public string body { get; set; }
public string code { get; set; }
}
public class Multipart
{
public int partNumber { get; set; }
public int totalParts { get; set; }
}
public class Program
{
public string programID { get; set; }
public string airDateTime { get; set; }
public int duration { get; set; }
public string md5 { get; set; }
public List<string> audioProperties { get; set; }
public List<string> videoProperties { get; set; }
public List<Rating> ratings { get; set; }
public bool? @new { get; set; }
public Multipart multipart { get; set; }
public string liveTapeDelay { get; set; }
public bool premiere { get; set; }
public bool repeat { get; set; }
public string isPremiereOrFinale { get; set; }
}
@@ -1060,16 +1117,22 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public class MetadataSchedule
{
public string modified { get; set; }
public string md5 { get; set; }
public string startDate { get; set; }
public string endDate { get; set; }
public int days { get; set; }
}
public class Day
{
public string stationID { get; set; }
public List<Program> programs { get; set; }
public MetadataSchedule metadata { get; set; }
public Day()
@@ -1092,24 +1155,28 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public class Description100
{
public string descriptionLanguage { get; set; }
public string description { get; set; }
}
public class Description1000
{
public string descriptionLanguage { get; set; }
public string description { get; set; }
}
public class DescriptionsProgram
{
public List<Description100> description100 { get; set; }
public List<Description1000> description1000 { get; set; }
}
public class Gracenote
{
public int season { get; set; }
public int episode { get; set; }
}
@@ -1121,104 +1188,154 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public class ContentRating
{
public string body { get; set; }
public string code { get; set; }
}
public class Cast
{
public string billingOrder { get; set; }
public string role { get; set; }
public string nameId { get; set; }
public string personId { get; set; }
public string name { get; set; }
public string characterName { get; set; }
}
public class Crew
{
public string billingOrder { get; set; }
public string role { get; set; }
public string nameId { get; set; }
public string personId { get; set; }
public string name { get; set; }
}
public class QualityRating
{
public string ratingsBody { get; set; }
public string rating { get; set; }
public string minRating { get; set; }
public string maxRating { get; set; }
public string increment { get; set; }
}
public class Movie
{
public string year { get; set; }
public int duration { get; set; }
public List<QualityRating> qualityRating { get; set; }
}
public class Recommendation
{
public string programID { get; set; }
public string title120 { get; set; }
}
public class ProgramDetails
{
public string audience { get; set; }
public string programID { get; set; }
public List<Title> titles { get; set; }
public EventDetails eventDetails { get; set; }
public DescriptionsProgram descriptions { get; set; }
public string originalAirDate { get; set; }
public List<string> genres { get; set; }
public string episodeTitle150 { get; set; }
public List<MetadataPrograms> metadata { get; set; }
public List<ContentRating> contentRating { get; set; }
public List<Cast> cast { get; set; }
public List<Crew> crew { get; set; }
public string entityType { get; set; }
public string showType { get; set; }
public bool hasImageArtwork { get; set; }
public string primaryImage { get; set; }
public string thumbImage { get; set; }
public string backdropImage { get; set; }
public string bannerImage { get; set; }
public string imageID { get; set; }
public string md5 { get; set; }
public List<string> contentAdvisory { get; set; }
public Movie movie { get; set; }
public List<Recommendation> recommendations { get; set; }
}
public class Caption
{
public string content { get; set; }
public string lang { get; set; }
}
public class ImageData
{
public string width { get; set; }
public string height { get; set; }
public string uri { get; set; }
public string size { get; set; }
public string aspect { get; set; }
public string category { get; set; }
public string text { get; set; }
public string primary { get; set; }
public string tier { get; set; }
public Caption caption { get; set; }
}
public class ShowImages
{
public string programID { get; set; }
public List<ImageData> data { get; set; }
}
}
}
}

View File

@@ -224,6 +224,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
uniqueString = "-" + programInfo.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture);
}
if (programInfo.EpisodeNumber.HasValue)
{
uniqueString = "-" + programInfo.EpisodeNumber.Value.ToString(CultureInfo.InvariantCulture);

View File

@@ -406,8 +406,8 @@ namespace Emby.Server.Implementations.LiveTv
if (!(service is EmbyTV.EmbyTV))
{
// We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
//mediaSource.SupportsDirectPlay = false;
//mediaSource.SupportsDirectStream = false;
// mediaSource.SupportsDirectPlay = false;
// mediaSource.SupportsDirectStream = false;
mediaSource.SupportsTranscoding = true;
foreach (var stream in mediaSource.MediaStreams)
{
@@ -556,9 +556,10 @@ namespace Emby.Server.Implementations.LiveTv
{
forceUpdate = true;
}
item.ParentId = channel.Id;
//item.ChannelType = channelType;
// item.ChannelType = channelType;
item.Audio = info.Audio;
item.ChannelId = channel.Id;
@@ -575,6 +576,7 @@ namespace Emby.Server.Implementations.LiveTv
{
forceUpdate = true;
}
item.ExternalSeriesId = seriesId;
var isSeries = info.IsSeries || !string.IsNullOrEmpty(info.EpisodeTitle);
@@ -589,30 +591,37 @@ namespace Emby.Server.Implementations.LiveTv
{
tags.Add("Live");
}
if (info.IsPremiere)
{
tags.Add("Premiere");
}
if (info.IsNews)
{
tags.Add("News");
}
if (info.IsSports)
{
tags.Add("Sports");
}
if (info.IsKids)
{
tags.Add("Kids");
}
if (info.IsRepeat)
{
tags.Add("Repeat");
}
if (info.IsMovie)
{
tags.Add("Movie");
}
if (isSeries)
{
tags.Add("Series");
@@ -635,6 +644,7 @@ namespace Emby.Server.Implementations.LiveTv
{
forceUpdate = true;
}
item.IsSeries = isSeries;
item.Name = info.Name;
@@ -652,12 +662,14 @@ namespace Emby.Server.Implementations.LiveTv
{
forceUpdate = true;
}
item.StartDate = info.StartDate;
if (item.EndDate != info.EndDate)
{
forceUpdate = true;
}
item.EndDate = info.EndDate;
item.ProductionYear = info.ProductionYear;
@@ -1168,7 +1180,6 @@ namespace Emby.Server.Implementations.LiveTv
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
ChannelIds = new Guid[] { currentChannel.Id },
DtoOptions = new DtoOptions(true)
}).Cast<LiveTvProgram>().ToDictionary(i => i.Id);
var newPrograms = new List<LiveTvProgram>();
@@ -1368,10 +1379,10 @@ namespace Emby.Server.Implementations.LiveTv
// limit = (query.Limit ?? 10) * 2;
limit = null;
//var allActivePaths = EmbyTV.EmbyTV.Current.GetAllActiveRecordings().Select(i => i.Path).ToArray();
//var items = allActivePaths.Select(i => _libraryManager.FindByPath(i, false)).Where(i => i != null).ToArray();
// var allActivePaths = EmbyTV.EmbyTV.Current.GetAllActiveRecordings().Select(i => i.Path).ToArray();
// var items = allActivePaths.Select(i => _libraryManager.FindByPath(i, false)).Where(i => i != null).ToArray();
//return new QueryResult<BaseItem>
// return new QueryResult<BaseItem>
//{
// Items = items,
// TotalRecordCount = items.Length
@@ -1738,7 +1749,6 @@ namespace Emby.Server.Implementations.LiveTv
var results = await GetTimers(new TimerQuery
{
Id = id
}, cancellationToken).ConfigureAwait(false);
return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
@@ -1790,7 +1800,6 @@ namespace Emby.Server.Implementations.LiveTv
.Select(i =>
{
return i.Item1;
})
.ToArray();
@@ -1845,7 +1854,6 @@ namespace Emby.Server.Implementations.LiveTv
}
return _tvDtoService.GetSeriesTimerInfoDto(i.Item1, i.Item2, channelName);
})
.ToArray();
@@ -1878,7 +1886,6 @@ namespace Emby.Server.Implementations.LiveTv
OrderBy = new[] { (ItemSortBy.StartDate, SortOrder.Ascending) },
TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Id },
DtoOptions = options
}) : new List<BaseItem>();
RemoveFields(options);
@@ -1956,7 +1963,7 @@ namespace Emby.Server.Implementations.LiveTv
OriginalAirDate = program.PremiereDate,
Overview = program.Overview,
StartDate = program.StartDate,
//ImagePath = program.ExternalImagePath,
// ImagePath = program.ExternalImagePath,
Name = program.Name,
OfficialRating = program.OfficialRating
};
@@ -2456,7 +2463,6 @@ namespace Emby.Server.Implementations.LiveTv
UserId = user.Id,
IsRecordingsFolder = true,
RefreshLatestChannelItems = refreshChannels
}).Items);
return folders.Cast<BaseItem>().ToList();

View File

@@ -35,7 +35,7 @@ namespace Emby.Server.Implementations.LiveTv
}
/// <summary>
/// Creates the triggers that define when the task will run
/// Creates the triggers that define when the task will run.
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()

View File

@@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var result = await GetChannelsInternal(tuner, cancellationToken).ConfigureAwait(false);
var list = result.ToList();
//logger.LogInformation("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list));
// logger.LogInformation("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list));
if (!string.IsNullOrEmpty(key) && list.Count > 0)
{
@@ -99,7 +99,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
catch (IOException)
{
}
}
}
@@ -116,7 +115,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
catch (IOException)
{
}
}
}

View File

@@ -111,7 +111,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
ChannelType = ChannelType.TV,
IsLegacyTuner = (i.URL ?? string.Empty).StartsWith("hdhomerun", StringComparison.OrdinalIgnoreCase),
Path = i.URL
}).Cast<ChannelInfo>().ToList();
}
@@ -171,6 +170,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
_modelCache[cacheKey] = response;
}
}
return response;
}
@@ -201,7 +201,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
var name = line.Substring(0, index - 1);
var currentChannel = line.Substring(index + 7);
if (currentChannel != "none") { status = LiveTvTunerStatus.LiveTv; } else { status = LiveTvTunerStatus.Available; }
if (currentChannel != "none")
{
status = LiveTvTunerStatus.LiveTv;
}
else
{
status = LiveTvTunerStatus.Available;
}
tuners.Add(new LiveTvTunerInfo
{
Name = name,
@@ -230,11 +238,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
inside = true;
continue;
}
if (let == '>')
{
inside = false;
continue;
}
if (!inside)
{
buffer[bufferIndex] = let;
@@ -332,12 +342,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private class Channels
{
public string GuideNumber { get; set; }
public string GuideName { get; set; }
public string VideoCodec { get; set; }
public string AudioCodec { get; set; }
public string URL { get; set; }
public bool Favorite { get; set; }
public bool DRM { get; set; }
public int HD { get; set; }
}
@@ -481,7 +498,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
Height = height,
BitRate = videoBitrate,
NalLengthSize = nal
},
new MediaStream
{
@@ -502,8 +518,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
SupportsTranscoding = true,
IsInfiniteStream = true,
IgnoreDts = true,
//IgnoreIndex = true,
//ReadAtNativeFramerate = true
// IgnoreIndex = true,
// ReadAtNativeFramerate = true
};
mediaSource.InferTotalBitrate();
@@ -659,13 +675,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public class DiscoverResponse
{
public string FriendlyName { get; set; }
public string ModelNumber { get; set; }
public string FirmwareName { get; set; }
public string FirmwareVersion { get; set; }
public string DeviceID { get; set; }
public string DeviceAuth { get; set; }
public string BaseURL { get; set; }
public string LineupURL { get; set; }
public int TunerCount { get; set; }
public bool SupportsTranscoding
@@ -674,7 +698,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
var model = ModelNumber ?? string.Empty;
if ((model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
if (model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1)
{
return true;
}
@@ -722,7 +746,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
}
}
catch (OperationCanceledException)
{

View File

@@ -117,17 +117,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
taskCompletionSource,
LiveStreamCancellationTokenSource.Token).ConfigureAwait(false);
//OpenedMediaSource.Protocol = MediaProtocol.File;
//OpenedMediaSource.Path = tempFile;
//OpenedMediaSource.ReadAtNativeFramerate = true;
// OpenedMediaSource.Protocol = MediaProtocol.File;
// OpenedMediaSource.Path = tempFile;
// OpenedMediaSource.ReadAtNativeFramerate = true;
MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
MediaSource.Protocol = MediaProtocol.Http;
//OpenedMediaSource.SupportsDirectPlay = false;
//OpenedMediaSource.SupportsDirectStream = true;
//OpenedMediaSource.SupportsTranscoding = true;
// OpenedMediaSource.SupportsDirectPlay = false;
// OpenedMediaSource.SupportsDirectStream = true;
// OpenedMediaSource.SupportsTranscoding = true;
//await Task.Delay(5000).ConfigureAwait(false);
// await Task.Delay(5000).ConfigureAwait(false);
await taskCompletionSource.Task.ConfigureAwait(false);
}

View File

@@ -58,12 +58,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected virtual int EmptyReadLimit => 1000;
public MediaSourceInfo OriginalMediaSource { get; set; }
public MediaSourceInfo MediaSource { get; set; }
public int ConsumerCount { get; set; }
public string OriginalStreamId { get; set; }
public bool EnableStreamSharing { get; set; }
public string UniqueId { get; }
public string TunerHostId { get; }
@@ -220,11 +223,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
catch (IOException)
{
}
catch (ArgumentException)
{
}
catch (Exception ex)
{

View File

@@ -127,7 +127,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
using (var stream = await new M3uParser(Logger, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
{
}
}

View File

@@ -210,7 +210,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
}
}
}
if (!IsValidChannelNumber(numberString))
@@ -284,7 +283,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out var number))
{
//channel.Number = number.ToString();
// channel.Number = number.ToString();
nameInExtInf = nameInExtInf.Substring(numberIndex + 1).Trim(new[] { ' ', '-' });
}
}

View File

@@ -103,21 +103,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
_ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
//OpenedMediaSource.Protocol = MediaProtocol.File;
//OpenedMediaSource.Path = tempFile;
//OpenedMediaSource.ReadAtNativeFramerate = true;
// OpenedMediaSource.Protocol = MediaProtocol.File;
// OpenedMediaSource.Path = tempFile;
// OpenedMediaSource.ReadAtNativeFramerate = true;
MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
MediaSource.Protocol = MediaProtocol.Http;
//OpenedMediaSource.Path = TempFilePath;
//OpenedMediaSource.Protocol = MediaProtocol.File;
// OpenedMediaSource.Path = TempFilePath;
// OpenedMediaSource.Protocol = MediaProtocol.File;
//OpenedMediaSource.Path = _tempFilePath;
//OpenedMediaSource.Protocol = MediaProtocol.File;
//OpenedMediaSource.SupportsDirectPlay = false;
//OpenedMediaSource.SupportsDirectStream = true;
//OpenedMediaSource.SupportsTranscoding = true;
// OpenedMediaSource.Path = _tempFilePath;
// OpenedMediaSource.Protocol = MediaProtocol.File;
// OpenedMediaSource.SupportsDirectPlay = false;
// OpenedMediaSource.SupportsDirectStream = true;
// OpenedMediaSource.SupportsTranscoding = true;
await taskCompletionSource.Task.ConfigureAwait(false);
if (taskCompletionSource.Task.Exception != null)
{

View File

@@ -19,8 +19,8 @@
"Sync": "Sinkroniseer",
"HeaderFavoriteSongs": "Gunsteling Liedjies",
"Songs": "Liedjies",
"DeviceOnlineWithName": "{0} is verbind",
"DeviceOfflineWithName": "{0} het afgesluit",
"DeviceOnlineWithName": "{0} gekoppel is",
"DeviceOfflineWithName": "{0} is ontkoppel",
"Collections": "Versamelings",
"Inherit": "Ontvang",
"HeaderLiveTV": "Live TV",
@@ -91,5 +91,9 @@
"ChapterNameValue": "Hoofstuk",
"CameraImageUploadedFrom": "'n Nuwe kamera photo opgelaai van {0}",
"AuthenticationSucceededWithUserName": "{0} suksesvol geverifieer",
"Albums": "Albums"
"Albums": "Albums",
"TasksChannelsCategory": "Internet kanale",
"TasksApplicationCategory": "aansoek",
"TasksLibraryCategory": "biblioteek",
"TasksMaintenanceCategory": "onderhoud"
}

View File

@@ -1,5 +1,5 @@
{
"Albums": "ألبومات",
"Albums": "البومات",
"AppDeviceValues": "تطبيق: {0}, جهاز: {1}",
"Application": "تطبيق",
"Artists": "الفنانين",
@@ -14,7 +14,7 @@
"FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}",
"Favorites": "المفضلة",
"Folders": "المجلدات",
"Genres": "الأنواع",
"Genres": "التضنيفات",
"HeaderAlbumArtists": "فناني الألبومات",
"HeaderCameraUploads": "تحميلات الكاميرا",
"HeaderContinueWatching": "استئناف",
@@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي",
"NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي",
"NotificationOptionCameraImageUploaded": "تم رفع صورة الكاميرا",
"NotificationOptionInstallationFailed": "فشل في التثبيت",
"NotificationOptionInstallationFailed": "فشل التثبيت",
"NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد",
"NotificationOptionPluginError": "فشل في البرنامج المضاف",
"NotificationOptionPluginInstalled": "تم تثبيت الملحق",

View File

@@ -62,13 +62,13 @@
"NotificationOptionPluginInstalled": "প্লাগিন ইন্সটল করা হয়েছে",
"NotificationOptionPluginError": "প্লাগিন ব্যর্থ",
"NotificationOptionNewLibraryContent": "নতুন কন্টেন্ট যোগ করা হয়েছে",
"NotificationOptionInstallationFailed": "ইন্সটল ব্যর্থ",
"NotificationOptionInstallationFailed": "ইন্সটল ব্যর্থ হয়েছে",
"NotificationOptionCameraImageUploaded": "ক্যামেরার ছবি আপলোড হয়েছে",
"NotificationOptionAudioPlaybackStopped": "গান বাজা বন্ধ হয়েছে",
"NotificationOptionAudioPlayback": "গান বাজা শুরু হয়েছে",
"NotificationOptionApplicationUpdateInstalled": "এপ্লিকেশনের আপডেট ইনস্টল করা হয়েছে",
"NotificationOptionApplicationUpdateAvailable": "এপ্লিকেশনের আপডেট রয়েছে",
"NewVersionIsAvailable": "জেলিফিন সার্ভারের একটি নতুন ভার্শন ডাউনলোডের জন্য তৈরী",
"NewVersionIsAvailable": "জেলিফিন সার্ভারের একটি নতুন ভার্শন ডাউনলোডের জন্য তৈরী",
"NameSeasonUnknown": "সিজন অজানা",
"NameSeasonNumber": "সিজন {0}",
"NameInstallFailed": "{0} ইন্সটল ব্যর্থ",
@@ -100,5 +100,18 @@
"TaskCleanCacheDescription": "সিস্টেমে আর প্রয়োজন নেই ক্যাশ, ফাইলগুলি মুছে ফেলুন।",
"TaskCleanCache": "ক্লিন ক্যাশ ডিরেক্টরি",
"TasksChannelsCategory": "ইন্টারনেট চ্যানেল",
"TasksApplicationCategory": "আবেদন"
"TasksApplicationCategory": "আবেদন",
"TaskDownloadMissingSubtitlesDescription": "মেটাডেটা কনফিগারেশনের উপর ভিত্তি করে অনুপস্থিত সাবটাইটেলগুলির জন্য ইন্টারনেট অনুসন্ধান করে।",
"TaskDownloadMissingSubtitles": "অনুপস্থিত সাবটাইটেলগুলি ডাউনলোড করুন",
"TaskRefreshChannelsDescription": "ইন্টারনেট চ্যানেল তথ্য রিফ্রেশ করুন।",
"TaskRefreshChannels": "চ্যানেল রিফ্রেশ করুন",
"TaskCleanTranscodeDescription": "এক দিনেরও বেশি পুরানো ট্রান্সকোড ফাইলগুলি মুছে ফেলুন।",
"TaskCleanTranscode": "ট্রান্সকোড ডিরেক্টরি ক্লিন করুন",
"TaskUpdatePluginsDescription": "স্বয়ংক্রিয়ভাবে আপডেট কনফিগার করা প্লাগইনগুলির জন্য আপডেট ডাউনলোড এবং ইনস্টল করুন।",
"TaskUpdatePlugins": "প্লাগইন আপডেট করুন",
"TaskRefreshPeopleDescription": "আপনার মিডিয়া লাইব্রেরিতে অভিনেতা এবং পরিচালকদের জন্য মেটাডাটা আপডেট করুন।",
"TaskRefreshPeople": "পিপল রিফ্রেশ করুন",
"TaskCleanLogsDescription": "{0} দিনের বেশী পুরানো লগ ফাইলগুলি মুছে ফেলুন।",
"TaskCleanLogs": "লগ ডিরেক্টরি ক্লিন করুন",
"TaskRefreshLibraryDescription": "নতুন ফাইলের জন্য মিডিয়া লাইব্রেরি স্ক্যান এবং মেটাডাটা রিফ্রেশ করুন।"
}

View File

@@ -3,7 +3,7 @@
"AppDeviceValues": "App: {0}, Gerät: {1}",
"Application": "Anwendung",
"Artists": "Interpreten",
"AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich authentifiziert",
"AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich angemeldet",
"Books": "Bücher",
"CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen",
"Channels": "Kanäle",

View File

@@ -20,7 +20,7 @@
"HeaderContinueWatching": "Seguir viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteEpisodes": "Capítulos favoritos",
"HeaderFavoriteShows": "Programas favoritos",
"HeaderFavoriteSongs": "Canciones favoritas",
"HeaderLiveTV": "TV en vivo",

View File

@@ -31,7 +31,7 @@
"ItemAddedWithName": "{0} fue agregado a la biblioteca",
"ItemRemovedWithName": "{0} fue removido de la biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}",
"LabelRunningTimeValue": "Duración: {0}",
"LabelRunningTimeValue": "Tiempo de reproducción: {0}",
"Latest": "Recientes",
"MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado",
"MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}",

View File

@@ -1,5 +1,5 @@
{
"LabelRunningTimeValue": "Duración: {0}",
"LabelRunningTimeValue": "Tiempo en ejecución: {0}",
"ValueSpecialEpisodeName": "Especial - {0}",
"Sync": "Sincronizar",
"Songs": "Canciones",

View File

@@ -109,9 +109,10 @@
"TaskCleanLogs": "Nettoyer le répertoire des journaux",
"TaskRefreshLibraryDescription": "Analyse votre bibliothèque média pour trouver de nouveaux fichiers et rafraîchit les métadonnées.",
"TaskRefreshChapterImages": "Extraire les images de chapitre",
"TaskRefreshChapterImagesDescription": "Créer des vignettes pour les vidéos qui ont des chapitres",
"TaskRefreshChapterImagesDescription": "Créer des vignettes pour les vidéos qui ont des chapitres.",
"TaskRefreshLibrary": "Analyser la bibliothèque de médias",
"TaskCleanCache": "Nettoyer le répertoire des fichiers temporaires",
"TasksApplicationCategory": "Application",
"TaskCleanCacheDescription": "Supprime les fichiers temporaires qui ne sont plus nécessaire pour le système."
"TaskCleanCacheDescription": "Supprime les fichiers temporaires qui ne sont plus nécessaire pour le système.",
"TasksChannelsCategory": "Canaux Internet"
}

View File

@@ -5,23 +5,23 @@
"Artists": "Izvođači",
"AuthenticationSucceededWithUserName": "{0} uspješno ovjerena",
"Books": "Knjige",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"CameraImageUploadedFrom": "Nova fotografija sa kamere je uploadana iz {0}",
"Channels": "Kanali",
"ChapterNameValue": "Poglavlje {0}",
"Collections": "Kolekcije",
"DeviceOfflineWithName": "{0} se odspojilo",
"DeviceOnlineWithName": "{0} je spojeno",
"FailedLoginAttemptWithUserName": "Neuspjeli pokušaj prijave za {0}",
"Favorites": "Omiljeni",
"Favorites": "Favoriti",
"Folders": "Mape",
"Genres": "Žanrovi",
"HeaderAlbumArtists": "Izvođači albuma",
"HeaderCameraUploads": "Camera Uploads",
"HeaderContinueWatching": "Continue Watching",
"HeaderAlbumArtists": "Izvođači na albumu",
"HeaderCameraUploads": "Uvoz sa kamere",
"HeaderContinueWatching": "Nastavi gledati",
"HeaderFavoriteAlbums": "Omiljeni albumi",
"HeaderFavoriteArtists": "Omiljeni izvođači",
"HeaderFavoriteEpisodes": "Omiljene epizode",
"HeaderFavoriteShows": "Omiljene emisije",
"HeaderFavoriteShows": "Omiljene serije",
"HeaderFavoriteSongs": "Omiljene pjesme",
"HeaderLiveTV": "TV uživo",
"HeaderNextUp": "Sljedeće je",
@@ -34,23 +34,23 @@
"LabelRunningTimeValue": "Vrijeme rada: {0}",
"Latest": "Najnovije",
"MessageApplicationUpdated": "Jellyfin Server je ažuriran",
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
"MessageApplicationUpdatedTo": "Jellyfin Server je ažuriran na {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Odjeljak postavka servera {0} je ažuriran",
"MessageServerConfigurationUpdated": "Postavke servera su ažurirane",
"MixedContent": "Miješani sadržaj",
"Movies": "Filmovi",
"Music": "Glazba",
"MusicVideos": "Glazbeni spotovi",
"NameInstallFailed": "{0} installation failed",
"NameInstallFailed": "{0} neuspješnih instalacija",
"NameSeasonNumber": "Sezona {0}",
"NameSeasonUnknown": "Season Unknown",
"NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
"NameSeasonUnknown": "Nepoznata sezona",
"NewVersionIsAvailable": "Nova verzija Jellyfin servera je dostupna za preuzimanje.",
"NotificationOptionApplicationUpdateAvailable": "Dostupno ažuriranje aplikacije",
"NotificationOptionApplicationUpdateInstalled": "Instalirano ažuriranje aplikacije",
"NotificationOptionAudioPlayback": "Reprodukcija glazbe započeta",
"NotificationOptionAudioPlaybackStopped": "Reprodukcija audiozapisa je zaustavljena",
"NotificationOptionCameraImageUploaded": "Slike kamere preuzete",
"NotificationOptionInstallationFailed": "Instalacija nije izvršena",
"NotificationOptionInstallationFailed": "Instalacija neuspješna",
"NotificationOptionNewLibraryContent": "Novi sadržaj je dodan",
"NotificationOptionPluginError": "Dodatak otkazao",
"NotificationOptionPluginInstalled": "Dodatak instaliran",
@@ -62,7 +62,7 @@
"NotificationOptionVideoPlayback": "Reprodukcija videa započeta",
"NotificationOptionVideoPlaybackStopped": "Reprodukcija videozapisa je zaustavljena",
"Photos": "Slike",
"Playlists": "Popisi",
"Playlists": "Popis za reprodukciju",
"Plugin": "Dodatak",
"PluginInstalledWithName": "{0} je instalirano",
"PluginUninstalledWithName": "{0} je deinstalirano",
@@ -70,15 +70,15 @@
"ProviderValue": "Pružitelj: {0}",
"ScheduledTaskFailedWithName": "{0} neuspjelo",
"ScheduledTaskStartedWithName": "{0} pokrenuto",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"Shows": "Shows",
"ServerNameNeedsToBeRestarted": "{0} treba biti ponovno pokrenuto",
"Shows": "Serije",
"Songs": "Pjesme",
"StartupEmbyServerIsLoading": "Jellyfin Server se učitava. Pokušajte ponovo kasnije.",
"SubtitleDownloadFailureForItem": "Titlovi prijevoda nisu preuzeti za {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitleDownloadFailureFromForItem": "Prijevodi nisu uspješno preuzeti {0} od {1}",
"Sync": "Sink.",
"System": "Sistem",
"TvShows": "TV Shows",
"TvShows": "Serije",
"User": "Korisnik",
"UserCreatedWithName": "Korisnik {0} je stvoren",
"UserDeletedWithName": "Korisnik {0} je obrisan",
@@ -87,10 +87,10 @@
"UserOfflineFromDevice": "{0} se odspojilo od {1}",
"UserOnlineFromDevice": "{0} je online od {1}",
"UserPasswordChangedWithName": "Lozinka je promijenjena za korisnika {0}",
"UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"UserPolicyUpdatedWithName": "Pravila za korisnika su ažurirana za {0}",
"UserStartedPlayingItemWithValues": "{0} je pokrenuo {1}",
"UserStoppedPlayingItemWithValues": "{0} je zaustavio {1}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"ValueHasBeenAddedToLibrary": "{0} je dodano u medijsku biblioteku",
"ValueSpecialEpisodeName": "Specijal - {0}",
"VersionNumber": "Verzija {0}",
"TaskRefreshLibraryDescription": "Skenira vašu medijsku knjižnicu sa novim datotekama i osvježuje metapodatke.",
@@ -100,5 +100,19 @@
"TaskCleanCacheDescription": "Briše priručne datoteke nepotrebne za sistem.",
"TaskCleanCache": "Očisti priručnu memoriju",
"TasksApplicationCategory": "Aplikacija",
"TasksMaintenanceCategory": "Održavanje"
"TasksMaintenanceCategory": "Održavanje",
"TaskDownloadMissingSubtitlesDescription": "Pretraživanje interneta za prijevodima koji nedostaju bazirano na konfiguraciji meta podataka.",
"TaskDownloadMissingSubtitles": "Preuzimanje prijevoda koji nedostaju",
"TaskRefreshChannelsDescription": "Osvježava informacije o internet kanalima.",
"TaskRefreshChannels": "Osvježi kanale",
"TaskCleanTranscodeDescription": "Briše transkodirane fajlove starije od jednog dana.",
"TaskCleanTranscode": "Očisti direktorij za transkodiranje",
"TaskUpdatePluginsDescription": "Preuzima i instalira ažuriranja za dodatke koji su podešeni da se ažuriraju automatski.",
"TaskUpdatePlugins": "Ažuriraj dodatke",
"TaskRefreshPeopleDescription": "Ažurira meta podatke za glumce i redatelje u vašoj medijskoj biblioteci.",
"TaskRefreshPeople": "Osvježi ljude",
"TaskCleanLogsDescription": "Briši logove koji su stariji od {0} dana.",
"TaskCleanLogs": "Očisti direktorij sa logovima",
"TasksChannelsCategory": "Internet kanali",
"TasksLibraryCategory": "Biblioteka"
}

View File

@@ -57,5 +57,7 @@
"HeaderCameraUploads": "कॅमेरा अपलोड",
"CameraImageUploadedFrom": "एक नवीन कॅमेरा चित्र {0} येथून अपलोड केले आहे",
"Application": "अ‍ॅप्लिकेशन",
"AppDeviceValues": "अ‍ॅप: {0}, यंत्र: {1}"
"AppDeviceValues": "अ‍ॅप: {0}, यंत्र: {1}",
"Collections": "संग्रह",
"ChapterNameValue": "धडा {0}"
}

View File

@@ -5,47 +5,47 @@
"Artists": "Artis",
"AuthenticationSucceededWithUserName": "{0} berjaya disahkan",
"Books": "Buku-buku",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
"CameraImageUploadedFrom": "Ada gambar dari kamera yang baru dimuat naik melalui {0}",
"Channels": "Saluran",
"ChapterNameValue": "Chapter {0}",
"ChapterNameValue": "Bab {0}",
"Collections": "Koleksi",
"DeviceOfflineWithName": "{0} has disconnected",
"DeviceOnlineWithName": "{0} is connected",
"DeviceOfflineWithName": "{0} telah diputuskan sambungan",
"DeviceOnlineWithName": "{0} telah disambung",
"FailedLoginAttemptWithUserName": "Cubaan log masuk gagal dari {0}",
"Favorites": "Favorites",
"Folders": "Folders",
"Favorites": "Kegemaran",
"Folders": "Fail-fail",
"Genres": "Genre-genre",
"HeaderAlbumArtists": "Album Artists",
"HeaderAlbumArtists": "Album Artis-artis",
"HeaderCameraUploads": "Muatnaik Kamera",
"HeaderContinueWatching": "Terus Menonton",
"HeaderFavoriteAlbums": "Favorite Albums",
"HeaderFavoriteArtists": "Favorite Artists",
"HeaderFavoriteEpisodes": "Favorite Episodes",
"HeaderFavoriteShows": "Favorite Shows",
"HeaderFavoriteSongs": "Favorite Songs",
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Next Up",
"HeaderRecordingGroups": "Recording Groups",
"HomeVideos": "Home videos",
"Inherit": "Inherit",
"ItemAddedWithName": "{0} was added to the library",
"ItemRemovedWithName": "{0} was removed from the library",
"HeaderFavoriteAlbums": "Album-album Kegemaran",
"HeaderFavoriteArtists": "Artis-artis Kegemaran",
"HeaderFavoriteEpisodes": "Episod-episod Kegemaran",
"HeaderFavoriteShows": "Rancangan-rancangan Kegemaran",
"HeaderFavoriteSongs": "Lagu-lagu Kegemaran",
"HeaderLiveTV": "TV Siaran Langsung",
"HeaderNextUp": "Seterusnya",
"HeaderRecordingGroups": "Kumpulan-kumpulan Rakaman",
"HomeVideos": "Video Personal",
"Inherit": "Mewarisi",
"ItemAddedWithName": "{0} telah ditambahkan ke dalam pustaka",
"ItemRemovedWithName": "{0} telah dibuang daripada pustaka",
"LabelIpAddressValue": "Alamat IP: {0}",
"LabelRunningTimeValue": "Running time: {0}",
"Latest": "Latest",
"MessageApplicationUpdated": "Jellyfin Server has been updated",
"MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageServerConfigurationUpdated": "Server configuration has been updated",
"MixedContent": "Mixed content",
"Movies": "Movies",
"LabelRunningTimeValue": "Masa berjalan: {0}",
"Latest": "Terbaru",
"MessageApplicationUpdated": "Jellyfin Server telah dikemas kini",
"MessageApplicationUpdatedTo": "Jellyfin Server telah dikemas kini ke {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Konfigurasi pelayan di bahagian {0} telah dikemas kini",
"MessageServerConfigurationUpdated": "Konfigurasi pelayan telah dikemas kini",
"MixedContent": "Kandungan campuran",
"Movies": "Filem",
"Music": "Muzik",
"MusicVideos": "Video muzik",
"NameInstallFailed": "{0} installation failed",
"NameSeasonNumber": "Season {0}",
"NameSeasonUnknown": "Season Unknown",
"NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.",
"NotificationOptionApplicationUpdateAvailable": "Application update available",
"NameInstallFailed": "{0} pemasangan gagal",
"NameSeasonNumber": "Musim {0}",
"NameSeasonUnknown": "Musim Tidak Diketahui",
"NewVersionIsAvailable": "Versi terbaru Jellyfin Server bersedia untuk dimuat turunkan.",
"NotificationOptionApplicationUpdateAvailable": "Kemas kini aplikasi telah sedia",
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
"NotificationOptionAudioPlayback": "Audio playback started",
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",

View File

@@ -0,0 +1,86 @@
{
"NotificationOptionUserLockedOut": "प्रयोगकर्ता प्रतिबन्धित",
"NotificationOptionTaskFailed": "निर्धारित कार्य विफलता",
"NotificationOptionServerRestartRequired": "सर्भर रिस्टार्ट आवाश्यक छ",
"NotificationOptionPluginUpdateInstalled": "प्लगइन अद्यावधिक स्थापना भयो",
"NotificationOptionPluginUninstalled": "प्लगइन विस्थापित",
"NotificationOptionPluginInstalled": "प्लगइन स्थापना भयो",
"NotificationOptionPluginError": "प्लगइन असफलता",
"NotificationOptionNewLibraryContent": "नयाँ सामग्री थपियो",
"NotificationOptionInstallationFailed": "स्थापना असफलता",
"NotificationOptionCameraImageUploaded": "क्यामेरा फोटो अपलोड गरियो",
"NotificationOptionAudioPlaybackStopped": "ध्वनि प्रक्षेपण रोकियो",
"NotificationOptionAudioPlayback": "ध्वनि प्रक्षेपण शुरू भयो",
"NotificationOptionApplicationUpdateInstalled": "अनुप्रयोग अद्यावधिक स्थापना भयो",
"NotificationOptionApplicationUpdateAvailable": "अनुप्रयोग अपडेट उपलब्ध छ",
"NewVersionIsAvailable": "जेलीफिन सर्भर को नयाँ संस्करण डाउनलोड को लागी उपलब्ध छ।",
"NameSeasonUnknown": "अज्ञात श्रृंखला",
"NameSeasonNumber": "श्रृंखला {0}",
"NameInstallFailed": "{0} स्थापना असफल भयो",
"MusicVideos": "सांगीतिक भिडियोहरू",
"Music": "संगीत",
"Movies": "चलचित्रहरू",
"MixedContent": "मिश्रित सामग्री",
"MessageServerConfigurationUpdated": "सर्भर कन्फिगरेसन अद्यावधिक गरिएको छ",
"MessageNamedServerConfigurationUpdatedWithValue": "सर्भर कन्फिगरेसन विभाग {0} अद्यावधिक गरिएको छ",
"MessageApplicationUpdatedTo": "जेलीफिन सर्भर {0} मा अद्यावधिक गरिएको छ",
"MessageApplicationUpdated": "जेलीफिन सर्भर अपडेट गरिएको छ",
"Latest": "नविनतम",
"LabelRunningTimeValue": "कुल समय: {0}",
"LabelIpAddressValue": "आईपी ठेगाना: {0}",
"ItemRemovedWithName": "{0}लाई पुस्तकालयबाट हटाईयो",
"ItemAddedWithName": "{0} लाईब्रेरीमा थपियो",
"Inherit": "इनहेरिट",
"HomeVideos": "घरेलु भिडियोहरू",
"HeaderRecordingGroups": "रेकर्ड समूहहरू",
"HeaderNextUp": "आगामी",
"HeaderLiveTV": "प्रत्यक्ष टिभी",
"HeaderFavoriteSongs": "मनपर्ने गीतहरू",
"HeaderFavoriteShows": "मनपर्ने कार्यक्रमहरू",
"HeaderFavoriteEpisodes": "मनपर्ने एपिसोडहरू",
"HeaderFavoriteArtists": "मनपर्ने कलाकारहरू",
"HeaderFavoriteAlbums": "मनपर्ने एल्बमहरू",
"HeaderContinueWatching": "हेर्न जारी राख्नुहोस्",
"HeaderCameraUploads": "क्यामेरा अपलोडहरू",
"HeaderAlbumArtists": "एल्बमका कलाकारहरू",
"Genres": "विधाहरू",
"Folders": "फोल्डरहरू",
"Favorites": "मनपर्ने",
"FailedLoginAttemptWithUserName": "{0}को लग इन प्रयास असफल",
"DeviceOnlineWithName": "{0}को साथ जडित",
"DeviceOfflineWithName": "{0}बाट विच्छेदन भयो",
"Collections": "संग्रह",
"ChapterNameValue": "अध्याय {0}",
"Channels": "च्यानलहरू",
"AppDeviceValues": "अनुप्रयोग: {0}, उपकरण: {1}",
"AuthenticationSucceededWithUserName": "{0} सफलतापूर्वक प्रमाणीकरण गरियो",
"CameraImageUploadedFrom": "{0}बाट नयाँ क्यामेरा छवि अपलोड गरिएको छ",
"Books": "पुस्तकहरु",
"Artists": "कलाकारहरू",
"Application": "अनुप्रयोगहरू",
"Albums": "एल्बमहरू",
"TasksLibraryCategory": "पुस्तकालय",
"TasksApplicationCategory": "अनुप्रयोग",
"TasksMaintenanceCategory": "मर्मत",
"UserPolicyUpdatedWithName": "प्रयोगकर्ता नीति को लागी अद्यावधिक गरिएको छ {0}",
"UserPasswordChangedWithName": "पासवर्ड प्रयोगकर्ताका लागि परिवर्तन गरिएको छ {0}",
"UserOnlineFromDevice": "{0} बाट अनलाइन छ {1}",
"UserOfflineFromDevice": "{0} बाट विच्छेदन भएको छ {1}",
"UserLockedOutWithName": "प्रयोगकर्ता {0} लक गरिएको छ",
"UserDeletedWithName": "प्रयोगकर्ता {0} हटाइएको छ",
"UserCreatedWithName": "प्रयोगकर्ता {0} सिर्जना गरिएको छ",
"User": "प्रयोगकर्ता",
"PluginInstalledWithName": "",
"StartupEmbyServerIsLoading": "Jellyfin सर्भर लोड हुँदैछ। कृपया छिट्टै फेरि प्रयास गर्नुहोस्।",
"Songs": "गीतहरू",
"Shows": "शोहरू",
"ServerNameNeedsToBeRestarted": "{0} लाई पुन: सुरु गर्नु पर्छ",
"ScheduledTaskStartedWithName": "{0} सुरु भयो",
"ScheduledTaskFailedWithName": "{0} असफल",
"ProviderValue": "प्रदायक: {0}",
"Plugin": "प्लगइनहरू",
"Playlists": "प्लेलिस्टहरू",
"Photos": "तस्बिरहरु",
"NotificationOptionVideoPlaybackStopped": "भिडियो प्लेब्याक रोकियो",
"NotificationOptionVideoPlayback": "भिडियो प्लेब्याक सुरु भयो"
}

View File

@@ -101,7 +101,17 @@
"TaskCleanLogsDescription": "Deletar arquivos de log que existe a mais de {0} dias.",
"TaskCleanLogs": "Limpar diretório de log",
"TaskRefreshLibrary": "Escanear biblioteca de mídias",
"TaskRefreshChapterImagesDescription": "Criar miniaturas para videos que tem capítulos.",
"TaskCleanCacheDescription": "Deletar arquivos de cache que não são mais usados pelo sistema.",
"TasksChannelsCategory": "Canais de Internet"
"TaskRefreshChapterImagesDescription": "Cria miniaturas para vídeos que têm capítulos.",
"TaskCleanCacheDescription": "Apaga ficheiros em cache que não são usados pelo sistema.",
"TasksChannelsCategory": "Canais de Internet",
"TaskRefreshChapterImages": "Extrair Imagens do Capítulo",
"TaskDownloadMissingSubtitlesDescription": "Pesquisa na Internet as legendas em falta com base na configuração de metadados.",
"TaskDownloadMissingSubtitles": "Download das legendas em falta",
"TaskRefreshChannelsDescription": "Atualiza as informações do canal da Internet.",
"TaskCleanTranscodeDescription": "Apagar os ficheiros com mais de um dia, de Transcode.",
"TaskCleanTranscode": "Limpar o diretório de Transcode",
"TaskUpdatePluginsDescription": "Download e instala as atualizações para plug-ins configurados para atualização automática.",
"TaskRefreshPeopleDescription": "Atualiza os metadados para atores e diretores na tua biblioteca de media.",
"TaskRefreshPeople": "Atualizar pessoas",
"TaskRefreshLibraryDescription": "Pesquisa a tua biblioteca de media por novos ficheiros e atualiza os metadados."
}

View File

@@ -67,5 +67,7 @@
"Artists": "นักแสดง",
"Application": "แอปพลิเคชั่น",
"AppDeviceValues": "App: {0}, อุปกรณ์: {1}",
"Albums": "อัลบั้ม"
"Albums": "อัลบั้ม",
"ScheduledTaskStartedWithName": "{0} เริ่มต้น",
"ScheduledTaskFailedWithName": "{0} ล้มเหลว"
}

View File

@@ -1,6 +1,6 @@
{
"Albums": "專輯",
"AppDeviceValues": "軟件: {0}, 設備: {1}",
"AppDeviceValues": "程式: {0}, 設備: {1}",
"Application": "應用程式",
"Artists": "藝人",
"AuthenticationSucceededWithUserName": "{0} 授權成功",
@@ -113,5 +113,6 @@
"TaskCleanCacheDescription": "刪除系統不再需要的緩存文件。",
"TaskCleanCache": "清理緩存目錄",
"TasksChannelsCategory": "互聯網頻道",
"TasksLibraryCategory": "庫"
"TasksLibraryCategory": "庫",
"TaskRefreshPeople": "刷新人物"
}

View File

@@ -19,6 +19,7 @@ namespace Emby.Server.Implementations.Net
var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
try
{
retVal.EnableBroadcast = true;
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
@@ -46,6 +47,7 @@ namespace Emby.Server.Implementations.Net
var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
try
{
retVal.EnableBroadcast = true;
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
@@ -98,7 +100,6 @@ namespace Emby.Server.Implementations.Net
}
catch (SocketException)
{
}
try
@@ -109,12 +110,12 @@ namespace Emby.Server.Implementations.Net
}
catch (SocketException)
{
}
try
{
//retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
retVal.EnableBroadcast = true;
// retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive);
var localIp = IPAddress.Any;

View File

@@ -37,7 +37,10 @@ namespace Emby.Server.Implementations.Net
public UdpSocket(Socket socket, int localPort, IPAddress ip)
{
if (socket == null) throw new ArgumentNullException(nameof(socket));
if (socket == null)
{
throw new ArgumentNullException(nameof(socket));
}
_socket = socket;
_localPort = localPort;
@@ -103,7 +106,10 @@ namespace Emby.Server.Implementations.Net
public UdpSocket(Socket socket, IPEndPoint endPoint)
{
if (socket == null) throw new ArgumentNullException(nameof(socket));
if (socket == null)
{
throw new ArgumentNullException(nameof(socket));
}
_socket = socket;
_socket.Connect(endPoint);

View File

@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
@@ -13,6 +12,9 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Networking
{
/// <summary>
/// Class to take care of network interface management.
/// </summary>
public class NetworkManager : INetworkManager
{
private readonly ILogger<NetworkManager> _logger;
@@ -21,8 +23,14 @@ namespace Emby.Server.Implementations.Networking
private readonly object _localIpAddressSyncLock = new object();
private readonly object _subnetLookupLock = new object();
private Dictionary<string, List<string>> _subnetLookup = new Dictionary<string, List<string>>(StringComparer.Ordinal);
private readonly Dictionary<string, List<string>> _subnetLookup = new Dictionary<string, List<string>>(StringComparer.Ordinal);
private List<PhysicalAddress> _macAddresses;
/// <summary>
/// Initializes a new instance of the <see cref="NetworkManager"/> class.
/// </summary>
/// <param name="logger">Logger to use for messages.</param>
public NetworkManager(ILogger<NetworkManager> logger)
{
_logger = logger;
@@ -31,8 +39,10 @@ namespace Emby.Server.Implementations.Networking
NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
}
/// <inheritdoc/>
public event EventHandler NetworkChanged;
/// <inheritdoc/>
public Func<string[]> LocalSubnetsFn { get; set; }
private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
@@ -58,13 +68,14 @@ namespace Emby.Server.Implementations.Networking
NetworkChanged?.Invoke(this, EventArgs.Empty);
}
public IPAddress[] GetLocalIpAddresses(bool ignoreVirtualInterface = true)
/// <inheritdoc/>
public IPAddress[] GetLocalIpAddresses()
{
lock (_localIpAddressSyncLock)
{
if (_localIpAddresses == null)
{
var addresses = GetLocalIpAddressesInternal(ignoreVirtualInterface).ToArray();
var addresses = GetLocalIpAddressesInternal().ToArray();
_localIpAddresses = addresses;
}
@@ -73,42 +84,47 @@ namespace Emby.Server.Implementations.Networking
}
}
private List<IPAddress> GetLocalIpAddressesInternal(bool ignoreVirtualInterface)
private List<IPAddress> GetLocalIpAddressesInternal()
{
var list = GetIPsDefault(ignoreVirtualInterface).ToList();
var list = GetIPsDefault().ToList();
if (list.Count == 0)
{
list = GetLocalIpAddressesFallback().GetAwaiter().GetResult().ToList();
}
var listClone = list.ToList();
var listClone = new List<IPAddress>();
return list
var subnets = LocalSubnetsFn();
foreach (var i in list)
{
if (i.IsIPv6LinkLocal || i.ToString().StartsWith("169.254.", StringComparison.OrdinalIgnoreCase))
{
continue;
}
if (Array.IndexOf(subnets, $"[{i}]") == -1)
{
listClone.Add(i);
}
}
return listClone
.OrderBy(i => i.AddressFamily == AddressFamily.InterNetwork ? 0 : 1)
.ThenBy(i => listClone.IndexOf(i))
.Where(FilterIpAddress)
// .ThenBy(i => listClone.IndexOf(i))
.GroupBy(i => i.ToString())
.Select(x => x.First())
.ToList();
}
private static bool FilterIpAddress(IPAddress address)
{
if (address.IsIPv6LinkLocal
|| address.ToString().StartsWith("169.", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return true;
}
/// <inheritdoc/>
public bool IsInPrivateAddressSpace(string endpoint)
{
return IsInPrivateAddressSpace(endpoint, true);
}
// Checks if the address in endpoint is an RFC1918, RFC1122, or RFC3927 address
private bool IsInPrivateAddressSpace(string endpoint, bool checkSubnets)
{
if (string.Equals(endpoint, "::1", StringComparison.OrdinalIgnoreCase))
@@ -116,12 +132,12 @@ namespace Emby.Server.Implementations.Networking
return true;
}
// ipv6
// IPV6
if (endpoint.Split('.').Length > 4)
{
// Handle ipv4 mapped to ipv6
var originalEndpoint = endpoint;
endpoint = endpoint.Replace("::ffff:", string.Empty);
endpoint = endpoint.Replace("::ffff:", string.Empty, StringComparison.OrdinalIgnoreCase);
if (string.Equals(endpoint, originalEndpoint, StringComparison.OrdinalIgnoreCase))
{
@@ -130,23 +146,26 @@ namespace Emby.Server.Implementations.Networking
}
// Private address space:
// http://en.wikipedia.org/wiki/Private_network
if (endpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase))
{
return Is172AddressPrivate(endpoint);
}
if (endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
if (string.Equals(endpoint, "localhost", StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (checkSubnets && endpoint.StartsWith("192.168", StringComparison.OrdinalIgnoreCase))
if (!IPAddress.TryParse(endpoint, out var ipAddress))
{
return true;
return false;
}
byte[] octet = ipAddress.GetAddressBytes();
if ((octet[0] == 10) ||
(octet[0] == 172 && (octet[1] >= 16 && octet[1] <= 31)) || // RFC1918
(octet[0] == 192 && octet[1] == 168) || // RFC1918
(octet[0] == 127) || // RFC1122
(octet[0] == 169 && octet[1] == 254)) // RFC3927
{
return false;
}
if (checkSubnets && IsInPrivateAddressSpaceAndLocalSubnet(endpoint))
@@ -157,6 +176,7 @@ namespace Emby.Server.Implementations.Networking
return false;
}
/// <inheritdoc/>
public bool IsInPrivateAddressSpaceAndLocalSubnet(string endpoint)
{
if (endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase))
@@ -167,7 +187,7 @@ namespace Emby.Server.Implementations.Networking
foreach (var subnet_Match in subnets)
{
//logger.LogDebug("subnet_Match:" + subnet_Match);
// logger.LogDebug("subnet_Match:" + subnet_Match);
if (endpoint.StartsWith(subnet_Match + ".", StringComparison.OrdinalIgnoreCase))
{
@@ -179,6 +199,7 @@ namespace Emby.Server.Implementations.Networking
return false;
}
// Gives a list of possible subnets from the system whose interface ip starts with endpointFirstPart
private List<string> GetSubnets(string endpointFirstPart)
{
lock (_subnetLookupLock)
@@ -224,46 +245,81 @@ namespace Emby.Server.Implementations.Networking
}
}
private static bool Is172AddressPrivate(string endpoint)
{
for (var i = 16; i <= 31; i++)
{
if (endpoint.StartsWith("172." + i.ToString(CultureInfo.InvariantCulture) + ".", StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
/// <inheritdoc/>
public bool IsInLocalNetwork(string endpoint)
{
return IsInLocalNetworkInternal(endpoint, true);
}
/// <inheritdoc/>
public bool IsAddressInSubnets(string addressString, string[] subnets)
{
return IsAddressInSubnets(IPAddress.Parse(addressString), addressString, subnets);
}
/// <inheritdoc/>
public bool IsAddressInSubnets(IPAddress address, bool excludeInterfaces, bool excludeRFC)
{
byte[] octet = address.GetAddressBytes();
if ((octet[0] == 127) || // RFC1122
(octet[0] == 169 && octet[1] == 254)) // RFC3927
{
// don't use on loopback or 169 interfaces
return false;
}
string addressString = address.ToString();
string excludeAddress = "[" + addressString + "]";
var subnets = LocalSubnetsFn();
// Include any address if LAN subnets aren't specified
if (subnets.Length == 0)
{
return true;
}
// Exclude any addresses if they appear in the LAN list in [ ]
if (Array.IndexOf(subnets, excludeAddress) != -1)
{
return false;
}
return IsAddressInSubnets(address, addressString, subnets);
}
/// <summary>
/// Checks if the give address falls within the ranges given in [subnets]. The addresses in subnets can be hosts or subnets in the CIDR format.
/// </summary>
/// <param name="address">IPAddress version of the address.</param>
/// <param name="addressString">The address to check.</param>
/// <param name="subnets">If true, check against addresses in the LAN settings which have [] arroud and return true if it matches the address give in address.</param>
/// <returns><c>false</c>if the address isn't in the subnets, <c>true</c> otherwise.</returns>
private static bool IsAddressInSubnets(IPAddress address, string addressString, string[] subnets)
{
foreach (var subnet in subnets)
{
var normalizedSubnet = subnet.Trim();
// Is the subnet a host address and does it match the address being passes?
if (string.Equals(normalizedSubnet, addressString, StringComparison.OrdinalIgnoreCase))
{
return true;
}
// Parse CIDR subnets and see if address falls within it.
if (normalizedSubnet.Contains('/', StringComparison.Ordinal))
{
var ipNetwork = IPNetwork.Parse(normalizedSubnet);
if (ipNetwork.Contains(address))
try
{
return true;
var ipNetwork = IPNetwork.Parse(normalizedSubnet);
if (ipNetwork.Contains(address))
{
return true;
}
}
catch
{
// Ignoring - invalid subnet passed encountered.
}
}
}
@@ -288,7 +344,7 @@ namespace Emby.Server.Implementations.Networking
var localSubnets = localSubnetsFn();
foreach (var subnet in localSubnets)
{
// only validate if there's at least one valid entry
// Only validate if there's at least one valid entry.
if (!string.IsNullOrWhiteSpace(subnet))
{
return IsAddressInSubnets(address, addressString, localSubnets) || IsInPrivateAddressSpace(addressString, false);
@@ -345,7 +401,7 @@ namespace Emby.Server.Implementations.Networking
}
catch (InvalidOperationException)
{
// Can happen with reverse proxy or IIS url rewriting
// Can happen with reverse proxy or IIS url rewriting?
}
catch (Exception ex)
{
@@ -362,7 +418,7 @@ namespace Emby.Server.Implementations.Networking
return Dns.GetHostAddressesAsync(hostName);
}
private IEnumerable<IPAddress> GetIPsDefault(bool ignoreVirtualInterface)
private IEnumerable<IPAddress> GetIPsDefault()
{
IEnumerable<NetworkInterface> interfaces;
@@ -382,15 +438,7 @@ namespace Emby.Server.Implementations.Networking
{
var ipProperties = network.GetIPProperties();
// Try to exclude virtual adapters
// http://stackoverflow.com/questions/8089685/c-sharp-finding-my-machines-local-ip-address-and-not-the-vms
var addr = ipProperties.GatewayAddresses.FirstOrDefault();
if (addr == null
|| (ignoreVirtualInterface
&& (addr.Address.Equals(IPAddress.Any) || addr.Address.Equals(IPAddress.IPv6Any))))
{
return Enumerable.Empty<IPAddress>();
}
// Exclude any addresses if they appear in the LAN list in [ ]
return ipProperties.UnicastAddresses
.Select(i => i.Address)
@@ -411,7 +459,7 @@ namespace Emby.Server.Implementations.Networking
}
/// <summary>
/// Gets a random port number that is currently available
/// Gets a random port number that is currently available.
/// </summary>
/// <returns>System.Int32.</returns>
public int GetRandomUnusedTcpPort()
@@ -423,33 +471,29 @@ namespace Emby.Server.Implementations.Networking
return port;
}
/// <inheritdoc/>
public int GetRandomUnusedUdpPort()
{
var localEndPoint = new IPEndPoint(IPAddress.Any, 0);
using (var udpClient = new UdpClient(localEndPoint))
{
var port = ((IPEndPoint)udpClient.Client.LocalEndPoint).Port;
return port;
return ((IPEndPoint)udpClient.Client.LocalEndPoint).Port;
}
}
private List<PhysicalAddress> _macAddresses;
/// <inheritdoc/>
public List<PhysicalAddress> GetMacAddresses()
{
if (_macAddresses == null)
{
_macAddresses = GetMacAddressesInternal().ToList();
}
return _macAddresses;
return _macAddresses ??= GetMacAddressesInternal().ToList();
}
private static IEnumerable<PhysicalAddress> GetMacAddressesInternal()
=> NetworkInterface.GetAllNetworkInterfaces()
.Where(i => i.NetworkInterfaceType != NetworkInterfaceType.Loopback)
.Select(x => x.GetPhysicalAddress())
.Where(x => x != null && x != PhysicalAddress.None);
.Where(x => !x.Equals(PhysicalAddress.None));
/// <inheritdoc/>
public bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask)
{
IPAddress network1 = GetNetworkAddress(address1, subnetMask);
@@ -476,6 +520,7 @@ namespace Emby.Server.Implementations.Networking
return new IPAddress(broadcastAddress);
}
/// <inheritdoc/>
public IPAddress GetLocalIpSubnetMask(IPAddress address)
{
NetworkInterface[] interfaces;
@@ -496,14 +541,11 @@ namespace Emby.Server.Implementations.Networking
foreach (NetworkInterface ni in interfaces)
{
if (ni.GetIPProperties().GatewayAddresses.FirstOrDefault() != null)
foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
{
foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
if (ip.Address.Equals(address) && ip.IPv4Mask != null)
{
if (ip.Address.Equals(address) && ip.IPv4Mask != null)
{
return ip.IPv4Mask;
}
return ip.IPv4Mask;
}
}
}

View File

@@ -401,6 +401,7 @@ namespace Emby.Server.Implementations.Playlists
{
entry.Duration = TimeSpan.FromTicks(child.RunTimeTicks.Value);
}
playlist.PlaylistEntries.Add(entry);
}
@@ -466,7 +467,7 @@ namespace Emby.Server.Implementations.Playlists
playlist.PlaylistEntries.Add(entry);
}
string text = new M3u8Content().ToText(playlist);
string text = new M3uContent().ToText(playlist);
File.WriteAllText(playlistPath, text);
}
@@ -538,13 +539,21 @@ namespace Emby.Server.Implementations.Playlists
private static string UnEscape(string content)
{
if (content == null) return content;
if (content == null)
{
return content;
}
return content.Replace("&amp;", "&").Replace("&apos;", "'").Replace("&quot;", "\"").Replace("&gt;", ">").Replace("&lt;", "<");
}
private static string Escape(string content)
{
if (content == null) return null;
if (content == null)
{
return null;
}
return content.Replace("&", "&amp;").Replace("'", "&apos;").Replace("\"", "&quot;").Replace(">", "&gt;").Replace("<", "&lt;");
}

View File

@@ -18,7 +18,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks
{
/// <summary>
/// Class ScheduledTaskWorker
/// Class ScheduledTaskWorker.
/// </summary>
public class ScheduledTaskWorker : IScheduledTaskWorker
{
@@ -111,11 +111,11 @@ namespace Emby.Server.Implementations.ScheduledTasks
private bool _readFromFile = false;
/// <summary>
/// The _last execution result
/// The _last execution result.
/// </summary>
private TaskResult _lastExecutionResult;
/// <summary>
/// The _last execution result sync lock
/// The _last execution result sync lock.
/// </summary>
private readonly object _lastExecutionResultSyncLock = new object();
/// <summary>
@@ -143,12 +143,14 @@ namespace Emby.Server.Implementations.ScheduledTasks
Logger.LogError(ex, "Error deserializing {File}", path);
}
}
_readFromFile = true;
}
}
return _lastExecutionResult;
}
private set
{
_lastExecutionResult = value;
@@ -182,7 +184,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
public string Category => ScheduledTask.Category;
/// <summary>
/// Gets the current cancellation token
/// Gets the current cancellation token.
/// </summary>
/// <value>The current cancellation token source.</value>
private CancellationTokenSource CurrentCancellationTokenSource { get; set; }
@@ -261,6 +263,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
var triggers = InternalTriggers;
return triggers.Select(i => i.Item1).ToArray();
}
set
{
if (value == null)
@@ -278,7 +281,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
/// The _id
/// The _id.
/// </summary>
private string _id;
@@ -358,7 +361,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
private Task _currentTask;
/// <summary>
/// Executes the task
/// Executes the task.
/// </summary>
/// <param name="options">Task options.</param>
/// <returns>Task.</returns>
@@ -453,7 +456,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
/// Stops the task if it is currently executing
/// Stops the task if it is currently executing.
/// </summary>
/// <exception cref="InvalidOperationException">Cannot cancel a Task unless it is in the Running state.</exception>
public void Cancel()
@@ -640,6 +643,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
Logger.LogError(ex, "Error calling CancellationToken.Cancel();");
}
}
var task = _currentTask;
if (task != null)
{
@@ -675,6 +679,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
Logger.LogError(ex, "Error calling CancellationToken.Dispose();");
}
}
if (wassRunning)
{
OnTaskCompleted(startTime, DateTime.UtcNow, TaskCompletionStatus.Aborted, null);
@@ -683,7 +688,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
/// Converts a TaskTriggerInfo into a concrete BaseTaskTrigger
/// Converts a TaskTriggerInfo into a concrete BaseTaskTrigger.
/// </summary>
/// <param name="info">The info.</param>
/// <returns>BaseTaskTrigger.</returns>
@@ -753,7 +758,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
/// Disposes each trigger
/// Disposes each trigger.
/// </summary>
private void DisposeTriggers()
{

View File

@@ -15,7 +15,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks
{
/// <summary>
/// Class TaskManager
/// Class TaskManager.
/// </summary>
public class TaskManager : ITaskManager
{
@@ -23,13 +23,13 @@ namespace Emby.Server.Implementations.ScheduledTasks
public event EventHandler<TaskCompletionEventArgs> TaskCompleted;
/// <summary>
/// Gets the list of Scheduled Tasks
/// Gets the list of Scheduled Tasks.
/// </summary>
/// <value>The scheduled tasks.</value>
public IScheduledTaskWorker[] ScheduledTasks { get; private set; }
/// <summary>
/// The _task queue
/// The _task queue.
/// </summary>
private readonly ConcurrentQueue<Tuple<Type, TaskOptions>> _taskQueue =
new ConcurrentQueue<Tuple<Type, TaskOptions>>();
@@ -81,7 +81,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
/// Cancels if running
/// Cancels if running.
/// </summary>
/// <typeparam name="T"></typeparam>
public void CancelIfRunning<T>()
@@ -95,7 +95,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// Queues the scheduled task.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="options">Task options</param>
/// <param name="options">Task options.</param>
public void QueueScheduledTask<T>(TaskOptions options)
where T : IScheduledTask
{

View File

@@ -163,7 +163,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
catch (ObjectDisposedException)
{
//TODO Investigate and properly fix.
// TODO Investigate and properly fix.
break;
}
}

View File

@@ -13,7 +13,7 @@ using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
/// <summary>
/// Deletes old cache files
/// Deletes old cache files.
/// </summary>
public class DeleteCacheFileTask : IScheduledTask, IConfigurableScheduledTask
{
@@ -44,7 +44,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
/// <summary>
/// Creates the triggers that define when the task will run
/// Creates the triggers that define when the task will run.
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
@@ -57,7 +57,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
/// <summary>
/// Returns the task to be executed
/// Returns the task to be executed.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
@@ -93,7 +93,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <summary>
/// Deletes the cache files from directory with a last write time less than a given date
/// Deletes the cache files from directory with a last write time less than a given date.
/// </summary>
/// <param name="cancellationToken">The task cancellation token.</param>
/// <param name="directory">The directory.</param>

View File

@@ -13,7 +13,7 @@ using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
/// <summary>
/// Deletes all transcoding temp files
/// Deletes all transcoding temp files.
/// </summary>
public class DeleteTranscodeFileTask : IScheduledTask, IConfigurableScheduledTask
{

View File

@@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
private Timer Timer { get; set; }
/// <summary>
/// Stars waiting for the trigger action
/// Stars waiting for the trigger action.
/// </summary>
/// <param name="lastResult">The last result.</param>
/// <param name="logger">The logger.</param>
@@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
/// Stops waiting for the trigger action
/// Stops waiting for the trigger action.
/// </summary>
public void Stop()
{

View File

@@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks
{
/// <summary>
/// Represents a task trigger that runs repeatedly on an interval
/// Represents a task trigger that runs repeatedly on an interval.
/// </summary>
public class IntervalTrigger : ITaskTrigger
{
@@ -31,7 +31,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
private DateTime _lastStartDate;
/// <summary>
/// Stars waiting for the trigger action
/// Stars waiting for the trigger action.
/// </summary>
/// <param name="lastResult">The last result.</param>
/// <param name="logger">The logger.</param>
@@ -70,7 +70,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
/// Stops waiting for the trigger action
/// Stops waiting for the trigger action.
/// </summary>
public void Stop()
{

View File

@@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
/// Stars waiting for the trigger action
/// Stars waiting for the trigger action.
/// </summary>
/// <param name="lastResult">The last result.</param>
/// <param name="logger">The logger.</param>
@@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
/// Stops waiting for the trigger action
/// Stops waiting for the trigger action.
/// </summary>
public void Stop()
{

View File

@@ -6,12 +6,12 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks
{
/// <summary>
/// Represents a task trigger that fires on a weekly basis
/// Represents a task trigger that fires on a weekly basis.
/// </summary>
public class WeeklyTrigger : ITaskTrigger
{
/// <summary>
/// Get the time of day to trigger the task to run
/// Get the time of day to trigger the task to run.
/// </summary>
/// <value>The time of day.</value>
public TimeSpan TimeOfDay { get; set; }
@@ -34,7 +34,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
private Timer Timer { get; set; }
/// <summary>
/// Stars waiting for the trigger action
/// Stars waiting for the trigger action.
/// </summary>
/// <param name="lastResult">The last result.</param>
/// <param name="logger">The logger.</param>
@@ -77,7 +77,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
/// Stops waiting for the trigger action
/// Stops waiting for the trigger action.
/// </summary>
public void Stop()
{

View File

@@ -61,7 +61,6 @@ namespace Emby.Server.Implementations.Security
AddColumn(db, "AccessTokens", "UserName", "TEXT", existingColumnNames);
AddColumn(db, "AccessTokens", "DateLastActivity", "DATETIME", existingColumnNames);
AddColumn(db, "AccessTokens", "AppVersion", "TEXT", existingColumnNames);
}, TransactionMode);
connection.RunQueries(new[]
@@ -99,7 +98,7 @@ namespace Emby.Server.Implementations.Security
statement.TryBind("@AppName", info.AppName);
statement.TryBind("@AppVersion", info.AppVersion);
statement.TryBind("@DeviceName", info.DeviceName);
statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N", CultureInfo.InvariantCulture)));
statement.TryBind("@UserId", info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N", CultureInfo.InvariantCulture));
statement.TryBind("@UserName", info.UserName);
statement.TryBind("@IsActive", true);
statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
@@ -107,7 +106,6 @@ namespace Emby.Server.Implementations.Security
statement.MoveNext();
}
}, TransactionMode);
}
}
@@ -133,7 +131,7 @@ namespace Emby.Server.Implementations.Security
statement.TryBind("@AppName", info.AppName);
statement.TryBind("@AppVersion", info.AppVersion);
statement.TryBind("@DeviceName", info.DeviceName);
statement.TryBind("@UserId", (info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N", CultureInfo.InvariantCulture)));
statement.TryBind("@UserId", info.UserId.Equals(Guid.Empty) ? null : info.UserId.ToString("N", CultureInfo.InvariantCulture));
statement.TryBind("@UserName", info.UserName);
statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
statement.TryBind("@DateLastActivity", info.DateLastActivity.ToDateTimeParamValue());
@@ -367,7 +365,6 @@ namespace Emby.Server.Implementations.Security
return result;
}
}, ReadTransactionMode);
}
}
@@ -398,7 +395,6 @@ namespace Emby.Server.Implementations.Security
statement.MoveNext();
}
}, TransactionMode);
}
}

View File

@@ -40,7 +40,9 @@ namespace Emby.Server.Implementations.Services
if (httpResult != null)
{
if (httpResult.RequestContext == null)
{
httpResult.RequestContext = request;
}
response.StatusCode = httpResult.Status;
}
@@ -59,8 +61,8 @@ namespace Emby.Server.Implementations.Services
}
}
//ContentType='text/html' is the default for a HttpResponse
//Do not override if another has been set
// ContentType='text/html' is the default for a HttpResponse
// Do not override if another has been set
if (response.ContentType == null || response.ContentType == "text/html")
{
response.ContentType = defaultContentType;

View File

@@ -59,8 +59,8 @@ namespace Emby.Server.Implementations.Services
ServiceExecGeneral.CreateServiceRunnersFor(requestType, actions);
//var returnMarker = GetTypeWithGenericTypeDefinitionOf(requestType, typeof(IReturn<>));
//var responseType = returnMarker != null ?
// var returnMarker = GetTypeWithGenericTypeDefinitionOf(requestType, typeof(IReturn<>));
// var responseType = returnMarker != null ?
// GetGenericArguments(returnMarker)[0]
// : mi.ReturnType != typeof(object) && mi.ReturnType != typeof(void) ?
// mi.ReturnType
@@ -144,7 +144,10 @@ namespace Emby.Server.Implementations.Services
var yieldedWildcardMatches = RestPath.GetFirstMatchWildCardHashKeys(matchUsingPathParts);
foreach (var potentialHashMatch in yieldedWildcardMatches)
{
if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches)) continue;
if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches))
{
continue;
}
var bestScore = -1;
RestPath bestMatch = null;
@@ -182,7 +185,7 @@ namespace Emby.Server.Implementations.Services
serviceRequiresContext.Request = req;
}
//Executes the service and returns the result
// Executes the service and returns the result
return ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName());
}
}

View File

@@ -42,11 +42,15 @@ namespace Emby.Server.Implementations.Services
}
if (mi.GetParameters().Length != 1)
{
continue;
}
var actionName = mi.Name;
if (!AllVerbs.Contains(actionName, StringComparer.OrdinalIgnoreCase))
{
continue;
}
list.Add(mi);
}
@@ -63,7 +67,10 @@ namespace Emby.Server.Implementations.Services
{
foreach (var actionCtx in actions)
{
if (execMap.ContainsKey(actionCtx.Id)) continue;
if (execMap.ContainsKey(actionCtx.Id))
{
continue;
}
execMap[actionCtx.Id] = actionCtx;
}

View File

@@ -180,7 +180,7 @@ namespace Emby.Server.Implementations.Services
=> string.Equals(method, expected, StringComparison.OrdinalIgnoreCase);
/// <summary>
/// Duplicate params have their values joined together in a comma-delimited string
/// Duplicate params have their values joined together in a comma-delimited string.
/// </summary>
private static Dictionary<string, string> GetFlattenedRequestParams(HttpRequest request)
{

View File

@@ -9,6 +9,7 @@ namespace Emby.Server.Implementations.Services
public string Id { get; set; }
public ActionInvokerFn ServiceAction { get; set; }
public MediaBrowser.Model.Services.IHasRequestFilter[] RequestFilters { get; set; }
public static string Key(Type serviceType, string method, string requestDtoName)

View File

@@ -62,7 +62,9 @@ namespace Emby.Server.Implementations.Services
public string Path => this.restPath;
public string Summary { get; private set; }
public string Description { get; private set; }
public bool IsHidden { get; private set; }
public static string[] GetPathPartsForMatching(string pathInfo)
@@ -118,11 +120,14 @@ namespace Emby.Server.Implementations.Services
var componentsList = new List<string>();
//We only split on '.' if the restPath has them. Allows for /{action}.{type}
// We only split on '.' if the restPath has them. Allows for /{action}.{type}
var hasSeparators = new List<bool>();
foreach (var component in this.restPath.Split(PathSeperatorChar))
{
if (string.IsNullOrEmpty(component)) continue;
if (string.IsNullOrEmpty(component))
{
continue;
}
if (component.IndexOf(VariablePrefix, StringComparison.OrdinalIgnoreCase) != -1
&& component.IndexOf(ComponentSeperator) != -1)
@@ -159,6 +164,7 @@ namespace Emby.Server.Implementations.Services
this.isWildcard[i] = true;
variableName = variableName.Substring(0, variableName.Length - 1);
}
this.variablesNames[i] = variableName;
this.VariableArgsCount++;
}
@@ -298,12 +304,12 @@ namespace Emby.Server.Implementations.Services
return -1;
}
//Routes with least wildcard matches get the highest score
var score = Math.Max((100 - wildcardMatchCount), 1) * 1000
//Routes with less variable (and more literal) matches
+ Math.Max((10 - VariableArgsCount), 1) * 100;
// Routes with least wildcard matches get the highest score
var score = Math.Max(100 - wildcardMatchCount, 1) * 1000
// Routes with less variable (and more literal) matches
+ Math.Max(10 - VariableArgsCount, 1) * 100;
//Exact verb match is better than ANY
// Exact verb match is better than ANY
if (Verbs.Length == 1 && string.Equals(httpMethod, Verbs[0], StringComparison.OrdinalIgnoreCase))
{
score += 10;
@@ -439,12 +445,14 @@ namespace Emby.Server.Implementations.Services
&& requestComponents.Length >= this.TotalComponentsCount - this.wildcardCount;
if (!isValidWildCardPath)
{
throw new ArgumentException(
string.Format(
CultureInfo.InvariantCulture,
"Path Mismatch: Request Path '{0}' has invalid number of components compared to: '{1}'",
pathInfo,
this.restPath));
}
}
var requestKeyValuesMap = new Dictionary<string, string>();
@@ -470,7 +478,7 @@ namespace Emby.Server.Implementations.Services
+ variableName + " on " + RequestType.GetMethodName());
}
var value = requestComponents.Length > pathIx ? requestComponents[pathIx] : null; //wildcard has arg mismatch
var value = requestComponents.Length > pathIx ? requestComponents[pathIx] : null; // wildcard has arg mismatch
if (value != null && this.isWildcard[i])
{
if (i == this.TotalComponentsCount - 1)
@@ -519,8 +527,8 @@ namespace Emby.Server.Implementations.Services
if (queryStringAndFormData != null)
{
//Query String and form data can override variable path matches
//path variables < query string < form data
// Query String and form data can override variable path matches
// path variables < query string < form data
foreach (var name in queryStringAndFormData)
{
requestKeyValuesMap[name.Key] = name.Value;

View File

@@ -22,7 +22,9 @@ namespace Emby.Server.Implementations.Services
}
public Action<object, object> PropertySetFn { get; private set; }
public Func<string, object> PropertyParseStringFn { get; private set; }
public Type PropertyType { get; private set; }
}
@@ -83,7 +85,7 @@ namespace Emby.Server.Implementations.Services
if (propertySerializerEntry.PropertyType == typeof(bool))
{
//InputExtensions.cs#530 MVC Checkbox helper emits extra hidden input field, generating 2 values, first is the real value
// InputExtensions.cs#530 MVC Checkbox helper emits extra hidden input field, generating 2 values, first is the real value
propertyTextValue = StringExtensions.LeftPart(propertyTextValue, ',').ToString();
}

View File

@@ -18,13 +18,21 @@ namespace Emby.Server.Implementations.Services
public class SwaggerSpec
{
public string swagger { get; set; }
public string[] schemes { get; set; }
public SwaggerInfo info { get; set; }
public string host { get; set; }
public string basePath { get; set; }
public SwaggerTag[] tags { get; set; }
public IDictionary<string, Dictionary<string, SwaggerMethod>> paths { get; set; }
public Dictionary<string, SwaggerDefinition> definitions { get; set; }
public SwaggerComponents components { get; set; }
}
@@ -36,15 +44,20 @@ namespace Emby.Server.Implementations.Services
public class SwaggerSecurityScheme
{
public string name { get; set; }
public string type { get; set; }
public string @in { get; set; }
}
public class SwaggerInfo
{
public string description { get; set; }
public string version { get; set; }
public string title { get; set; }
public string termsOfService { get; set; }
public SwaggerConcactInfo contact { get; set; }
@@ -53,36 +66,52 @@ namespace Emby.Server.Implementations.Services
public class SwaggerConcactInfo
{
public string email { get; set; }
public string name { get; set; }
public string url { get; set; }
}
public class SwaggerTag
{
public string description { get; set; }
public string name { get; set; }
}
public class SwaggerMethod
{
public string summary { get; set; }
public string description { get; set; }
public string[] tags { get; set; }
public string operationId { get; set; }
public string[] consumes { get; set; }
public string[] produces { get; set; }
public SwaggerParam[] parameters { get; set; }
public Dictionary<string, SwaggerResponse> responses { get; set; }
public Dictionary<string, string[]>[] security { get; set; }
}
public class SwaggerParam
{
public string @in { get; set; }
public string name { get; set; }
public string description { get; set; }
public bool required { get; set; }
public string type { get; set; }
public string collectionFormat { get; set; }
}
@@ -97,15 +126,20 @@ namespace Emby.Server.Implementations.Services
public class SwaggerDefinition
{
public string type { get; set; }
public Dictionary<string, SwaggerProperty> properties { get; set; }
}
public class SwaggerProperty
{
public string type { get; set; }
public string format { get; set; }
public string description { get; set; }
public string[] @enum { get; set; }
public string @default { get; set; }
}

View File

@@ -9,7 +9,7 @@ namespace Emby.Server.Implementations.Services
/// Donated by Ivan Korneliuk from his post:
/// http://korneliuk.blogspot.com/2012/08/servicestack-reusing-dtos.html
///
/// Modified to only allow using routes matching the supplied HTTP Verb
/// Modified to only allow using routes matching the supplied HTTP Verb.
/// </summary>
public static class UrlExtensions
{

View File

@@ -296,7 +296,7 @@ namespace Emby.Server.Implementations.Session
}
catch (DbUpdateConcurrencyException e)
{
_logger.LogWarning(e, "Error updating user's last activity date.");
_logger.LogDebug(e, "Error updating user's last activity date.");
}
}
}
@@ -502,7 +502,8 @@ namespace Emby.Server.Implementations.Session
Client = appName,
DeviceId = deviceId,
ApplicationVersion = appVersion,
Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture)
Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture),
ServerId = _appHost.SystemId
};
var username = user?.Username;
@@ -843,7 +844,7 @@ namespace Emby.Server.Implementations.Session
}
/// <summary>
/// Used to report that playback has ended for an item
/// Used to report that playback has ended for an item.
/// </summary>
/// <param name="info">The info.</param>
/// <returns>Task.</returns>

Some files were not shown because too many files have changed in this diff Show More