aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS.md1
-rw-r--r--Emby.Server.Implementations/Activity/ActivityRepository.cs134
-rw-r--r--Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs61
-rw-r--r--Emby.Server.Implementations/Data/SqliteExtensions.cs7
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs467
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserRepository.cs15
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj2
-rw-r--r--Emby.Server.Implementations/Localization/Core/el.json10
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/nb.json4
-rw-r--r--Emby.Server.Implementations/Security/AuthenticationRepository.cs85
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj2
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs12
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs2
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs30
-rw-r--r--MediaBrowser.Api/Playback/Hls/VideoHlsService.cs10
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs3
-rw-r--r--MediaBrowser.Api/Playback/Progressive/VideoService.cs2
-rw-r--r--MediaBrowser.Api/UserService.cs37
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs139
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs6
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs4
-rw-r--r--deployment/fedora-package-x64/pkg-src/jellyfin.spec4
24 files changed, 558 insertions, 487 deletions
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index c96228f31..c95133dfd 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -28,6 +28,7 @@
- [DrPandemic](https://github.com/drpandemic)
- [joern-h](https://github.com/joern-h)
- [Khinenw](https://github.com/HelloWorld017)
+ - [fhriley](https://github.com/fhriley)
# Emby Contributors
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
index 91371b833..541b23afd 100644
--- a/Emby.Server.Implementations/Activity/ActivityRepository.cs
+++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs
@@ -155,94 +155,100 @@ namespace Emby.Server.Implementations.Activity
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
{
- using (var connection = GetConnection(true))
- {
- var commandText = BaseActivitySelectText;
- var whereClauses = new List<string>();
+ var commandText = BaseActivitySelectText;
+ var whereClauses = new List<string>();
- if (minDate.HasValue)
+ if (minDate.HasValue)
+ {
+ whereClauses.Add("DateCreated>=@DateCreated");
+ }
+ if (hasUserId.HasValue)
+ {
+ if (hasUserId.Value)
{
- whereClauses.Add("DateCreated>=@DateCreated");
+ whereClauses.Add("UserId not null");
}
- if (hasUserId.HasValue)
+ else
{
- if (hasUserId.Value)
- {
- whereClauses.Add("UserId not null");
- }
- else
- {
- whereClauses.Add("UserId is null");
- }
+ whereClauses.Add("UserId is null");
}
+ }
- var whereTextWithoutPaging = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- if (startIndex.HasValue && startIndex.Value > 0)
- {
- var pagingWhereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLog {0} ORDER BY DateCreated DESC LIMIT {1})",
- pagingWhereText,
- startIndex.Value.ToString(_usCulture)));
- }
+ var whereTextWithoutPaging = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
- var whereText = whereClauses.Count == 0 ?
+ if (startIndex.HasValue && startIndex.Value > 0)
+ {
+ var pagingWhereText = whereClauses.Count == 0 ?
string.Empty :
" where " + string.Join(" AND ", whereClauses.ToArray());
- commandText += whereText;
+ whereClauses.Add(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "Id NOT IN (SELECT Id FROM ActivityLog {0} ORDER BY DateCreated DESC LIMIT {1})",
+ pagingWhereText,
+ startIndex.Value));
+ }
- commandText += " ORDER BY DateCreated DESC";
+ var whereText = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
- if (limit.HasValue)
- {
- commandText += " LIMIT " + limit.Value.ToString(_usCulture);
- }
+ commandText += whereText;
- var statementTexts = new List<string>();
- statementTexts.Add(commandText);
- statementTexts.Add("select count (Id) from ActivityLog" + whereTextWithoutPaging);
+ commandText += " ORDER BY DateCreated DESC";
- return connection.RunInTransaction(db =>
- {
- var list = new List<ActivityLogEntry>();
- var result = new QueryResult<ActivityLogEntry>();
+ if (limit.HasValue)
+ {
+ commandText += " LIMIT " + limit.Value.ToString(_usCulture);
+ }
+
+ var statementTexts = new[]
+ {
+ commandText,
+ "select count (Id) from ActivityLog" + whereTextWithoutPaging
+ };
- var statements = PrepareAll(db, statementTexts).ToList();
+ var list = new List<ActivityLogEntry>();
+ var result = new QueryResult<ActivityLogEntry>();
- using (var statement = statements[0])
+ using (var connection = GetConnection(true))
+ {
+ connection.RunInTransaction(
+ db =>
{
- if (minDate.HasValue)
- {
- statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
- }
+ var statements = PrepareAll(db, statementTexts).ToList();
- foreach (var row in statement.ExecuteQuery())
+ using (var statement = statements[0])
{
- list.Add(GetEntry(row));
+ if (minDate.HasValue)
+ {
+ statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
+ }
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(GetEntry(row));
+ }
}
- }
- using (var statement = statements[1])
- {
- if (minDate.HasValue)
+ using (var statement = statements[1])
{
- statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
- }
+ if (minDate.HasValue)
+ {
+ statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
+ }
- result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
- }
-
- result.Items = list.ToArray();
- return result;
-
- }, ReadTransactionMode);
+ result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
+ }
+ },
+ ReadTransactionMode);
}
+
+ result.Items = list.ToArray();
+ return result;
}
private static ActivityLogEntry GetEntry(IReadOnlyList<IResultSetValue> reader)
diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
index 77f5d9479..1a67ab912 100644
--- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
@@ -62,14 +62,14 @@ namespace Emby.Server.Implementations.Data
/// <returns>Task.</returns>
private void InitializeInternal()
{
- using (var connection = GetConnection())
+ string[] queries =
{
- string[] queries = {
-
- "create table if not exists userdisplaypreferences (id GUID NOT NULL, userId GUID NOT NULL, client text NOT NULL, data BLOB NOT NULL)",
- "create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)"
- };
+ "create table if not exists userdisplaypreferences (id GUID NOT NULL, userId GUID NOT NULL, client text NOT NULL, data BLOB NOT NULL)",
+ "create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)"
+ };
+ using (var connection = GetConnection())
+ {
connection.RunQueries(queries);
}
}
@@ -81,7 +81,6 @@ namespace Emby.Server.Implementations.Data
/// <param name="userId">The user id.</param>
/// <param name="client">The client.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
/// <exception cref="ArgumentNullException">item</exception>
public void SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, CancellationToken cancellationToken)
{
@@ -99,10 +98,9 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
- {
- SaveDisplayPreferences(displayPreferences, userId, client, db);
- }, TransactionMode);
+ connection.RunInTransaction(
+ db => SaveDisplayPreferences(displayPreferences, userId, client, db),
+ TransactionMode);
}
}
@@ -127,7 +125,6 @@ namespace Emby.Server.Implementations.Data
/// <param name="displayPreferences">The display preferences.</param>
/// <param name="userId">The user id.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
/// <exception cref="ArgumentNullException">item</exception>
public void SaveAllDisplayPreferences(IEnumerable<DisplayPreferences> displayPreferences, Guid userId, CancellationToken cancellationToken)
{
@@ -140,13 +137,15 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
- {
- foreach (var displayPreference in displayPreferences)
+ connection.RunInTransaction(
+ db =>
{
- SaveDisplayPreferences(displayPreference, userId, displayPreference.Client, db);
- }
- }, TransactionMode);
+ foreach (var displayPreference in displayPreferences)
+ {
+ SaveDisplayPreferences(displayPreference, userId, displayPreference.Client, db);
+ }
+ },
+ TransactionMode);
}
}
@@ -180,12 +179,12 @@ namespace Emby.Server.Implementations.Data
return Get(row);
}
}
-
- return new DisplayPreferences
- {
- Id = guidId.ToString("N", CultureInfo.InvariantCulture)
- };
}
+
+ return new DisplayPreferences
+ {
+ Id = guidId.ToString("N", CultureInfo.InvariantCulture)
+ };
}
/// <summary>
@@ -215,22 +214,12 @@ namespace Emby.Server.Implementations.Data
}
private DisplayPreferences Get(IReadOnlyList<IResultSetValue> row)
- {
- using (var stream = new MemoryStream(row[0].ToBlob()))
- {
- stream.Position = 0;
- return _jsonSerializer.DeserializeFromStream<DisplayPreferences>(stream);
- }
- }
+ => _jsonSerializer.DeserializeFromString<DisplayPreferences>(row.GetString(0));
public void SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken)
- {
- SaveDisplayPreferences(displayPreferences, new Guid(userId), client, cancellationToken);
- }
+ => SaveDisplayPreferences(displayPreferences, new Guid(userId), client, cancellationToken);
public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, string userId, string client)
- {
- return GetDisplayPreferences(displayPreferencesId, new Guid(userId), client);
- }
+ => GetDisplayPreferences(displayPreferencesId, new Guid(userId), client);
}
}
diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs
index c0f27b25a..0fb2c10fd 100644
--- a/Emby.Server.Implementations/Data/SqliteExtensions.cs
+++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs
@@ -18,10 +18,6 @@ namespace Emby.Server.Implementations.Data
connection.RunInTransaction(conn =>
{
- //foreach (var query in queries)
- //{
- // conn.Execute(query);
- //}
conn.ExecuteAll(string.Join(";", queries));
});
}
@@ -38,7 +34,8 @@ namespace Emby.Server.Implementations.Data
public static Guid ReadGuidFromBlob(this IResultSetValue result)
{
- return new Guid(result.ToBlob());
+ // TODO: Remove ToArray when upgrading to netstandard2.1
+ return new Guid(result.ToBlob().ToArray());
}
public static string ToDateTimeParamValue(this DateTime dateValue)
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 3ca0728fe..9d983307f 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -99,35 +99,114 @@ namespace Emby.Server.Implementations.Data
/// </summary>
public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager)
{
- using (var connection = GetConnection())
- {
- const string createMediaStreamsTableCommand
+ const string CreateMediaStreamsTableCommand
= "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))";
- string[] queries = {
- "PRAGMA locking_mode=EXCLUSIVE",
+ string[] queries =
+ {
+ "PRAGMA locking_mode=EXCLUSIVE",
- "create table if not exists TypedBaseItems (guid GUID primary key NOT NULL, type TEXT NOT NULL, data BLOB NULL, ParentId GUID NULL, Path TEXT NULL)",
+ "create table if not exists TypedBaseItems (guid GUID primary key NOT NULL, type TEXT NOT NULL, data BLOB NULL, ParentId GUID NULL, Path TEXT NULL)",
- "create table if not exists AncestorIds (ItemId GUID NOT NULL, AncestorId GUID NOT NULL, AncestorIdText TEXT NOT NULL, PRIMARY KEY (ItemId, AncestorId))",
- "create index if not exists idx_AncestorIds1 on AncestorIds(AncestorId)",
- "create index if not exists idx_AncestorIds5 on AncestorIds(AncestorIdText,ItemId)",
+ "create table if not exists AncestorIds (ItemId GUID NOT NULL, AncestorId GUID NOT NULL, AncestorIdText TEXT NOT NULL, PRIMARY KEY (ItemId, AncestorId))",
+ "create index if not exists idx_AncestorIds1 on AncestorIds(AncestorId)",
+ "create index if not exists idx_AncestorIds5 on AncestorIds(AncestorIdText,ItemId)",
- "create table if not exists ItemValues (ItemId GUID NOT NULL, Type INT NOT NULL, Value TEXT NOT NULL, CleanValue TEXT NOT NULL)",
+ "create table if not exists ItemValues (ItemId GUID NOT NULL, Type INT NOT NULL, Value TEXT NOT NULL, CleanValue TEXT NOT NULL)",
- "create table if not exists People (ItemId GUID, Name TEXT NOT NULL, Role TEXT, PersonType TEXT, SortOrder int, ListOrder int)",
+ "create table if not exists People (ItemId GUID, Name TEXT NOT NULL, Role TEXT, PersonType TEXT, SortOrder int, ListOrder int)",
- "drop index if exists idxPeopleItemId",
- "create index if not exists idxPeopleItemId1 on People(ItemId,ListOrder)",
- "create index if not exists idxPeopleName on People(Name)",
+ "drop index if exists idxPeopleItemId",
+ "create index if not exists idxPeopleItemId1 on People(ItemId,ListOrder)",
+ "create index if not exists idxPeopleName on People(Name)",
- "create table if not exists " + ChaptersTableName + " (ItemId GUID, ChapterIndex INT NOT NULL, StartPositionTicks BIGINT NOT NULL, Name TEXT, ImagePath TEXT, PRIMARY KEY (ItemId, ChapterIndex))",
+ "create table if not exists " + ChaptersTableName + " (ItemId GUID, ChapterIndex INT NOT NULL, StartPositionTicks BIGINT NOT NULL, Name TEXT, ImagePath TEXT, PRIMARY KEY (ItemId, ChapterIndex))",
- createMediaStreamsTableCommand,
+ CreateMediaStreamsTableCommand,
- "pragma shrink_memory"
- };
+ "pragma shrink_memory"
+ };
+
+
+ string[] postQueries =
+ {
+ // obsolete
+ "drop index if exists idx_TypedBaseItems",
+ "drop index if exists idx_mediastreams",
+ "drop index if exists idx_mediastreams1",
+ "drop index if exists idx_"+ChaptersTableName,
+ "drop index if exists idx_UserDataKeys1",
+ "drop index if exists idx_UserDataKeys2",
+ "drop index if exists idx_TypeTopParentId3",
+ "drop index if exists idx_TypeTopParentId2",
+ "drop index if exists idx_TypeTopParentId4",
+ "drop index if exists idx_Type",
+ "drop index if exists idx_TypeTopParentId",
+ "drop index if exists idx_GuidType",
+ "drop index if exists idx_TopParentId",
+ "drop index if exists idx_TypeTopParentId6",
+ "drop index if exists idx_ItemValues2",
+ "drop index if exists Idx_ProviderIds",
+ "drop index if exists idx_ItemValues3",
+ "drop index if exists idx_ItemValues4",
+ "drop index if exists idx_ItemValues5",
+ "drop index if exists idx_UserDataKeys3",
+ "drop table if exists UserDataKeys",
+ "drop table if exists ProviderIds",
+ "drop index if exists Idx_ProviderIds1",
+ "drop table if exists Images",
+ "drop index if exists idx_Images",
+ "drop index if exists idx_TypeSeriesPresentationUniqueKey",
+ "drop index if exists idx_SeriesPresentationUniqueKey",
+ "drop index if exists idx_TypeSeriesPresentationUniqueKey2",
+ "drop index if exists idx_AncestorIds3",
+ "drop index if exists idx_AncestorIds4",
+ "drop index if exists idx_AncestorIds2",
+
+ "create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)",
+ "create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
+
+ "create index if not exists idx_PresentationUniqueKey on TypedBaseItems(PresentationUniqueKey)",
+ "create index if not exists idx_GuidTypeIsFolderIsVirtualItem on TypedBaseItems(Guid,Type,IsFolder,IsVirtualItem)",
+ "create index if not exists idx_CleanNameType on TypedBaseItems(CleanName,Type)",
+
+ // covering index
+ "create index if not exists idx_TopParentIdGuid on TypedBaseItems(TopParentId,Guid)",
+
+ // series
+ "create index if not exists idx_TypeSeriesPresentationUniqueKey1 on TypedBaseItems(Type,SeriesPresentationUniqueKey,PresentationUniqueKey,SortName)",
+
+ // series counts
+ // seriesdateplayed sort order
+ "create index if not exists idx_TypeSeriesPresentationUniqueKey3 on TypedBaseItems(SeriesPresentationUniqueKey,Type,IsFolder,IsVirtualItem)",
+
+ // live tv programs
+ "create index if not exists idx_TypeTopParentIdStartDate on TypedBaseItems(Type,TopParentId,StartDate)",
+
+ // covering index for getitemvalues
+ "create index if not exists idx_TypeTopParentIdGuid on TypedBaseItems(Type,TopParentId,Guid)",
+
+ // used by movie suggestions
+ "create index if not exists idx_TypeTopParentIdGroup on TypedBaseItems(Type,TopParentId,PresentationUniqueKey)",
+ "create index if not exists idx_TypeTopParentId5 on TypedBaseItems(TopParentId,IsVirtualItem)",
+
+ // latest items
+ "create index if not exists idx_TypeTopParentId9 on TypedBaseItems(TopParentId,Type,IsVirtualItem,PresentationUniqueKey,DateCreated)",
+ "create index if not exists idx_TypeTopParentId8 on TypedBaseItems(TopParentId,IsFolder,IsVirtualItem,PresentationUniqueKey,DateCreated)",
+
+ // resume
+ "create index if not exists idx_TypeTopParentId7 on TypedBaseItems(TopParentId,MediaType,IsVirtualItem,PresentationUniqueKey)",
+
+ // items by name
+ "create index if not exists idx_ItemValues6 on ItemValues(ItemId,Type,CleanValue)",
+ "create index if not exists idx_ItemValues7 on ItemValues(Type,CleanValue,ItemId)",
+
+ // Used to update inherited tags
+ "create index if not exists idx_ItemValues8 on ItemValues(Type, ItemId, Value)",
+ };
+ using (var connection = GetConnection())
+ {
connection.RunQueries(queries);
connection.RunInTransaction(db =>
@@ -235,83 +314,6 @@ namespace Emby.Server.Implementations.Data
}, TransactionMode);
- string[] postQueries =
- {
- // obsolete
- "drop index if exists idx_TypedBaseItems",
- "drop index if exists idx_mediastreams",
- "drop index if exists idx_mediastreams1",
- "drop index if exists idx_"+ChaptersTableName,
- "drop index if exists idx_UserDataKeys1",
- "drop index if exists idx_UserDataKeys2",
- "drop index if exists idx_TypeTopParentId3",
- "drop index if exists idx_TypeTopParentId2",
- "drop index if exists idx_TypeTopParentId4",
- "drop index if exists idx_Type",
- "drop index if exists idx_TypeTopParentId",
- "drop index if exists idx_GuidType",
- "drop index if exists idx_TopParentId",
- "drop index if exists idx_TypeTopParentId6",
- "drop index if exists idx_ItemValues2",
- "drop index if exists Idx_ProviderIds",
- "drop index if exists idx_ItemValues3",
- "drop index if exists idx_ItemValues4",
- "drop index if exists idx_ItemValues5",
- "drop index if exists idx_UserDataKeys3",
- "drop table if exists UserDataKeys",
- "drop table if exists ProviderIds",
- "drop index if exists Idx_ProviderIds1",
- "drop table if exists Images",
- "drop index if exists idx_Images",
- "drop index if exists idx_TypeSeriesPresentationUniqueKey",
- "drop index if exists idx_SeriesPresentationUniqueKey",
- "drop index if exists idx_TypeSeriesPresentationUniqueKey2",
- "drop index if exists idx_AncestorIds3",
- "drop index if exists idx_AncestorIds4",
- "drop index if exists idx_AncestorIds2",
-
- "create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)",
- "create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
-
- "create index if not exists idx_PresentationUniqueKey on TypedBaseItems(PresentationUniqueKey)",
- "create index if not exists idx_GuidTypeIsFolderIsVirtualItem on TypedBaseItems(Guid,Type,IsFolder,IsVirtualItem)",
- "create index if not exists idx_CleanNameType on TypedBaseItems(CleanName,Type)",
-
- // covering index
- "create index if not exists idx_TopParentIdGuid on TypedBaseItems(TopParentId,Guid)",
-
- // series
- "create index if not exists idx_TypeSeriesPresentationUniqueKey1 on TypedBaseItems(Type,SeriesPresentationUniqueKey,PresentationUniqueKey,SortName)",
-
- // series counts
- // seriesdateplayed sort order
- "create index if not exists idx_TypeSeriesPresentationUniqueKey3 on TypedBaseItems(SeriesPresentationUniqueKey,Type,IsFolder,IsVirtualItem)",
-
- // live tv programs
- "create index if not exists idx_TypeTopParentIdStartDate on TypedBaseItems(Type,TopParentId,StartDate)",
-
- // covering index for getitemvalues
- "create index if not exists idx_TypeTopParentIdGuid on TypedBaseItems(Type,TopParentId,Guid)",
-
- // used by movie suggestions
- "create index if not exists idx_TypeTopParentIdGroup on TypedBaseItems(Type,TopParentId,PresentationUniqueKey)",
- "create index if not exists idx_TypeTopParentId5 on TypedBaseItems(TopParentId,IsVirtualItem)",
-
- // latest items
- "create index if not exists idx_TypeTopParentId9 on TypedBaseItems(TopParentId,Type,IsVirtualItem,PresentationUniqueKey,DateCreated)",
- "create index if not exists idx_TypeTopParentId8 on TypedBaseItems(TopParentId,IsFolder,IsVirtualItem,PresentationUniqueKey,DateCreated)",
-
- // resume
- "create index if not exists idx_TypeTopParentId7 on TypedBaseItems(TopParentId,MediaType,IsVirtualItem,PresentationUniqueKey)",
-
- // items by name
- "create index if not exists idx_ItemValues6 on ItemValues(ItemId,Type,CleanValue)",
- "create index if not exists idx_ItemValues7 on ItemValues(Type,CleanValue,ItemId)",
-
- // Used to update inherited tags
- "create index if not exists idx_ItemValues8 on ItemValues(Type, ItemId, Value)",
- };
-
connection.RunQueries(postQueries);
}
@@ -1994,14 +1996,14 @@ namespace Emby.Server.Implementations.Data
throw new ArgumentNullException(nameof(chapters));
}
+ var idBlob = id.ToGuidBlob();
+
using (var connection = GetConnection())
{
connection.RunInTransaction(db =>
{
- var idBlob = id.ToGuidBlob();
-
- // First delete chapters
- db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob);
+ // First delete chapters
+ db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob);
InsertChapters(idBlob, chapters, db);
@@ -2530,6 +2532,7 @@ namespace Emby.Server.Implementations.Data
commandText += " where " + string.Join(" AND ", whereClauses);
}
+ int count;
using (var connection = GetConnection(true))
{
using (var statement = PrepareStatement(connection, commandText))
@@ -2545,11 +2548,12 @@ namespace Emby.Server.Implementations.Data
// Running this again will bind the params
GetWhereClauses(query, statement);
- var count = statement.ExecuteQuery().SelectScalarInt().First();
- LogQueryTime("GetCount", commandText, now);
- return count;
+ count = statement.ExecuteQuery().SelectScalarInt().First();
}
}
+
+ LogQueryTime("GetCount", commandText, now);
+ return count;
}
public List<BaseItem> GetItemList(InternalItemsQuery query)
@@ -2599,10 +2603,9 @@ namespace Emby.Server.Implementations.Data
}
}
+ var items = new List<BaseItem>();
using (var connection = GetConnection(true))
{
- var items = new List<BaseItem>();
-
using (var statement = PrepareStatement(connection, commandText))
{
if (EnableJoinUserData(query))
@@ -2653,11 +2656,11 @@ namespace Emby.Server.Implementations.Data
items = newList;
}
+ }
- LogQueryTime("GetItemList", commandText, now);
+ LogQueryTime("GetItemList", commandText, now);
- return items;
- }
+ return items;
}
private string FixUnicodeChars(string buffer)
@@ -2750,8 +2753,6 @@ namespace Emby.Server.Implementations.Data
var now = DateTime.UtcNow;
- var list = new List<BaseItem>();
-
// Hack for right now since we currently don't support filtering out these duplicates within a query
if (query.Limit.HasValue && query.EnableGroupByMetadataKey)
{
@@ -2817,11 +2818,13 @@ namespace Emby.Server.Implementations.Data
statementTexts.Add(commandText);
}
+ var list = new List<BaseItem>();
+ var result = new QueryResult<BaseItem>();
using (var connection = GetConnection(true))
{
- return connection.RunInTransaction(db =>
+ connection.RunInTransaction(db =>
{
- var result = new QueryResult<BaseItem>();
+
var statements = PrepareAll(db, statementTexts).ToList();
if (!isReturningZeroItems)
@@ -2876,14 +2879,12 @@ namespace Emby.Server.Implementations.Data
result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
}
}
-
- LogQueryTime("GetItems", commandText, now);
-
- result.Items = list.ToArray();
- return result;
-
}, ReadTransactionMode);
}
+
+ LogQueryTime("GetItems", commandText, now);
+ result.Items = list.ToArray();
+ return result;
}
private string GetOrderByText(InternalItemsQuery query)
@@ -3049,10 +3050,9 @@ namespace Emby.Server.Implementations.Data
}
}
+ var list = new List<Guid>();
using (var connection = GetConnection(true))
{
- var list = new List<Guid>();
-
using (var statement = PrepareStatement(connection, commandText))
{
if (EnableJoinUserData(query))
@@ -3071,11 +3071,10 @@ namespace Emby.Server.Implementations.Data
list.Add(row[0].ReadGuidFromBlob());
}
}
-
- LogQueryTime("GetItemList", commandText, now);
-
- return list;
}
+
+ LogQueryTime("GetItemList", commandText, now);
+ return list;
}
public List<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query)
@@ -3137,6 +3136,7 @@ namespace Emby.Server.Implementations.Data
{
path = row.GetString(1);
}
+
list.Add(new Tuple<Guid, string>(id, path));
}
}
@@ -3198,7 +3198,7 @@ namespace Emby.Server.Implementations.Data
}
}
- var list = new List<Guid>();
+
var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
var statementTexts = new List<string>();
@@ -3228,12 +3228,12 @@ namespace Emby.Server.Implementations.Data
statementTexts.Add(commandText);
}
+ var list = new List<Guid>();
+ var result = new QueryResult<Guid>();
using (var connection = GetConnection(true))
{
- return connection.RunInTransaction(db =>
+ connection.RunInTransaction(db =>
{
- var result = new QueryResult<Guid>();
-
var statements = PrepareAll(db, statementTexts).ToList();
if (!isReturningZeroItems)
@@ -3276,14 +3276,13 @@ namespace Emby.Server.Implementations.Data
result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
}
}
-
- LogQueryTime("GetItemIds", commandText, now);
-
- result.Items = list.ToArray();
- return result;
-
}, ReadTransactionMode);
}
+
+ LogQueryTime("GetItemIds", commandText, now);
+
+ result.Items = list.ToArray();
+ return result;
}
private bool IsAlphaNumeric(string str)
@@ -4859,22 +4858,25 @@ namespace Emby.Server.Implementations.Data
private void UpdateInheritedTags(CancellationToken cancellationToken)
{
- using (var connection = GetConnection())
- {
- connection.RunInTransaction(db =>
+ string sql = string.Join(
+ ";",
+ new string[]
{
- connection.ExecuteAll(string.Join(";", new string[]
- {
- "delete from itemvalues where type = 6",
+ "delete from itemvalues where type = 6",
- "insert into itemvalues (ItemId, Type, Value, CleanValue) select ItemId, 6, Value, CleanValue from ItemValues where Type=4",
+ "insert into itemvalues (ItemId, Type, Value, CleanValue) select ItemId, 6, Value, CleanValue from ItemValues where Type=4",
- @"insert into itemvalues (ItemId, Type, Value, CleanValue) select AncestorIds.itemid, 6, ItemValues.Value, ItemValues.CleanValue
+ @"insert into itemvalues (ItemId, Type, Value, CleanValue) select AncestorIds.itemid, 6, ItemValues.Value, ItemValues.CleanValue
FROM AncestorIds
LEFT JOIN ItemValues ON (AncestorIds.AncestorId = ItemValues.ItemId)
where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type = 4 "
+ });
- }));
+ using (var connection = GetConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ connection.ExecuteAll(sql);
}, TransactionMode);
}
@@ -4928,23 +4930,23 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
var idBlob = id.ToGuidBlob();
- // Delete people
- ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", idBlob);
+ // Delete people
+ ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", idBlob);
- // Delete chapters
- ExecuteWithSingleParam(db, "delete from " + ChaptersTableName + " where ItemId=@Id", idBlob);
+ // Delete chapters
+ ExecuteWithSingleParam(db, "delete from " + ChaptersTableName + " where ItemId=@Id", idBlob);
- // Delete media streams
- ExecuteWithSingleParam(db, "delete from mediastreams where ItemId=@Id", idBlob);
+ // Delete media streams
+ ExecuteWithSingleParam(db, "delete from mediastreams where ItemId=@Id", idBlob);
- // Delete ancestors
- ExecuteWithSingleParam(db, "delete from AncestorIds where ItemId=@Id", idBlob);
+ // Delete ancestors
+ ExecuteWithSingleParam(db, "delete from AncestorIds where ItemId=@Id", idBlob);
- // Delete item values
- ExecuteWithSingleParam(db, "delete from ItemValues where ItemId=@Id", idBlob);
+ // Delete item values
+ ExecuteWithSingleParam(db, "delete from ItemValues where ItemId=@Id", idBlob);
- // Delete the item
- ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", idBlob);
+ // Delete the item
+ ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", idBlob);
}, TransactionMode);
}
}
@@ -4992,6 +4994,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
list.Add(row.GetString(0));
}
}
+
return list;
}
}
@@ -5242,10 +5245,9 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
commandText += " Group By CleanValue";
+ var list = new List<string>();
using (var connection = GetConnection(true))
{
- var list = new List<string>();
-
using (var statement = PrepareStatement(connection, commandText))
{
foreach (var row in statement.ExecuteQuery())
@@ -5257,10 +5259,10 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
}
}
- LogQueryTime("GetItemValueNames", commandText, now);
-
- return list;
}
+
+ LogQueryTime("GetItemValueNames", commandText, now);
+ return list;
}
private QueryResult<(BaseItem, ItemCounts)> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType)
@@ -5417,6 +5419,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
statementTexts.Add(commandText);
}
+
if (query.EnableTotalRecordCount)
{
var countText = "select "
@@ -5428,98 +5431,98 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
statementTexts.Add(countText);
}
+ var list = new List<(BaseItem, ItemCounts)>();
+ var result = new QueryResult<(BaseItem, ItemCounts)>();
using (var connection = GetConnection(true))
{
- return connection.RunInTransaction(db =>
- {
- var list = new List<(BaseItem, ItemCounts)>();
- var result = new QueryResult<(BaseItem, ItemCounts)>();
-
- var statements = PrepareAll(db, statementTexts).ToList();
-
- if (!isReturningZeroItems)
+ connection.RunInTransaction(
+ db =>
{
- using (var statement = statements[0])
+ var statements = PrepareAll(db, statementTexts).ToList();
+
+ if (!isReturningZeroItems)
{
- statement.TryBind("@SelectType", returnType);
- if (EnableJoinUserData(query))
+ using (var statement = statements[0])
{
- statement.TryBind("@UserId", query.User.InternalId);
- }
+ statement.TryBind("@SelectType", returnType);
+ if (EnableJoinUserData(query))
+ {
+ statement.TryBind("@UserId", query.User.InternalId);
+ }
- if (typeSubQuery != null)
- {
- GetWhereClauses(typeSubQuery, null);
- }
- BindSimilarParams(query, statement);
- BindSearchParams(query, statement);
- GetWhereClauses(innerQuery, statement);
- GetWhereClauses(outerQuery, statement);
+ if (typeSubQuery != null)
+ {
+ GetWhereClauses(typeSubQuery, null);
+ }
- var hasEpisodeAttributes = HasEpisodeAttributes(query);
- var hasProgramAttributes = HasProgramAttributes(query);
- var hasServiceName = HasServiceName(query);
- var hasStartDate = HasStartDate(query);
- var hasTrailerTypes = HasTrailerTypes(query);
- var hasArtistFields = HasArtistFields(query);
- var hasSeriesFields = HasSeriesFields(query);
+ BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
+ GetWhereClauses(innerQuery, statement);
+ GetWhereClauses(outerQuery, statement);
- foreach (var row in statement.ExecuteQuery())
- {
- var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields);
- if (item != null)
+ var hasEpisodeAttributes = HasEpisodeAttributes(query);
+ var hasProgramAttributes = HasProgramAttributes(query);
+ var hasServiceName = HasServiceName(query);
+ var hasStartDate = HasStartDate(query);
+ var hasTrailerTypes = HasTrailerTypes(query);
+ var hasArtistFields = HasArtistFields(query);
+ var hasSeriesFields = HasSeriesFields(query);
+
+ foreach (var row in statement.ExecuteQuery())
{
- var countStartColumn = columns.Count - 1;
+ var item = GetItem(row, query, hasProgramAttributes, hasEpisodeAttributes, hasServiceName, hasStartDate, hasTrailerTypes, hasArtistFields, hasSeriesFields);
+ if (item != null)
+ {
+ var countStartColumn = columns.Count - 1;
- list.Add((item, GetItemCounts(row, countStartColumn, typesToCount)));
+ list.Add((item, GetItemCounts(row, countStartColumn, typesToCount)));
+ }
}
}
-
- LogQueryTime("GetItemValues", commandText, now);
}
- }
- if (query.EnableTotalRecordCount)
- {
- commandText = "select "
- + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" }))
- + GetFromText()
- + GetJoinUserDataText(query)
- + whereText;
-
- using (var statement = statements[statements.Count - 1])
+ if (query.EnableTotalRecordCount)
{
- statement.TryBind("@SelectType", returnType);
- if (EnableJoinUserData(query))
- {
- statement.TryBind("@UserId", query.User.InternalId);
- }
+ commandText = "select "
+ + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" }))
+ + GetFromText()
+ + GetJoinUserDataText(query)
+ + whereText;
- if (typeSubQuery != null)
+ using (var statement = statements[statements.Count - 1])
{
- GetWhereClauses(typeSubQuery, null);
- }
- BindSimilarParams(query, statement);
- BindSearchParams(query, statement);
- GetWhereClauses(innerQuery, statement);
- GetWhereClauses(outerQuery, statement);
+ statement.TryBind("@SelectType", returnType);
+ if (EnableJoinUserData(query))
+ {
+ statement.TryBind("@UserId", query.User.InternalId);
+ }
- result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
+ if (typeSubQuery != null)
+ {
+ GetWhereClauses(typeSubQuery, null);
+ }
+ BindSimilarParams(query, statement);
+ BindSearchParams(query, statement);
+ GetWhereClauses(innerQuery, statement);
+ GetWhereClauses(outerQuery, statement);
- LogQueryTime("GetItemValues", commandText, now);
+ result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
+ }
}
- }
-
- if (result.TotalRecordCount == 0)
- {
- result.TotalRecordCount = list.Count;
- }
- result.Items = list.ToArray();
+ },
+ ReadTransactionMode);
+ }
- return result;
+ LogQueryTime("GetItemValues", commandText, now);
- }, ReadTransactionMode);
+ if (result.TotalRecordCount == 0)
+ {
+ result.TotalRecordCount = list.Count;
}
+
+ result.Items = list.ToArray();
+
+ return result;
}
private ItemCounts GetItemCounts(IReadOnlyList<IResultSetValue> reader, int countStartColumn, string[] typesToCount)
@@ -5702,8 +5705,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
var itemIdBlob = itemId.ToGuidBlob();
- // First delete chapters
- db.Execute("delete from People where ItemId=@ItemId", itemIdBlob);
+ // First delete chapters
+ db.Execute("delete from People where ItemId=@ItemId", itemIdBlob);
InsertPeople(itemIdBlob, people, db);
@@ -5863,8 +5866,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{
var itemIdBlob = id.ToGuidBlob();
- // First delete chapters
- db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob);
+ // First delete chapters
+ db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob);
InsertMediaStreams(itemIdBlob, streams, db);
diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
index d6d250fb3..11629b389 100644
--- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
@@ -35,9 +35,8 @@ namespace Emby.Server.Implementations.Data
public string Name => "SQLite";
/// <summary>
- /// Opens the connection to the database
+ /// Opens the connection to the database.
/// </summary>
- /// <returns>Task.</returns>
public void Initialize()
{
using (var connection = GetConnection())
@@ -180,14 +179,10 @@ namespace Emby.Server.Implementations.Data
var id = row[0].ToInt64();
var guid = row[1].ReadGuidFromBlob();
- using (var stream = new MemoryStream(row[2].ToBlob()))
- {
- stream.Position = 0;
- var user = _jsonSerializer.DeserializeFromStream<User>(stream);
- user.InternalId = id;
- user.Id = guid;
- return user;
- }
+ var user = _jsonSerializer.DeserializeFromString<User>(row.GetString(2));
+ user.InternalId = id;
+ user.Id = guid;
+ return user;
}
/// <summary>
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index c78d96d4a..b48193c58 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -34,7 +34,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
<PackageReference Include="ServiceStack.Text.Core" Version="5.6.0" />
<PackageReference Include="sharpcompress" Version="0.23.0" />
- <PackageReference Include="SQLitePCL.pretty.netstandard" Version="1.0.0" />
+ <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.0.1" />
</ItemGroup>
<ItemGroup>
diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
index db7ebb0c0..3589a4893 100644
--- a/Emby.Server.Implementations/Localization/Core/el.json
+++ b/Emby.Server.Implementations/Localization/Core/el.json
@@ -3,7 +3,7 @@
"AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}",
"Application": "Εφαρμογή",
"Artists": "Καλλιτέχνες",
- "AuthenticationSucceededWithUserName": "{0} επιτυχείς σύνδεση",
+ "AuthenticationSucceededWithUserName": "Ο χρήστης {0} επαληθεύτηκε με επιτυχία",
"Books": "Βιβλία",
"CameraImageUploadedFrom": "Μια νέα εικόνα κάμερας έχει αποσταλεί από {0}",
"Channels": "Κανάλια",
@@ -15,9 +15,9 @@
"Favorites": "Αγαπημένα",
"Folders": "Φάκελοι",
"Genres": "Είδη",
- "HeaderAlbumArtists": "Άλμπουμ Καλλιτεχνών",
+ "HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ",
"HeaderCameraUploads": "Μεταφορτώσεις Κάμερας",
- "HeaderContinueWatching": "Συνεχίστε να παρακολουθείτε",
+ "HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
"HeaderFavoriteEpisodes": "Αγαπημένα Επεισόδια",
@@ -27,7 +27,7 @@
"HeaderNextUp": "Επόμενο",
"HeaderRecordingGroups": "Γκρουπ Εγγραφών",
"HomeVideos": "Προσωπικά βίντεο",
- "Inherit": "Inherit",
+ "Inherit": "Κληρονόμηση",
"ItemAddedWithName": "{0} προστέθηκε στη βιβλιοθήκη",
"ItemRemovedWithName": "{0} διαγράφηκε από τη βιβλιοθήκη",
"LabelIpAddressValue": "Διεύθυνση IP: {0}",
@@ -92,6 +92,6 @@
"UserStartedPlayingItemWithValues": "{0} παίζει {1} σε {2}",
"UserStoppedPlayingItemWithValues": "{0} τελείωσε να παίζει {1} σε {2}",
"ValueHasBeenAddedToLibrary": "{0} προστέθηκαν στη βιβλιοθήκη πολυμέσων σας",
- "ValueSpecialEpisodeName": "Special - {0}",
+ "ValueSpecialEpisodeName": "Σπέσιαλ - {0}",
"VersionNumber": "Έκδοση {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index f03184d5b..c78794967 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -23,7 +23,7 @@
"HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteShows": "Series favoritas",
"HeaderFavoriteSongs": "Canciones favoritas",
- "HeaderLiveTV": "TV en directo",
+ "HeaderLiveTV": "Televisión en directo",
"HeaderNextUp": "Siguiendo",
"HeaderRecordingGroups": "Grupos de grabación",
"HomeVideos": "Vídeos caseros",
@@ -79,7 +79,7 @@
"SubtitlesDownloadedForItem": "Descargar subtítulos para {0}",
"Sync": "Sincronizar",
"System": "Sistema",
- "TvShows": "Series de TV",
+ "TvShows": "Programas de televisión",
"User": "Usuario",
"UserCreatedWithName": "El usuario {0} ha sido creado",
"UserDeletedWithName": "El usuario {0} ha sido borrado",
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index e434b7605..6bfedb712 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -17,14 +17,14 @@
"Genres": "Genres",
"HeaderAlbumArtists": "Artistes de l'album",
"HeaderCameraUploads": "Photos transférées",
- "HeaderContinueWatching": "Continuer à regarder",
+ "HeaderContinueWatching": "Reprendre",
"HeaderFavoriteAlbums": "Albums favoris",
"HeaderFavoriteArtists": "Artistes favoris",
"HeaderFavoriteEpisodes": "Épisodes favoris",
"HeaderFavoriteShows": "Séries favorites",
"HeaderFavoriteSongs": "Chansons favorites",
"HeaderLiveTV": "TV en direct",
- "HeaderNextUp": "En Cours",
+ "HeaderNextUp": "À suivre",
"HeaderRecordingGroups": "Groupes d'enregistrements",
"HomeVideos": "Vidéos personnelles",
"Inherit": "Hériter",
diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json
index dbda794ad..daa3f5880 100644
--- a/Emby.Server.Implementations/Localization/Core/nb.json
+++ b/Emby.Server.Implementations/Localization/Core/nb.json
@@ -1,11 +1,11 @@
{
"Albums": "Album",
- "AppDeviceValues": "App:{0}, Enhet {1}",
+ "AppDeviceValues": "App:{0}, Enhet: {1}",
"Application": "Applikasjon",
"Artists": "Artister",
"AuthenticationSucceededWithUserName": "{0} vellykkede autentisert",
"Books": "Bøker",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
+ "CameraImageUploadedFrom": "Et nytt kamerabilde er lastet opp fra {0}",
"Channels": "Kanaler",
"ChapterNameValue": "Kapittel {0}",
"Collections": "Samlinger",
diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
index 0b5ee5d03..1ef5c4b99 100644
--- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs
+++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
@@ -23,22 +23,22 @@ namespace Emby.Server.Implementations.Security
public void Initialize()
{
- using (var connection = GetConnection())
+ string[] queries =
{
- var tableNewlyCreated = !TableExists(connection, "Tokens");
+ "create table if not exists Tokens (Id INTEGER PRIMARY KEY, AccessToken TEXT NOT NULL, DeviceId TEXT NOT NULL, AppName TEXT NOT NULL, AppVersion TEXT NOT NULL, DeviceName TEXT NOT NULL, UserId TEXT, UserName TEXT, IsActive BIT NOT NULL, DateCreated DATETIME NOT NULL, DateLastActivity DATETIME NOT NULL)",
+ "create table if not exists Devices (Id TEXT NOT NULL PRIMARY KEY, CustomName TEXT, Capabilities TEXT)",
+ "drop index if exists idx_AccessTokens",
+ "drop index if exists Tokens1",
+ "drop index if exists Tokens2",
- string[] queries = {
-
- "create table if not exists Tokens (Id INTEGER PRIMARY KEY, AccessToken TEXT NOT NULL, DeviceId TEXT NOT NULL, AppName TEXT NOT NULL, AppVersion TEXT NOT NULL, DeviceName TEXT NOT NULL, UserId TEXT, UserName TEXT, IsActive BIT NOT NULL, DateCreated DATETIME NOT NULL, DateLastActivity DATETIME NOT NULL)",
- "create table if not exists Devices (Id TEXT NOT NULL PRIMARY KEY, CustomName TEXT, Capabilities TEXT)",
+ "create index if not exists Tokens3 on Tokens (AccessToken, DateLastActivity)",
+ "create index if not exists Tokens4 on Tokens (Id, DateLastActivity)",
+ "create index if not exists Devices1 on Devices (Id)"
+ };
- "drop index if exists idx_AccessTokens",
- "drop index if exists Tokens1",
- "drop index if exists Tokens2",
- "create index if not exists Tokens3 on Tokens (AccessToken, DateLastActivity)",
- "create index if not exists Tokens4 on Tokens (Id, DateLastActivity)",
- "create index if not exists Devices1 on Devices (Id)"
- };
+ using (var connection = GetConnection())
+ {
+ var tableNewlyCreated = !TableExists(connection, "Tokens");
connection.RunQueries(queries);
@@ -244,45 +244,46 @@ namespace Emby.Server.Implementations.Security
}
}
- var list = new List<AuthenticationInfo>();
+ var statementTexts = new[]
+ {
+ commandText,
+ "select count (Id) from Tokens" + whereTextWithoutPaging
+ };
+ var list = new List<AuthenticationInfo>();
+ var result = new QueryResult<AuthenticationInfo>();
using (var connection = GetConnection(true))
{
- return connection.RunInTransaction(db =>
- {
- var result = new QueryResult<AuthenticationInfo>();
-
- var statementTexts = new List<string>();
- statementTexts.Add(commandText);
- statementTexts.Add("select count (Id) from Tokens" + whereTextWithoutPaging);
-
- var statements = PrepareAll(db, statementTexts)
- .ToList();
-
- using (var statement = statements[0])
+ connection.RunInTransaction(
+ db =>
{
- BindAuthenticationQueryParams(query, statement);
+ var statements = PrepareAll(db, statementTexts)
+ .ToList();
- foreach (var row in statement.ExecuteQuery())
+ using (var statement = statements[0])
{
- list.Add(Get(row));
- }
+ BindAuthenticationQueryParams(query, statement);
- using (var totalCountStatement = statements[1])
- {
- BindAuthenticationQueryParams(query, totalCountStatement);
-
- result.TotalRecordCount = totalCountStatement.ExecuteQuery()
- .SelectScalarInt()
- .First();
- }
- }
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(Get(row));
+ }
- result.Items = list.ToArray();
- return result;
+ using (var totalCountStatement = statements[1])
+ {
+ BindAuthenticationQueryParams(query, totalCountStatement);
- }, ReadTransactionMode);
+ result.TotalRecordCount = totalCountStatement.ExecuteQuery()
+ .SelectScalarInt()
+ .First();
+ }
+ }
+ },
+ ReadTransactionMode);
}
+
+ result.Items = list.ToArray();
+ return result;
}
private static AuthenticationInfo Get(IReadOnlyList<IResultSetValue> reader)
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index ec7c026e5..e87283477 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -43,7 +43,7 @@
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.0.0" />
<PackageReference Include="SkiaSharp" Version="1.68.0" />
- <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="1.1.14" />
+ <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.0" />
<PackageReference Include="SQLitePCLRaw.provider.sqlite3.netstandard11" Version="1.1.14" />
</ItemGroup>
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 1f78f5afc..496c2032a 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -137,10 +137,7 @@ namespace MediaBrowser.Api.Playback
/// </summary>
private string GetOutputFilePath(StreamState state, EncodingOptions encodingOptions, string outputFileExtension)
{
- var data = GetCommandLineArguments("dummy\\dummy", encodingOptions, state, false);
-
- data += "-" + (state.Request.DeviceId ?? string.Empty)
- + "-" + (state.Request.PlaySessionId ?? string.Empty);
+ var data = $"{state.MediaPath}-{state.UserAgent}-{state.Request.DeviceId}-{state.Request.PlaySessionId}";
var filename = data.GetMD5().ToString("N", CultureInfo.InvariantCulture);
var ext = outputFileExtension.ToLowerInvariant();
@@ -154,7 +151,12 @@ namespace MediaBrowser.Api.Playback
return Path.Combine(folder, filename + ext);
}
- protected virtual string GetDefaultH264Preset() => "superfast";
+ protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
+
+ protected virtual string GetDefaultEncoderPreset()
+ {
+ return "superfast";
+ }
private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource)
{
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index 3c6d33e7e..27eb67ee6 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -304,7 +304,7 @@ namespace MediaBrowser.Api.Playback.Hls
return args;
}
- protected override string GetDefaultH264Preset()
+ protected override string GetDefaultEncoderPreset()
{
return "veryfast";
}
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index fdae59f56..fd686d441 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -895,21 +895,27 @@ namespace MediaBrowser.Api.Playback.Hls
// See if we can save come cpu cycles by avoiding encoding
if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
{
- if (state.VideoStream != null && EncodingHelper.IsH264(state.VideoStream) && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
+ if (state.VideoStream != null && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
{
- args += " -bsf:v h264_mp4toannexb";
+ string bitStreamArgs = EncodingHelper.GetBitStreamArgs(state.VideoStream);
+ if (!string.IsNullOrEmpty(bitStreamArgs))
+ {
+ args += " " + bitStreamArgs;
+ }
}
//args += " -flags -global_header";
}
else
{
- var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
+ var keyFrameArg = string.Format(
+ " -force_key_frames:0 \"expr:gte(t,{0}+n_forced*{1})\"",
+ GetStartNumber(state) * state.SegmentLength,
state.SegmentLength.ToString(CultureInfo.InvariantCulture));
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
- args += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultH264Preset()) + keyFrameArg;
+ args += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultEncoderPreset()) + keyFrameArg;
//args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
@@ -961,10 +967,10 @@ namespace MediaBrowser.Api.Playback.Hls
var timeDeltaParam = string.Empty;
- if (isEncoding && startNumber > 0)
+ if (isEncoding && state.TargetFramerate > 0)
{
- var startTime = state.SegmentLength * startNumber;
- timeDeltaParam = string.Format("-segment_time_delta -{0}", startTime);
+ float startTime = 1 / (state.TargetFramerate.Value * 2);
+ timeDeltaParam = string.Format("-segment_time_delta {0}", Math.Round(startTime, 3));
}
var segmentFormat = GetSegmentFileExtension(state.Request).TrimStart('.');
@@ -973,11 +979,8 @@ namespace MediaBrowser.Api.Playback.Hls
segmentFormat = "mpegts";
}
- var breakOnNonKeyFrames = state.EnableBreakOnNonKeyFrames(videoCodec);
-
- var breakOnNonKeyFramesArg = breakOnNonKeyFrames ? " -break_non_keyframes 1" : "";
-
- return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0{12} -segment_format {11} -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
+ return string.Format(
+ "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format {11} -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
inputModifier,
EncodingHelper.GetInputArgument(state, encodingOptions),
threads,
@@ -989,8 +992,7 @@ namespace MediaBrowser.Api.Playback.Hls
outputPath,
outputTsArg,
timeDeltaParam,
- segmentFormat,
- breakOnNonKeyFramesArg
+ segmentFormat
).Trim();
}
}
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index 3c715c5ad..4a5f4025b 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -92,10 +92,14 @@ namespace MediaBrowser.Api.Playback.Hls
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
{
// if h264_mp4toannexb is ever added, do not use it for live tv
- if (state.VideoStream != null && EncodingHelper.IsH264(state.VideoStream) &&
+ if (state.VideoStream != null &&
!string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
{
- args += " -bsf:v h264_mp4toannexb";
+ string bitStreamArgs = EncodingHelper.GetBitStreamArgs(state.VideoStream);
+ if (!string.IsNullOrEmpty(bitStreamArgs))
+ {
+ args += " " + bitStreamArgs;
+ }
}
}
else
@@ -105,7 +109,7 @@ namespace MediaBrowser.Api.Playback.Hls
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
- args += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultH264Preset()) + keyFrameArg;
+ args += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultEncoderPreset()) + keyFrameArg;
// Add resolution params, if specified
if (!hasGraphicalSubs)
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index 83a3f3e3c..c15681654 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
@@ -77,7 +77,8 @@ namespace MediaBrowser.Api.Playback.Progressive
{
var videoCodec = state.VideoRequest.VideoCodec;
- if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(videoCodec, "h265", StringComparison.OrdinalIgnoreCase))
{
return ".ts";
}
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index ab19fdc26..cfc8a283d 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -120,7 +120,7 @@ namespace MediaBrowser.Api.Playback.Progressive
protected override string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding)
{
- return EncodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, outputPath, GetDefaultH264Preset());
+ return EncodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, outputPath, GetDefaultEncoderPreset());
}
}
}
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index 21a94a4e0..f08d070ca 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -13,6 +13,7 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.Users;
+using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
@@ -247,8 +248,9 @@ namespace MediaBrowser.Api
private readonly INetworkManager _networkManager;
private readonly IDeviceManager _deviceManager;
private readonly IAuthorizationContext _authContext;
+ private readonly ILogger _logger;
- public UserService(IUserManager userManager, ISessionManager sessionMananger, IServerConfigurationManager config, INetworkManager networkManager, IDeviceManager deviceManager, IAuthorizationContext authContext)
+ public UserService(IUserManager userManager, ISessionManager sessionMananger, IServerConfigurationManager config, INetworkManager networkManager, IDeviceManager deviceManager, IAuthorizationContext authContext, ILoggerFactory loggerFactory)
{
_userManager = userManager;
_sessionMananger = sessionMananger;
@@ -256,6 +258,7 @@ namespace MediaBrowser.Api
_networkManager = networkManager;
_deviceManager = deviceManager;
_authContext = authContext;
+ _logger = loggerFactory.CreateLogger(nameof(UserService));
}
public object Get(GetPublicUsers request)
@@ -399,19 +402,27 @@ namespace MediaBrowser.Api
{
var auth = _authContext.GetAuthorizationInfo(Request);
- var result = await _sessionMananger.AuthenticateNewSession(new AuthenticationRequest
+ try
{
- App = auth.Client,
- AppVersion = auth.Version,
- DeviceId = auth.DeviceId,
- DeviceName = auth.Device,
- Password = request.Pw,
- PasswordSha1 = request.Password,
- RemoteEndPoint = Request.RemoteIp,
- Username = request.Username
- }).ConfigureAwait(false);
-
- return ToOptimizedResult(result);
+ var result = await _sessionMananger.AuthenticateNewSession(new AuthenticationRequest
+ {
+ App = auth.Client,
+ AppVersion = auth.Version,
+ DeviceId = auth.DeviceId,
+ DeviceName = auth.Device,
+ Password = request.Pw,
+ PasswordSha1 = request.Password,
+ RemoteEndPoint = Request.RemoteIp,
+ Username = request.Username
+ }).ConfigureAwait(false);
+
+ return ToOptimizedResult(result);
+ }
+ catch(SecurityException e)
+ {
+ // rethrow adding IP address to message
+ throw new SecurityException($"[{Request.RemoteIp}] {e.Message}");
+ }
}
/// <summary>
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index b9ccc93ef..87874001a 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -34,8 +34,16 @@ namespace MediaBrowser.Controller.MediaEncoding
public string GetH264Encoder(EncodingJobInfo state, EncodingOptions encodingOptions)
{
- var defaultEncoder = "libx264";
+ return GetH264OrH265Encoder("libx264", "h264", state, encodingOptions);
+ }
+ public string GetH265Encoder(EncodingJobInfo state, EncodingOptions encodingOptions)
+ {
+ return GetH264OrH265Encoder("libx265", "hevc", state, encodingOptions);
+ }
+
+ private string GetH264OrH265Encoder(string defaultEncoder, string hwEncoder, EncodingJobInfo state, EncodingOptions encodingOptions)
+ {
// Only use alternative encoders for video files.
// When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully
// Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this.
@@ -45,14 +53,14 @@ namespace MediaBrowser.Controller.MediaEncoding
var codecMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
- {"qsv", "h264_qsv"},
- {"h264_qsv", "h264_qsv"},
- {"nvenc", "h264_nvenc"},
- {"amf", "h264_amf"},
- {"omx", "h264_omx"},
- {"h264_v4l2m2m", "h264_v4l2m2m"},
- {"mediacodec", "h264_mediacodec"},
- {"vaapi", "h264_vaapi"}
+ {"qsv", hwEncoder + "_qsv"},
+ {hwEncoder + "_qsv", hwEncoder + "_qsv"},
+ {"nvenc", hwEncoder + "_nvenc"},
+ {"amf", hwEncoder + "_amf"},
+ {"omx", hwEncoder + "_omx"},
+ {hwEncoder + "_v4l2m2m", hwEncoder + "_v4l2m2m"},
+ {"mediacodec", hwEncoder + "_mediacodec"},
+ {"vaapi", hwEncoder + "_vaapi"}
};
if (!string.IsNullOrEmpty(hwType)
@@ -119,6 +127,11 @@ namespace MediaBrowser.Controller.MediaEncoding
if (!string.IsNullOrEmpty(codec))
{
+ if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
+ {
+ return GetH265Encoder(state, encodingOptions);
+ }
if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
{
return GetH264Encoder(state, encodingOptions);
@@ -476,6 +489,30 @@ namespace MediaBrowser.Controller.MediaEncoding
codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1;
}
+ public bool IsH265(MediaStream stream)
+ {
+ var codec = stream.Codec ?? string.Empty;
+
+ return codec.IndexOf("265", StringComparison.OrdinalIgnoreCase) != -1 ||
+ codec.IndexOf("hevc", StringComparison.OrdinalIgnoreCase) != -1;
+ }
+
+ public string GetBitStreamArgs(MediaStream stream)
+ {
+ if (IsH264(stream))
+ {
+ return "-bsf:v h264_mp4toannexb";
+ }
+ else if (IsH265(stream))
+ {
+ return "-bsf:v hevc_mp4toannexb";
+ }
+ else
+ {
+ return null;
+ }
+ }
+
public string GetVideoBitrateParam(EncodingJobInfo state, string videoCodec)
{
var bitrate = state.OutputVideoBitrate;
@@ -494,7 +531,8 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Format(" -b:v {0}", bitrate.Value.ToString(_usCulture));
}
- if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase))
{
// h264
return string.Format(" -maxrate {0} -bufsize {1}",
@@ -515,8 +553,10 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// Clients may direct play higher than level 41, but there's no reason to transcode higher
if (double.TryParse(level, NumberStyles.Any, _usCulture, out double requestLevel)
- && string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)
- && requestLevel > 41)
+ && requestLevel > 41
+ && (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoCodec, "h265", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoCodec, "hevc", StringComparison.OrdinalIgnoreCase)))
{
return "41";
}
@@ -620,49 +660,53 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Gets the video bitrate to specify on the command line
/// </summary>
- public string GetVideoQualityParam(EncodingJobInfo state, string videoEncoder, EncodingOptions encodingOptions, string defaultH264Preset)
+ public string GetVideoQualityParam(EncodingJobInfo state, string videoEncoder, EncodingOptions encodingOptions, string defaultPreset)
{
var param = string.Empty;
var isVc1 = state.VideoStream != null &&
string.Equals(state.VideoStream.Codec, "vc1", StringComparison.OrdinalIgnoreCase);
+ var isLibX265 = string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase);
- if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) || isLibX265)
{
- if (!string.IsNullOrEmpty(encodingOptions.H264Preset))
+ if (!string.IsNullOrEmpty(encodingOptions.EncoderPreset))
{
- param += "-preset " + encodingOptions.H264Preset;
+ param += "-preset " + encodingOptions.EncoderPreset;
}
else
{
- param += "-preset " + defaultH264Preset;
+ param += "-preset " + defaultPreset;
}
- if (encodingOptions.H264Crf >= 0 && encodingOptions.H264Crf <= 51)
+ int encodeCrf = encodingOptions.H264Crf;
+ if (isLibX265)
{
- param += " -crf " + encodingOptions.H264Crf.ToString(CultureInfo.InvariantCulture);
+ encodeCrf = encodingOptions.H265Crf;
+ }
+ if (encodeCrf >= 0 && encodeCrf <= 51)
+ {
+ param += " -crf " + encodeCrf.ToString(CultureInfo.InvariantCulture);
}
else
{
- param += " -crf 23";
+ string defaultCrf = "23";
+ if (isLibX265)
+ {
+ defaultCrf = "28";
+ }
+ param += " -crf " + defaultCrf;
}
}
- else if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
- {
- param += "-preset fast";
-
- param += " -crf 28";
- }
-
// h264 (h264_qsv)
else if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase))
{
string[] valid_h264_qsv = { "veryslow", "slower", "slow", "medium", "fast", "faster", "veryfast" };
- if (valid_h264_qsv.Contains(encodingOptions.H264Preset, StringComparer.OrdinalIgnoreCase))
+ if (valid_h264_qsv.Contains(encodingOptions.EncoderPreset, StringComparer.OrdinalIgnoreCase))
{
- param += "-preset " + encodingOptions.H264Preset;
+ param += "-preset " + encodingOptions.EncoderPreset;
}
else
{
@@ -674,9 +718,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// h264 (h264_nvenc)
- else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase))
{
- switch (encodingOptions.H264Preset)
+ switch (encodingOptions.EncoderPreset)
{
case "veryslow":
@@ -790,7 +835,8 @@ namespace MediaBrowser.Controller.MediaEncoding
// h264_qsv and h264_nvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format
// also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307
if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
+ string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
{
switch (level)
{
@@ -827,9 +873,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
// nvenc doesn't decode with param -level set ?!
- else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase))
+ else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase))
{
- //param += "";
+ // todo param += "";
}
else if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase))
{
@@ -842,6 +889,11 @@ namespace MediaBrowser.Controller.MediaEncoding
param += " -x264opts:0 subme=0:me_range=4:rc_lookahead=10:me=dia:no_chroma_me:8x8dct=0:partitions=none";
}
+ if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
+ {
+ // todo
+ }
+
if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) &&
@@ -1743,7 +1795,8 @@ namespace MediaBrowser.Controller.MediaEncoding
var videoStream = state.VideoStream;
- if (state.DeInterlace("h264", true) && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
+ if ((state.DeInterlace("h264", true) || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true)) &&
+ !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
var inputFramerate = videoStream == null ? null : videoStream.RealFrameRate;
@@ -2404,7 +2457,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return args;
}
- public string GetProgressiveVideoFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string outputPath, string defaultH264Preset)
+ public string GetProgressiveVideoFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string outputPath, string defaultPreset)
{
// Get the output codec name
var videoCodec = GetVideoEncoder(state, encodingOptions);
@@ -2428,7 +2481,7 @@ namespace MediaBrowser.Controller.MediaEncoding
GetInputArgument(state, encodingOptions),
keyFrame,
GetMapArgs(state),
- GetProgressiveVideoArguments(state, encodingOptions, videoCodec, defaultH264Preset),
+ GetProgressiveVideoArguments(state, encodingOptions, videoCodec, defaultPreset),
threads,
GetProgressiveVideoAudioArguments(state, encodingOptions),
GetSubtitleEmbedArguments(state),
@@ -2453,7 +2506,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Empty;
}
- public string GetProgressiveVideoArguments(EncodingJobInfo state, EncodingOptions encodingOptions, string videoCodec, string defaultH264Preset)
+ public string GetProgressiveVideoArguments(EncodingJobInfo state, EncodingOptions encodingOptions, string videoCodec, string defaultPreset)
{
var args = "-codec:v:0 " + videoCodec;
@@ -2464,11 +2517,15 @@ namespace MediaBrowser.Controller.MediaEncoding
if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- if (state.VideoStream != null && IsH264(state.VideoStream) &&
+ if (state.VideoStream != null &&
string.Equals(state.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
{
- args += " -bsf:v h264_mp4toannexb";
+ string bitStreamArgs = GetBitStreamArgs(state.VideoStream);
+ if (!string.IsNullOrEmpty(bitStreamArgs))
+ {
+ args += " " + bitStreamArgs;
+ }
}
if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimestamps)
@@ -2514,7 +2571,7 @@ namespace MediaBrowser.Controller.MediaEncoding
args += GetGraphicalSubtitleParam(state, encodingOptions, videoCodec);
}
- var qualityParam = GetVideoQualityParam(state, videoCodec, encodingOptions, defaultH264Preset);
+ var qualityParam = GetVideoQualityParam(state, videoCodec, encodingOptions, defaultPreset);
if (!string.IsNullOrEmpty(qualityParam))
{
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
index be97c75a2..d64feb2f7 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
@@ -118,14 +118,14 @@ namespace MediaBrowser.Controller.MediaEncoding
/// Gets or sets the profile.
/// </summary>
/// <value>The profile.</value>
- [ApiMember(Name = "Profile", Description = "Optional. Specify a specific h264 profile, e.g. main, baseline, high.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "Profile", Description = "Optional. Specify a specific an encoder profile (varies by encoder), e.g. main, baseline, high.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Profile { get; set; }
/// <summary>
/// Gets or sets the level.
/// </summary>
/// <value>The level.</value>
- [ApiMember(Name = "Level", Description = "Optional. Specify a level for the h264 profile, e.g. 3, 3.1.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "Level", Description = "Optional. Specify a level for the encoder profile (varies by encoder), e.g. 3, 3.1.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Level { get; set; }
/// <summary>
@@ -212,7 +212,7 @@ namespace MediaBrowser.Controller.MediaEncoding
/// Gets or sets the video codec.
/// </summary>
/// <value>The video codec.</value>
- [ApiMember(Name = "VideoCodec", Description = "Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h264, mpeg4, theora, vpx, wmv.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "VideoCodec", Description = "Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h265, h264, mpeg4, theora, vpx, wmv.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string VideoCodec { get; set; }
public string SubtitleCodec { get; set; }
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
index 285ff4ba5..9ae10d980 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -18,7 +18,8 @@ namespace MediaBrowser.Model.Configuration
public string EncoderAppPathDisplay { get; set; }
public string VaapiDevice { get; set; }
public int H264Crf { get; set; }
- public string H264Preset { get; set; }
+ public int H265Crf { get; set; }
+ public string EncoderPreset { get; set; }
public string DeinterlaceMethod { get; set; }
public bool EnableHardwareEncoding { get; set; }
public bool EnableSubtitleExtraction { get; set; }
@@ -34,6 +35,7 @@ namespace MediaBrowser.Model.Configuration
// This is a DRM device that is almost guaranteed to be there on every intel platform, plus it's the default one in ffmpeg if you don't specify anything
VaapiDevice = "/dev/dri/renderD128";
H264Crf = 23;
+ H265Crf = 28;
EnableHardwareEncoding = true;
EnableSubtitleExtraction = true;
HardwareDecodingCodecs = new string[] { "h264", "vc1" };
diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
index 91b74ffe1..3fa5e397a 100644
--- a/deployment/fedora-package-x64/pkg-src/jellyfin.spec
+++ b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
@@ -68,7 +68,7 @@ EOF
%{__install} -D -m 0644 %{SOURCE2} %{buildroot}%{_sysconfdir}/sysconfig/%{name}
%{__install} -D -m 0600 %{SOURCE3} %{buildroot}%{_sysconfdir}/sudoers.d/%{name}-sudoers
%{__install} -D -m 0755 %{SOURCE4} %{buildroot}%{_libexecdir}/%{name}/restart.sh
-%{__install} -D -m 0644 %{SOURCE6} %{buildroot}%{_prefix}/lib/firewalld/service/%{name}.xml
+%{__install} -D -m 0644 %{SOURCE6} %{buildroot}%{_prefix}/lib/firewalld/services/%{name}.xml
%files
%{_libdir}/%{name}/jellyfin-web/*
@@ -83,7 +83,7 @@ EOF
%{_libdir}/%{name}/sosdocsunix.txt
%{_unitdir}/%{name}.service
%{_libexecdir}/%{name}/restart.sh
-%{_prefix}/lib/firewalld/service/%{name}.xml
+%{_prefix}/lib/firewalld/services/%{name}.xml
%attr(755,jellyfin,jellyfin) %dir %{_sysconfdir}/%{name}
%config %{_sysconfdir}/sysconfig/%{name}
%config(noreplace) %attr(600,root,root) %{_sysconfdir}/sudoers.d/%{name}-sudoers