Merge branch 'master' into userdb-efcore

# Conflicts:
#	Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
#	Emby.Server.Implementations/Library/UserManager.cs
#	Jellyfin.Data/Entities/User.cs
#	Jellyfin.Data/ISavingChanges.cs
#	Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
#	Jellyfin.Server.Implementations/JellyfinDb.cs
#	Jellyfin.Server/Migrations/MigrationRunner.cs
#	MediaBrowser.Model/Notifications/NotificationOptions.cs
#	MediaBrowser.sln
pull/3423/head
Patrick Barron 4 years ago
commit c1360a1dc3

@ -13,7 +13,7 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
max_line_length = null
max_line_length = off
# YAML indentation
[*.{yml,yaml}]
@ -22,6 +22,7 @@ indent_size = 2
# XML indentation
[*.{csproj,xml}]
indent_size = 2
###############################
# .NET Coding Conventions #
###############################
@ -51,11 +52,12 @@ dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
dotnet_prefer_inferred_tuple_names = true:suggestion
dotnet_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
###############################
# Naming Conventions #
###############################
@ -67,7 +69,7 @@ dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style
dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field
dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected
dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected
dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
@ -159,6 +161,7 @@ csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
###############################
# C# Formatting Rules #
###############################
@ -189,9 +192,3 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false
# Wrapping preferences
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true
###############################
# VB Coding Conventions #
###############################
[*.vb]
# Modifier preferences
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System.Buffers.Binary;
using System.IO;

@ -13,6 +13,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System.IO;
namespace DvdLib.Ifo

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System.IO;
namespace DvdLib.Ifo

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System.IO;
namespace DvdLib.Ifo

@ -1,3 +1,5 @@
#pragma warning disable CS1591
namespace DvdLib.Ifo
{
public class Chapter

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.IO;

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
namespace DvdLib.Ifo

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System.Collections.Generic;
namespace DvdLib.Ifo

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using System.IO;
using System.Linq;

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using System.IO;

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
namespace DvdLib.Ifo

@ -89,14 +89,14 @@ namespace Emby.Naming.Video
if (parseName)
{
var cleanDateTimeResult = CleanDateTime(name);
name = cleanDateTimeResult.Name;
year = cleanDateTimeResult.Year;
if (extraResult.ExtraType == null
&& TryCleanString(cleanDateTimeResult.Name, out ReadOnlySpan<char> newName))
&& TryCleanString(name, out ReadOnlySpan<char> newName))
{
name = newName.ToString();
}
year = cleanDateTimeResult.Year;
}
return new VideoFileInfo

@ -1,7 +1,6 @@
using System.Collections.Generic;
using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.Updates;
using MediaBrowser.Providers.Music;
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
namespace Emby.Server.Implementations

@ -0,0 +1,389 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
namespace Emby.Server.Implementations.Data
{
public class SqliteUserDataRepository : BaseSqliteRepository, IUserDataRepository
{
public SqliteUserDataRepository(
ILogger<SqliteUserDataRepository> logger,
IApplicationPaths appPaths)
: base(logger)
{
DbFilePath = Path.Combine(appPaths.DataPath, "library.db");
}
/// <inheritdoc />
public string Name => "SQLite";
/// <summary>
/// Opens the connection to the database.
/// </summary>
public void Initialize(IUserManager userManager, SemaphoreSlim dbLock, SQLiteDatabaseConnection dbConnection)
{
WriteLock.Dispose();
WriteLock = dbLock;
WriteConnection?.Dispose();
WriteConnection = dbConnection;
using (var connection = GetConnection())
{
var userDatasTableExists = TableExists(connection, "UserDatas");
var userDataTableExists = TableExists(connection, "userdata");
var users = userDatasTableExists ? null : userManager.Users;
connection.RunInTransaction(db =>
{
db.ExecuteAll(string.Join(";", new[] {
"create table if not exists UserDatas (key nvarchar not null, userId INT not null, rating float null, played bit not null, playCount int not null, isFavorite bit not null, playbackPositionTicks bigint not null, lastPlayedDate datetime null, AudioStreamIndex INT, SubtitleStreamIndex INT)",
"drop index if exists idx_userdata",
"drop index if exists idx_userdata1",
"drop index if exists idx_userdata2",
"drop index if exists userdataindex1",
"drop index if exists userdataindex",
"drop index if exists userdataindex3",
"drop index if exists userdataindex4",
"create unique index if not exists UserDatasIndex1 on UserDatas (key, userId)",
"create index if not exists UserDatasIndex2 on UserDatas (key, userId, played)",
"create index if not exists UserDatasIndex3 on UserDatas (key, userId, playbackPositionTicks)",
"create index if not exists UserDatasIndex4 on UserDatas (key, userId, isFavorite)"
}));
if (userDataTableExists)
{
var existingColumnNames = GetColumnNames(db, "userdata");
AddColumn(db, "userdata", "InternalUserId", "int", existingColumnNames);
AddColumn(db, "userdata", "AudioStreamIndex", "int", existingColumnNames);
AddColumn(db, "userdata", "SubtitleStreamIndex", "int", existingColumnNames);
if (!userDatasTableExists)
{
ImportUserIds(db, users);
db.ExecuteAll("INSERT INTO UserDatas (key, userId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex) SELECT key, InternalUserId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex from userdata where InternalUserId not null");
}
}
}, TransactionMode);
}
}
private void ImportUserIds(IDatabaseConnection db, IEnumerable<User> users)
{
var userIdsWithUserData = GetAllUserIdsWithUserData(db);
using (var statement = db.PrepareStatement("update userdata set InternalUserId=@InternalUserId where UserId=@UserId"))
{
foreach (var user in users)
{
if (!userIdsWithUserData.Contains(user.Id))
{
continue;
}
statement.TryBind("@UserId", user.Id.ToByteArray());
statement.TryBind("@InternalUserId", user.InternalId);
statement.MoveNext();
statement.Reset();
}
}
}
private List<Guid> GetAllUserIdsWithUserData(IDatabaseConnection db)
{
var list = new List<Guid>();
using (var statement = PrepareStatement(db, "select DISTINCT UserId from UserData where UserId not null"))
{
foreach (var row in statement.ExecuteQuery())
{
try
{
list.Add(row[0].ReadGuidFromBlob());
}
catch (Exception ex)
{
Logger.LogError(ex, "Error while getting user");
}
}
}
return list;
}
/// <summary>
/// Saves the user data.
/// </summary>
public void SaveUserData(long internalUserId, string key, UserItemData userData, CancellationToken cancellationToken)
{
if (userData == null)
{
throw new ArgumentNullException(nameof(userData));
}
if (internalUserId <= 0)
{
throw new ArgumentNullException(nameof(internalUserId));
}
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException(nameof(key));
}
PersistUserData(internalUserId, key, userData, cancellationToken);
}
public void SaveAllUserData(long internalUserId, UserItemData[] userData, CancellationToken cancellationToken)
{
if (userData == null)
{
throw new ArgumentNullException(nameof(userData));
}
if (internalUserId <= 0)
{
throw new ArgumentNullException(nameof(internalUserId));
}
PersistAllUserData(internalUserId, userData, cancellationToken);
}
/// <summary>
/// Persists the user data.
/// </summary>
/// <param name="internalUserId">The user id.</param>
/// <param name="key">The key.</param>
/// <param name="userData">The user data.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public void PersistUserData(long internalUserId, string key, UserItemData userData, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using (var connection = GetConnection())
{
connection.RunInTransaction(db =>
{
SaveUserData(db, internalUserId, key, userData);
}, TransactionMode);
}
}
private static void SaveUserData(IDatabaseConnection db, long internalUserId, string key, UserItemData userData)
{
using (var statement = db.PrepareStatement("replace into UserDatas (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate,@AudioStreamIndex,@SubtitleStreamIndex)"))
{
statement.TryBind("@userId", internalUserId);
statement.TryBind("@key", key);
if (userData.Rating.HasValue)
{
statement.TryBind("@rating", userData.Rating.Value);
}
else
{
statement.TryBindNull("@rating");
}
statement.TryBind("@played", userData.Played);
statement.TryBind("@playCount", userData.PlayCount);
statement.TryBind("@isFavorite", userData.IsFavorite);
statement.TryBind("@playbackPositionTicks", userData.PlaybackPositionTicks);
if (userData.LastPlayedDate.HasValue)
{
statement.TryBind("@lastPlayedDate", userData.LastPlayedDate.Value.ToDateTimeParamValue());
}
else
{
statement.TryBindNull("@lastPlayedDate");
}
if (userData.AudioStreamIndex.HasValue)
{
statement.TryBind("@AudioStreamIndex", userData.AudioStreamIndex.Value);
}
else
{
statement.TryBindNull("@AudioStreamIndex");
}
if (userData.SubtitleStreamIndex.HasValue)
{
statement.TryBind("@SubtitleStreamIndex", userData.SubtitleStreamIndex.Value);
}
else
{
statement.TryBindNull("@SubtitleStreamIndex");
}
statement.MoveNext();
}
}
/// <summary>
/// Persist all user data for the specified user
/// </summary>
private void PersistAllUserData(long internalUserId, UserItemData[] userDataList, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
using (var connection = GetConnection())
{
connection.RunInTransaction(db =>
{
foreach (var userItemData in userDataList)
{
SaveUserData(db, internalUserId, userItemData.Key, userItemData);
}
}, TransactionMode);
}
}
/// <summary>
/// Gets the user data.
/// </summary>
/// <param name="internalUserId">The user id.</param>
/// <param name="key">The key.</param>
/// <returns>Task{UserItemData}.</returns>
/// <exception cref="ArgumentNullException">
/// userId
/// or
/// key
/// </exception>
public UserItemData GetUserData(long internalUserId, string key)
{
if (internalUserId <= 0)
{
throw new ArgumentNullException(nameof(internalUserId));
}
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException(nameof(key));
}
using (var connection = GetConnection(true))
{
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where key =@Key and userId=@UserId"))
{
statement.TryBind("@UserId", internalUserId);
statement.TryBind("@Key", key);
foreach (var row in statement.ExecuteQuery())
{
return ReadRow(row);
}
}
return null;
}
}
public UserItemData GetUserData(long internalUserId, List<string> keys)
{
if (keys == null)
{
throw new ArgumentNullException(nameof(keys));
}
if (keys.Count == 0)
{
return null;
}
return GetUserData(internalUserId, keys[0]);
}
/// <summary>
/// Return all user-data associated with the given user
/// </summary>
/// <param name="internalUserId"></param>
/// <returns></returns>
public List<UserItemData> GetAllUserData(long internalUserId)
{
if (internalUserId <= 0)
{
throw new ArgumentNullException(nameof(internalUserId));
}
var list = new List<UserItemData>();
using (var connection = GetConnection())
{
using (var statement = connection.PrepareStatement("select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from UserDatas where userId=@UserId"))
{
statement.TryBind("@UserId", internalUserId);
foreach (var row in statement.ExecuteQuery())
{
list.Add(ReadRow(row));
}
}
}
return list;
}
/// <summary>
/// Read a row from the specified reader into the provided userData object
/// </summary>
/// <param name="reader"></param>
private UserItemData ReadRow(IReadOnlyList<IResultSetValue> reader)
{
var userData = new UserItemData();
userData.Key = reader[0].ToString();
//userData.UserId = reader[1].ReadGuidFromBlob();
if (reader[2].SQLiteType != SQLiteType.Null)
{
userData.Rating = reader[2].ToDouble();
}
userData.Played = reader[3].ToBool();
userData.PlayCount = reader[4].ToInt();
userData.IsFavorite = reader[5].ToBool();
userData.PlaybackPositionTicks = reader[6].ToInt64();
if (reader[7].SQLiteType != SQLiteType.Null)
{
userData.LastPlayedDate = reader[7].TryReadDateTime();
}
if (reader[8].SQLiteType != SQLiteType.Null)
{
userData.AudioStreamIndex = reader[8].ToInt();
}
if (reader[9].SQLiteType != SQLiteType.Null)
{
userData.SubtitleStreamIndex = reader[9].ToInt();
}
return userData;
}
/// <inheritdoc/>
/// <remarks>
/// There is nothing to dispose here since <see cref="BaseSqliteRepository.WriteLock"/> and
/// <see cref="BaseSqliteRepository.WriteConnection"/> are managed by <see cref="SqliteItemRepository"/>.
/// See <see cref="Initialize(IUserManager, SemaphoreSlim, SQLiteDatabaseConnection)"/>.
/// </remarks>
protected override void Dispose(bool dispose)
{
}
}
}

@ -4,7 +4,7 @@
"Folders": "Fouers",
"Favorites": "Gunstelinge",
"HeaderFavoriteShows": "Gunsteling Vertonings",
"ValueSpecialEpisodeName": "Spesiaal - {0}",
"ValueSpecialEpisodeName": "Spesiale - {0}",
"HeaderAlbumArtists": "Album Kunstenaars",
"Books": "Boeke",
"HeaderNextUp": "Volgende",

@ -11,7 +11,7 @@
"Collections": "Colecciones",
"DeviceOfflineWithName": "{0} se ha desconectado",
"DeviceOnlineWithName": "{0} está conectado",
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión de {0}",
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión desde {0}",
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",

@ -71,7 +71,7 @@
"ScheduledTaskFailedWithName": "{0} falló",
"ScheduledTaskStartedWithName": "{0} iniciada",
"ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Series",
"Shows": "Mostrar",
"Songs": "Canciones",
"StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
"SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}",

@ -1,5 +1,5 @@
{
"HeaderLiveTV": "Suorat lähetykset",
"HeaderLiveTV": "Live-TV",
"NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa.",
"NameSeasonUnknown": "Tuntematon Kausi",
"NameSeasonNumber": "Kausi {0}",
@ -12,7 +12,7 @@
"MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen asetusryhmä {0} on päivitetty",
"MessageApplicationUpdatedTo": "Jellyfin palvelin on päivitetty versioon {0}",
"MessageApplicationUpdated": "Jellyfin palvelin on päivitetty",
"Latest": "Viimeisin",
"Latest": "Uusimmat",
"LabelRunningTimeValue": "Toiston kesto: {0}",
"LabelIpAddressValue": "IP-osoite: {0}",
"ItemRemovedWithName": "{0} poistettiin kirjastosta",
@ -41,7 +41,7 @@
"CameraImageUploadedFrom": "Uusi kamerakuva on ladattu {0}",
"Books": "Kirjat",
"AuthenticationSucceededWithUserName": "{0} todennus onnistui",
"Artists": "Esiintyjät",
"Artists": "Artistit",
"Application": "Sovellus",
"AppDeviceValues": "Sovellus: {0}, Laite: {1}",
"Albums": "Albumit",
@ -67,21 +67,21 @@
"UserDownloadingItemWithValues": "{0} lataa {1}",
"UserDeletedWithName": "Käyttäjä {0} poistettu",
"UserCreatedWithName": "Käyttäjä {0} luotu",
"TvShows": "TV-Ohjelmat",
"TvShows": "TV-sarjat",
"Sync": "Synkronoi",
"SubtitleDownloadFailureFromForItem": "Tekstityksen lataaminen epäonnistui {0} - {1}",
"SubtitleDownloadFailureFromForItem": "Tekstitysten lataus ({0} -> {1}) epäonnistui //this string would have to be generated for each provider and movie because of finnish cases, sorry",
"StartupEmbyServerIsLoading": "Jellyfin palvelin latautuu. Kokeile hetken kuluttua uudelleen.",
"Songs": "Kappaleet",
"Shows": "Ohjelmat",
"ServerNameNeedsToBeRestarted": "{0} vaatii uudelleenkäynnistyksen",
"Shows": "Sarjat",
"ServerNameNeedsToBeRestarted": "{0} täytyy käynnistää uudelleen",
"ProviderValue": "Tarjoaja: {0}",
"Plugin": "Liitännäinen",
"NotificationOptionVideoPlaybackStopped": "Videon toisto pysäytetty",
"NotificationOptionVideoPlayback": "Videon toisto aloitettu",
"NotificationOptionUserLockedOut": "Käyttäjä lukittu",
"NotificationOptionVideoPlayback": "Videota toistetaan",
"NotificationOptionUserLockedOut": "Käyttäjä kirjautui ulos",
"NotificationOptionTaskFailed": "Ajastettu tehtävä epäonnistui",
"NotificationOptionServerRestartRequired": "Palvelimen uudelleenkäynnistys vaaditaan",
"NotificationOptionPluginUpdateInstalled": "Lisäosan päivitys asennettu",
"NotificationOptionServerRestartRequired": "Palvelin pitää käynnistää uudelleen",
"NotificationOptionPluginUpdateInstalled": "Liitännäinen päivitetty",
"NotificationOptionPluginUninstalled": "Liitännäinen poistettu",
"NotificationOptionPluginInstalled": "Liitännäinen asennettu",
"NotificationOptionPluginError": "Ongelma liitännäisessä",
@ -90,8 +90,8 @@
"NotificationOptionCameraImageUploaded": "Kameran kuva ladattu",
"NotificationOptionAudioPlaybackStopped": "Äänen toisto lopetettu",
"NotificationOptionAudioPlayback": "Toistetaan ääntä",
"NotificationOptionApplicationUpdateInstalled": "Uusi sovellusversio asennettu",
"NotificationOptionApplicationUpdateAvailable": "Sovelluksesta on uusi versio saatavilla",
"NotificationOptionApplicationUpdateInstalled": "Sovelluspäivitys asennettu",
"NotificationOptionApplicationUpdateAvailable": "Ohjelmistopäivitys saatavilla",
"TasksMaintenanceCategory": "Ylläpito",
"TaskDownloadMissingSubtitlesDescription": "Etsii puuttuvia tekstityksiä videon metadatatietojen pohjalta.",
"TaskDownloadMissingSubtitles": "Lataa puuttuvat tekstitykset",

@ -94,5 +94,23 @@
"ValueSpecialEpisodeName": "Spécial - {0}",
"VersionNumber": "Version {0}",
"TasksLibraryCategory": "Bibliothèque",
"TasksMaintenanceCategory": "Entretien"
"TasksMaintenanceCategory": "Entretien",
"TaskDownloadMissingSubtitlesDescription": "Recherche l'internet pour des sous-titres manquants à base de métadonnées configurées.",
"TaskDownloadMissingSubtitles": "Télécharger des sous-titres manquants",
"TaskRefreshChannelsDescription": "Rafraîchit des informations des chaines d'internet.",
"TaskRefreshChannels": "Rafraîchir des chaines",
"TaskCleanTranscodeDescription": "Retirer des fichiers de transcodage de plus qu'un jour.",
"TaskCleanTranscode": "Nettoyer le directoire de transcodage",
"TaskUpdatePluginsDescription": "Télécharger et installer des mises à jours des plugins qui sont configurés m.à.j. automisés.",
"TaskUpdatePlugins": "Mise à jour des plugins",
"TaskRefreshPeopleDescription": "Met à jour les métadonnées pour les acteurs et réalisateurs dans votre bibliothèque.",
"TaskRefreshPeople": "Rafraîchir les acteurs",
"TaskCleanLogsDescription": "Retire les données qui ont plus que {0} jours.",
"TaskCleanLogs": "Nettoyer les données de directoire",
"TaskRefreshLibraryDescription": "Analyse votre bibliothèque média pour des nouveaux fichiers et rafraîchit les métadonnées.",
"TaskRefreshChapterImages": "Extraire des images du chapitre",
"TaskRefreshChapterImagesDescription": "Créer des vignettes pour des vidéos qui ont des chapitres",
"TaskRefreshLibrary": "Analyser la bibliothèque de média",
"TaskCleanCache": "Nettoyer le cache de directoire",
"TasksApplicationCategory": "Application"
}

@ -1,41 +1,41 @@
{
"Albums": "Albom",
"AppDeviceValues": "App: {0}, Grät: {1}",
"Application": "Aawändig",
"Artists": "Könstler",
"AuthenticationSucceededWithUserName": "{0} het sech aagmäudet",
"Books": "Büecher",
"CameraImageUploadedFrom": "Es nöis Foti esch ufeglade worde vo {0}",
"Channels": "Kanäu",
"ChapterNameValue": "Kapitu {0}",
"Collections": "Sammlige",
"DeviceOfflineWithName": "{0} esch offline gange",
"DeviceOnlineWithName": "{0} esch online cho",
"FailedLoginAttemptWithUserName": "Fäugschlagne Aamäudeversuech vo {0}",
"Favorites": "Favorite",
"Albums": "Alben",
"AppDeviceValues": "App: {0}, Gerät: {1}",
"Application": "Anwendung",
"Artists": "Künstler",
"AuthenticationSucceededWithUserName": "{0} hat sich angemeldet",
"Books": "Bücher",
"CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen",
"Channels": "Kanäle",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Sammlungen",
"DeviceOfflineWithName": "{0} wurde getrennt",
"DeviceOnlineWithName": "{0} ist verbunden",
"FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}",
"Favorites": "Favoriten",
"Folders": "Ordner",
"Genres": "Genres",
"HeaderAlbumArtists": "Albom-Könstler",
"HeaderAlbumArtists": "Album-Künstler",
"HeaderCameraUploads": "Kamera-Uploads",
"HeaderContinueWatching": "Wiiterluege",
"HeaderFavoriteAlbums": "Lieblingsalbe",
"HeaderFavoriteArtists": "Lieblings-Interprete",
"HeaderFavoriteEpisodes": "Lieblingsepisode",
"HeaderFavoriteShows": "Lieblingsserie",
"HeaderContinueWatching": "weiter schauen",
"HeaderFavoriteAlbums": "Lieblingsalben",
"HeaderFavoriteArtists": "Lieblings-Künstler",
"HeaderFavoriteEpisodes": "Lieblingsepisoden",
"HeaderFavoriteShows": "Lieblingsserien",
"HeaderFavoriteSongs": "Lieblingslieder",
"HeaderLiveTV": "Live-Färnseh",
"HeaderNextUp": "Als nächts",
"HeaderRecordingGroups": "Ufnahmegruppe",
"HomeVideos": "Heimfilmli",
"Inherit": "Hinzuefüege",
"ItemAddedWithName": "{0} esch de Bibliothek dezuegfüegt worde",
"ItemRemovedWithName": "{0} esch vo de Bibliothek entfärnt worde",
"LabelIpAddressValue": "IP-Adrässe: {0}",
"LabelRunningTimeValue": "Loufziit: {0}",
"Latest": "Nöischti",
"MessageApplicationUpdated": "Jellyfin Server esch aktualisiert worde",
"MessageApplicationUpdatedTo": "Jellyfin Server esch of Version {0} aktualisiert worde",
"MessageNamedServerConfigurationUpdatedWithValue": "De Serveriistöuigsberiich {0} esch aktualisiert worde",
"HeaderLiveTV": "Live-Fernseh",
"HeaderNextUp": "Als Nächstes",
"HeaderRecordingGroups": "Aufnahme-Gruppen",
"HomeVideos": "Heimvideos",
"Inherit": "Vererben",
"ItemAddedWithName": "{0} wurde der Bibliothek hinzugefügt",
"ItemRemovedWithName": "{0} wurde aus der Bibliothek entfernt",
"LabelIpAddressValue": "IP-Adresse: {0}",
"LabelRunningTimeValue": "Laufzeit: {0}",
"Latest": "Neueste",
"MessageApplicationUpdated": "Jellyfin-Server wurde aktualisiert",
"MessageApplicationUpdatedTo": "Jellyfin-Server wurde auf Version {0} aktualisiert",
"MessageNamedServerConfigurationUpdatedWithValue": "Der Server-Einstellungsbereich {0} wurde aktualisiert",
"MessageServerConfigurationUpdated": "Serveriistöuige send aktualisiert worde",
"MixedContent": "Gmeschti Inhäut",
"Movies": "Film",
@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Audiowedergab gstartet",
"NotificationOptionAudioPlaybackStopped": "Audiwedergab gstoppt",
"NotificationOptionCameraImageUploaded": "Foti ueglade",
"NotificationOptionInstallationFailed": "Installationsfäuer",
"NotificationOptionInstallationFailed": "Installationsfehler",
"NotificationOptionNewLibraryContent": "Nöie Inhaut hinzuegfüegt",
"NotificationOptionPluginError": "Plugin-Fäuer",
"NotificationOptionPluginInstalled": "Plugin installiert",
@ -92,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} het d'Wedergab vo {1} of {2} gstoppt",
"ValueHasBeenAddedToLibrary": "{0} esch dinnere Biblithek hinzuegfüegt worde",
"ValueSpecialEpisodeName": "Extra - {0}",
"VersionNumber": "Version {0}"
"VersionNumber": "Version {0}",
"TaskCleanLogs": "Lösche Log Pfad",
"TaskRefreshLibraryDescription": "Scanne alle Bibliotheken für hinzugefügte Datein und erneuere Metadaten.",
"TaskRefreshLibrary": "Scanne alle Bibliotheken",
"TaskRefreshChapterImagesDescription": "Kreiert Vorschaubilder für Videos welche Kapitel haben.",
"TaskRefreshChapterImages": "Extrahiere Kapitel-Bilder",
"TaskCleanCacheDescription": "Löscht Zwischenspeicherdatein die nicht länger von System gebraucht werden.",
"TaskCleanCache": "Leere Cache Pfad",
"TasksChannelsCategory": "Internet Kanäle",
"TasksApplicationCategory": "Applikation",
"TasksLibraryCategory": "Bibliothek",
"TasksMaintenanceCategory": "Verwaltung",
"TaskDownloadMissingSubtitlesDescription": "Durchsucht das Internet nach fehlenden Untertiteln, basierend auf den Metadaten Einstellungen.",
"TaskDownloadMissingSubtitles": "Lade fehlende Untertitel herunter",
"TaskRefreshChannelsDescription": "Aktualisiert Internet Kanal Informationen.",
"TaskRefreshChannels": "Aktualisiere Kanäle",
"TaskCleanTranscodeDescription": "Löscht Transkodierdateien welche älter als ein Tag sind.",
"TaskCleanTranscode": "Räume Transcodier Verzeichnis auf",
"TaskUpdatePluginsDescription": "Lädt Aktualisierungen für Erweiterungen herunter und installiert diese, für welche automatische Aktualisierungen konfiguriert sind.",
"TaskUpdatePlugins": "Aktualisiere Erweiterungen",
"TaskRefreshPeopleDescription": "Aktualisiert Metadaten für Schausteller und Regisseure in deiner Bibliothek.",
"TaskRefreshPeople": "Aktualisiere Schauspieler",
"TaskCleanLogsDescription": "Löscht Log Dateien die älter als {0} Tage sind."
}

@ -62,7 +62,7 @@
"NotificationOptionVideoPlayback": "Video playback started",
"NotificationOptionVideoPlaybackStopped": "Video playback stopped",
"Photos": "תמונות",
"Playlists": "רשימות ניגון",
"Playlists": "רשימות הפעלה",
"Plugin": "Plugin",
"PluginInstalledWithName": "{0} was installed",
"PluginUninstalledWithName": "{0} was uninstalled",

@ -30,7 +30,7 @@
"Inherit": "Naslijedi",
"ItemAddedWithName": "{0} je dodano u biblioteku",
"ItemRemovedWithName": "{0} je uklonjen iz biblioteke",
"LabelIpAddressValue": "Ip adresa: {0}",
"LabelIpAddressValue": "IP adresa: {0}",
"LabelRunningTimeValue": "Vrijeme rada: {0}",
"Latest": "Najnovije",
"MessageApplicationUpdated": "Jellyfin Server je ažuriran",
@ -92,5 +92,13 @@
"UserStoppedPlayingItemWithValues": "{0} je zaustavio {1}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"ValueSpecialEpisodeName": "Specijal - {0}",
"VersionNumber": "Verzija {0}"
"VersionNumber": "Verzija {0}",
"TaskRefreshLibraryDescription": "Skenira vašu medijsku knjižnicu sa novim datotekama i osvježuje metapodatke.",
"TaskRefreshLibrary": "Skeniraj medijsku knjižnicu",
"TaskRefreshChapterImagesDescription": "Stvara sličice za videozapise koji imaju poglavlja.",
"TaskRefreshChapterImages": "Raspakiraj slike poglavlja",
"TaskCleanCacheDescription": "Briše priručne datoteke nepotrebne za sistem.",
"TaskCleanCache": "Očisti priručnu memoriju",
"TasksApplicationCategory": "Aplikacija",
"TasksMaintenanceCategory": "Održavanje"
}

@ -5,7 +5,7 @@
"Artists": "Artisti",
"AuthenticationSucceededWithUserName": "{0} autenticato con successo",
"Books": "Libri",
"CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera dal device {0}",
"CameraImageUploadedFrom": "È stata caricata una nuova fotografia da {0}",
"Channels": "Canali",
"ChapterNameValue": "Capitolo {0}",
"Collections": "Collezioni",

@ -91,5 +91,12 @@
"Songs": "Песни",
"Shows": "Серии",
"ServerNameNeedsToBeRestarted": "{0} треба да се рестартира",
"ScheduledTaskStartedWithName": "{0} започна"
"ScheduledTaskStartedWithName": "{0} започна",
"TaskRefreshChapterImages": "Извези Слики од Поглавје",
"TaskCleanCacheDescription": "Ги брише кешираните фајлови што не се повеќе потребни од системот.",
"TaskCleanCache": "Исчисти Го Кешот",
"TasksChannelsCategory": "Интернет Канали",
"TasksApplicationCategory": "Апликација",
"TasksLibraryCategory": "Библиотека",
"TasksMaintenanceCategory": "Одржување"
}

@ -5,7 +5,7 @@
"Artists": "Artiesten",
"AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd",
"Books": "Boeken",
"CameraImageUploadedFrom": "Er is een nieuwe afbeelding toegevoegd via {0}",
"CameraImageUploadedFrom": "Er is een nieuwe camera afbeelding toegevoegd via {0}",
"Channels": "Kanalen",
"ChapterNameValue": "Hoofdstuk {0}",
"Collections": "Verzamelingen",
@ -26,7 +26,7 @@
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Volgende",
"HeaderRecordingGroups": "Opnamegroepen",
"HomeVideos": "Start video's",
"HomeVideos": "Home video's",
"Inherit": "Overerven",
"ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek",
"ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek",
@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Muziek gestart",
"NotificationOptionAudioPlaybackStopped": "Muziek gestopt",
"NotificationOptionCameraImageUploaded": "Camera-afbeelding geüpload",
"NotificationOptionInstallationFailed": "Installatie mislukking",
"NotificationOptionInstallationFailed": "Installatie mislukt",
"NotificationOptionNewLibraryContent": "Nieuwe content toegevoegd",
"NotificationOptionPluginError": "Plug-in fout",
"NotificationOptionPluginInstalled": "Plug-in geïnstalleerd",

@ -92,5 +92,26 @@
"UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}",
"ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici",
"ValueSpecialEpisodeName": "Poseben - {0}",
"VersionNumber": "Različica {0}"
"VersionNumber": "Različica {0}",
"TaskDownloadMissingSubtitles": "Prenesi manjkajoče podnapise",
"TaskRefreshChannelsDescription": "Osveži podatke spletnih kanalov.",
"TaskRefreshChannels": "Osveži kanale",
"TaskCleanTranscodeDescription": "Izbriše več kot dan stare datoteke prekodiranja.",
"TaskCleanTranscode": "Počisti mapo prekodiranja",
"TaskUpdatePluginsDescription": "Prenese in namesti posodobitve za dodatke, ki imajo omogočene samodejne posodobitve.",
"TaskUpdatePlugins": "Posodobi dodatke",
"TaskRefreshPeopleDescription": "Osveži metapodatke za igralce in režiserje v vaši knjižnici.",
"TaskRefreshPeople": "Osveži osebe",
"TaskCleanLogsDescription": "Izbriše dnevniške datoteke starejše od {0} dni.",
"TaskCleanLogs": "Počisti mapo dnevnika",
"TaskRefreshLibraryDescription": "Preišče vašo knjižnico za nove datoteke in osveži metapodatke.",
"TaskRefreshLibrary": "Preišči knjižnico predstavnosti",
"TaskRefreshChapterImagesDescription": "Ustvari sličice za poglavja videoposnetkov.",
"TaskRefreshChapterImages": "Izvleči slike poglavij",
"TaskCleanCacheDescription": "Izbriše predpomnjene datoteke, ki niso več potrebne.",
"TaskCleanCache": "Počisti mapo predpomnilnika",
"TasksChannelsCategory": "Spletni kanali",
"TasksApplicationCategory": "Aplikacija",
"TasksLibraryCategory": "Knjižnica",
"TasksMaintenanceCategory": "Vzdrževanje"
}

@ -9,7 +9,7 @@
"Channels": "Kanaler",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Samlingar",
"DeviceOfflineWithName": "{0} har tappat anslutningen",
"DeviceOfflineWithName": "{0} har kopplat från",
"DeviceOnlineWithName": "{0} är ansluten",
"FailedLoginAttemptWithUserName": "Misslyckat inloggningsförsök från {0}",
"Favorites": "Favoriter",
@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Ljuduppspelning har påbörjats",
"NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppades",
"NotificationOptionCameraImageUploaded": "Kamerabild har laddats upp",
"NotificationOptionInstallationFailed": "Fel vid installation",
"NotificationOptionInstallationFailed": "Installationen misslyckades",
"NotificationOptionNewLibraryContent": "Nytt innehåll har lagts till",
"NotificationOptionPluginError": "Fel uppstod med tillägget",
"NotificationOptionPluginInstalled": "Tillägg har installerats",
@ -113,5 +113,6 @@
"TasksChannelsCategory": "Internetkanaler",
"TasksApplicationCategory": "Applikation",
"TasksLibraryCategory": "Bibliotek",
"TasksMaintenanceCategory": "Underhåll"
"TasksMaintenanceCategory": "Underhåll",
"TaskRefreshPeople": "Uppdatera Personer"
}

@ -0,0 +1,36 @@
{
"MusicVideos": "Музичні відео",
"Music": "Музика",
"Movies": "Фільми",
"MessageApplicationUpdatedTo": "Jellyfin Server був оновлений до версії {0}",
"MessageApplicationUpdated": "Jellyfin Server був оновлений",
"Latest": "Останні",
"LabelIpAddressValue": "IP-адреси: {0}",
"ItemRemovedWithName": "{0} видалено з бібліотеки",
"ItemAddedWithName": "{0} додано до бібліотеки",
"HeaderNextUp": "Наступний",
"HeaderLiveTV": "Ефірне ТБ",
"HeaderFavoriteSongs": "Улюблені пісні",
"HeaderFavoriteShows": "Улюблені шоу",
"HeaderFavoriteEpisodes": "Улюблені серії",
"HeaderFavoriteArtists": "Улюблені виконавці",
"HeaderFavoriteAlbums": "Улюблені альбоми",
"HeaderContinueWatching": "Продовжити перегляд",
"HeaderCameraUploads": "Завантажено з камери",
"HeaderAlbumArtists": "Виконавці альбомів",
"Genres": "Жанри",
"Folders": "Директорії",
"Favorites": "Улюблені",
"DeviceOnlineWithName": "{0} під'єднано",
"DeviceOfflineWithName": "{0} від'єднано",
"Collections": "Колекції",
"ChapterNameValue": "Глава {0}",
"Channels": "Канали",
"CameraImageUploadedFrom": "Нова фотографія завантажена з {0}",
"Books": "Книги",
"AuthenticationSucceededWithUserName": "{0} успішно авторизовані",
"Artists": "Виконавці",
"Application": "Додаток",
"AppDeviceValues": "Додаток: {0}, Пристрій: {1}",
"Albums": "Альбоми"
}

@ -1,6 +1,6 @@
{
"Albums": "專輯",
"AppDeviceValues": "軟: {0}, 設備: {1}",
"AppDeviceValues": "軟: {0}, 設備: {1}",
"Application": "應用程式",
"Artists": "藝人",
"AuthenticationSucceededWithUserName": "{0} 授權成功",
@ -92,5 +92,8 @@
"UserStoppedPlayingItemWithValues": "{0} 已在 {2} 上停止播放 {1}",
"ValueHasBeenAddedToLibrary": "{0} 已添加到你的媒體庫",
"ValueSpecialEpisodeName": "特典 - {0}",
"VersionNumber": "版本{0}"
"VersionNumber": "版本{0}",
"TaskDownloadMissingSubtitles": "下載遺失的字幕",
"TaskUpdatePlugins": "更新插件",
"TasksApplicationCategory": "應用程式"
}

@ -63,6 +63,9 @@ namespace Emby.Server.Implementations.SocketSharp
if (!IPAddress.TryParse(GetHeader(CustomHeaderNames.XRealIP), out ip))
{
ip = Request.HttpContext.Connection.RemoteIpAddress;
// Default to the loopback address if no RemoteIpAddress is specified (i.e. during integration tests)
ip ??= IPAddress.Loopback;
}
}
@ -90,7 +93,10 @@ namespace Emby.Server.Implementations.SocketSharp
public IQueryCollection QueryString => Request.Query;
public bool IsLocal => Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress);
public bool IsLocal =>
(Request.HttpContext.Connection.LocalIpAddress == null
&& Request.HttpContext.Connection.RemoteIpAddress == null)
|| Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress);
public string HttpMethod => Request.Method;

@ -1,15 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Artwork")]
public partial class Artwork
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Book")]
public partial class Book : LibraryItem
{
partial void Init();
@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected Book() : base()
protected Book()
{
BookMetadata = new HashSet<BookMetadata>();
Releases = new HashSet<Release>();

@ -1,11 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
@ -16,7 +11,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected BookMetadata() : base()
protected BookMetadata()
{
Publishers = new HashSet<Company>();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Chapter")]
public partial class Chapter
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Collection")]
public partial class Collection
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("CollectionItem")]
public partial class CollectionItem
{
partial void Init();

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Company")]
public partial class Company
{
partial void Init();

@ -1,15 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("CompanyMetadata")]
public partial class CompanyMetadata : Metadata
{
partial void Init();
@ -17,7 +10,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected CompanyMetadata() : base()
protected CompanyMetadata()
{
Init();
}

@ -1,11 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
@ -16,7 +11,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected CustomItem() : base()
protected CustomItem()
{
CustomItemMetadata = new HashSet<CustomItemMetadata>();
Releases = new HashSet<Release>();

@ -1,15 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("CustomItemMetadata")]
public partial class CustomItemMetadata : Metadata
{
partial void Init();
@ -17,7 +9,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected CustomItemMetadata() : base()
protected CustomItemMetadata()
{
Init();
}

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Episode")]
public partial class Episode : LibraryItem
{
partial void Init();
@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected Episode() : base()
protected Episode()
{
// NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem.
// One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.

@ -1,15 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("EpisodeMetadata")]
public partial class EpisodeMetadata : Metadata
{
partial void Init();
@ -17,7 +10,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected EpisodeMetadata() : base()
protected EpisodeMetadata()
{
Init();
}

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Genre")]
public partial class Genre
{
partial void Init();

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Group")]
public partial class Group
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Library")]
public partial class Library
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("LibraryItem")]
public abstract partial class LibraryItem
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("LibraryRoot")]
public partial class LibraryRoot
{
partial void Init();

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("MediaFile")]
public partial class MediaFile
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("MediaFileStream")]
public partial class MediaFileStream
{
partial void Init();

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Metadata")]
public abstract partial class Metadata
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("MetadataProvider")]
public partial class MetadataProvider
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("MetadataProviderId")]
public partial class MetadataProviderId
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Movie")]
public partial class Movie : LibraryItem
{
partial void Init();
@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected Movie() : base()
protected Movie()
{
Releases = new HashSet<Release>();
MovieMetadata = new HashSet<MovieMetadata>();

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("MovieMetadata")]
public partial class MovieMetadata : Metadata
{
partial void Init();
@ -17,7 +12,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected MovieMetadata() : base()
protected MovieMetadata()
{
Studios = new HashSet<Company>();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("MusicAlbum")]
public partial class MusicAlbum : LibraryItem
{
partial void Init();
@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected MusicAlbum() : base()
protected MusicAlbum()
{
MusicAlbumMetadata = new HashSet<MusicAlbumMetadata>();
Tracks = new HashSet<Track>();

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("MusicAlbumMetadata")]
public partial class MusicAlbumMetadata : Metadata
{
partial void Init();
@ -17,7 +12,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected MusicAlbumMetadata() : base()
protected MusicAlbumMetadata()
{
Labels = new HashSet<Company>();

@ -1,15 +1,11 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Permission")]
public partial class Permission
{
partial void Init();

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Person")]
public partial class Person
{
partial void Init();

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("PersonRole")]
public partial class PersonRole
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Photo")]
public partial class Photo : LibraryItem
{
partial void Init();
@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected Photo() : base()
protected Photo()
{
PhotoMetadata = new HashSet<PhotoMetadata>();
Releases = new HashSet<Release>();

@ -1,15 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("PhotoMetadata")]
public partial class PhotoMetadata : Metadata
{
partial void Init();
@ -17,7 +10,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected PhotoMetadata() : base()
protected PhotoMetadata()
{
Init();
}

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Preference")]
public partial class Preference
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("ProviderMapping")]
public partial class ProviderMapping
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Rating")]
public partial class Rating
{
partial void Init();

@ -1,18 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
/// <summary>
/// This is the entity to store review ratings, not age ratings
/// </summary>
[Table("RatingSource")]
public partial class RatingSource
{
partial void Init();

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Release")]
public partial class Release
{
partial void Init();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Season")]
public partial class Season : LibraryItem
{
partial void Init();
@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected Season() : base()
protected Season()
{
// NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem.
// One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("SeasonMetadata")]
public partial class SeasonMetadata : Metadata
{
partial void Init();
@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected SeasonMetadata() : base()
protected SeasonMetadata()
{
Init();
}

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Series")]
public partial class Series : LibraryItem
{
partial void Init();
@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected Series() : base()
protected Series()
{
SeriesMetadata = new HashSet<SeriesMetadata>();
Seasons = new HashSet<Season>();

@ -1,15 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("SeriesMetadata")]
public partial class SeriesMetadata : Metadata
{
partial void Init();
@ -17,7 +12,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected SeriesMetadata() : base()
protected SeriesMetadata()
{
Networks = new HashSet<Company>();

@ -1,15 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("Track")]
public partial class Track : LibraryItem
{
partial void Init();
@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected Track() : base()
protected Track()
{
// NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem.
// One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.

@ -1,15 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Jellyfin.Data.Entities
{
[Table("TrackMetadata")]
public partial class TrackMetadata : Metadata
{
partial void Init();
@ -17,7 +10,7 @@ namespace Jellyfin.Data.Entities
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
protected TrackMetadata() : base()
protected TrackMetadata()
{
Init();
}

@ -8,14 +8,15 @@ using Jellyfin.Data.Enums;
namespace Jellyfin.Data.Entities
{
[Table("User")]
public class User
public partial class User
{
/// <summary>
/// The values being delimited here are Guids, so commas work as they do not appear in Guids.
/// </summary>
private const char Delimiter = ',';
partial void Init();
/// <summary>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </summary>
@ -26,6 +27,8 @@ namespace Jellyfin.Data.Entities
ProviderMappings = new HashSet<ProviderMapping>();
Preferences = new HashSet<Preference>();
AccessSchedules = new HashSet<AccessSchedule>();
Init();
}
/// <summary>
@ -77,6 +80,8 @@ namespace Jellyfin.Data.Entities
RememberSubtitleSelections = true;
EnableNextEpisodeAutoPlay = true;
EnableAutoLogin = false;
Init();
}
/// <summary>

@ -1,8 +1,6 @@
using System;
namespace Jellyfin.Data.Enums
{
public enum ArtKind : Int32
public enum ArtKind
{
Other,
Poster,

@ -1,8 +1,6 @@
using System;
namespace Jellyfin.Data.Enums
{
public enum MediaFileKind : Int32
public enum MediaFileKind
{
Main,
Sidecar,

@ -1,8 +1,6 @@
using System;
namespace Jellyfin.Data.Enums
{
public enum PersonRoleType : Int32
public enum PersonRoleType
{
Other,
Director,

@ -1,5 +1,3 @@
using System;
namespace Jellyfin.Data.Enums
{
public enum PreferenceKind

@ -1,8 +1,6 @@
using System;
namespace Jellyfin.Data.Enums
{
public enum Weekday : Int32
public enum Weekday
{
Sunday,
Monday,

@ -1,4 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
namespace Jellyfin.Data
{

@ -21,8 +21,6 @@ namespace Jellyfin.Server.Migrations
/// <summary>
/// Execute the migration routine.
/// </summary>
/// <param name="host">Host that hosts current version.</param>
/// <param name="logger">Host logger.</param>
public void Perform(CoreAppHost host, ILogger logger);
public void Perform();
}
}

@ -1,6 +1,7 @@
using System;
using System.Linq;
using MediaBrowser.Common.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Server.Migrations
@ -13,11 +14,10 @@ namespace Jellyfin.Server.Migrations
/// <summary>
/// The list of known migrations, in order of applicability.
/// </summary>
internal static readonly IMigrationRoutine[] Migrations =
private static readonly Type[] _migrationTypes =
{
new Routines.DisableTranscodingThrottling(),
new Routines.CreateUserLoggingConfigFile(),
new Routines.MigrateActivityLogDb()
typeof(Routines.DisableTranscodingThrottling),
typeof(Routines.CreateUserLoggingConfigFile)
};
/// <summary>
@ -28,6 +28,10 @@ namespace Jellyfin.Server.Migrations
public static void Run(CoreAppHost host, ILoggerFactory loggerFactory)
{
var logger = loggerFactory.CreateLogger<MigrationRunner>();
var migrations = _migrationTypes
.Select(m => ActivatorUtilities.CreateInstance(host.ServiceProvider, m))
.OfType<IMigrationRoutine>()
.ToArray();
var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration<MigrationOptions>(MigrationsListStore.StoreKey);
if (!host.ServerConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Count == 0)
@ -35,16 +39,16 @@ namespace Jellyfin.Server.Migrations
// If startup wizard is not finished, this is a fresh install.
// Don't run any migrations, just mark all of them as applied.
logger.LogInformation("Marking all known migrations as applied because this is a fresh install");
migrationOptions.Applied.AddRange(Migrations.Select(m => (m.Id, m.Name)));
migrationOptions.Applied.AddRange(migrations.Select(m => (m.Id, m.Name)));
host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions);
return;
}
var appliedMigrationIds = migrationOptions.Applied.Select(m => m.Id).ToHashSet();
for (var i = 0; i < Migrations.Length; i++)
for (var i = 0; i < migrations.Length; i++)
{
var migrationRoutine = Migrations[i];
var migrationRoutine = migrations[i];
if (appliedMigrationIds.Contains(migrationRoutine.Id))
{
logger.LogDebug("Skipping migration '{Name}' since it is already applied", migrationRoutine.Name);
@ -55,7 +59,7 @@ namespace Jellyfin.Server.Migrations
try
{
migrationRoutine.Perform(host, logger);
migrationRoutine.Perform();
}
catch (Exception ex)
{

@ -36,6 +36,13 @@ namespace Jellyfin.Server.Migrations.Routines
@"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}:{Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}",
};
private readonly IApplicationPaths _appPaths;
public CreateUserLoggingConfigFile(IApplicationPaths appPaths)
{
_appPaths = appPaths;
}
/// <inheritdoc/>
public Guid Id => Guid.Parse("{EF103419-8451-40D8-9F34-D1A8E93A1679}");
@ -43,9 +50,9 @@ namespace Jellyfin.Server.Migrations.Routines
public string Name => "CreateLoggingConfigHeirarchy";
/// <inheritdoc/>
public void Perform(CoreAppHost host, ILogger logger)
public void Perform()
{
var logDirectory = host.Resolve<IApplicationPaths>().ConfigurationDirectoryPath;
var logDirectory = _appPaths.ConfigurationDirectoryPath;
var existingConfigPath = Path.Combine(logDirectory, "logging.json");
// If the existing logging.json config file is unmodified, then 'reset' it by moving it to 'logging.old.json'

@ -10,6 +10,15 @@ namespace Jellyfin.Server.Migrations.Routines
/// </summary>
internal class DisableTranscodingThrottling : IMigrationRoutine
{
private readonly ILogger _logger;
private readonly IConfigurationManager _configManager;
public DisableTranscodingThrottling(ILogger<DisableTranscodingThrottling> logger, IConfigurationManager configManager)
{
_logger = logger;
_configManager = configManager;
}
/// <inheritdoc/>
public Guid Id => Guid.Parse("{4124C2CD-E939-4FFB-9BE9-9B311C413638}");
@ -17,16 +26,16 @@ namespace Jellyfin.Server.Migrations.Routines
public string Name => "DisableTranscodingThrottling";
/// <inheritdoc/>
public void Perform(CoreAppHost host, ILogger logger)
public void Perform()
{
// Set EnableThrottling to false since it wasn't used before and may introduce issues
var encoding = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration<EncodingOptions>("encoding");
var encoding = _configManager.GetConfiguration<EncodingOptions>("encoding");
if (encoding.EnableThrottling)
{
logger.LogInformation("Disabling transcoding throttling during migration");
_logger.LogInformation("Disabling transcoding throttling during migration");
encoding.EnableThrottling = false;
host.ServerConfigurationManager.SaveConfiguration("encoding", encoding);
_configManager.SaveConfiguration("encoding", encoding);
}
}
}

@ -161,23 +161,7 @@ namespace Jellyfin.Server
ApplicationHost.LogEnvironmentInfo(_logger, appPaths);
// Make sure we have all the code pages we can get
// Ref: https://docs.microsoft.com/en-us/dotnet/api/system.text.codepagesencodingprovider.instance?view=netcore-3.0#remarks
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// Increase the max http request limit
// The default connection limit is 10 for ASP.NET hosted applications and 2 for all others.
ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit);
// Disable the "Expect: 100-Continue" header by default
// http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
ServicePointManager.Expect100Continue = false;
Batteries_V2.Init();
if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK)
{
_logger.LogWarning("Failed to enable shared cache for SQLite");
}
PerformStaticInitialization();
var appHost = new CoreAppHost(
appPaths,
@ -205,7 +189,7 @@ namespace Jellyfin.Server
ServiceCollection serviceCollection = new ServiceCollection();
appHost.Init(serviceCollection);
var webHost = CreateWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build();
var webHost = new WebHostBuilder().ConfigureWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build();
// Re-use the web host service provider in the app host since ASP.NET doesn't allow a custom service collection.
appHost.ServiceProvider = webHost.Services;
@ -250,14 +234,49 @@ namespace Jellyfin.Server
}
}
private static IWebHostBuilder CreateWebHostBuilder(
/// <summary>
/// Call static initialization methods for the application.
/// </summary>
public static void PerformStaticInitialization()
{
// Make sure we have all the code pages we can get
// Ref: https://docs.microsoft.com/en-us/dotnet/api/system.text.codepagesencodingprovider.instance?view=netcore-3.0#remarks
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// Increase the max http request limit
// The default connection limit is 10 for ASP.NET hosted applications and 2 for all others.
ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit);
// Disable the "Expect: 100-Continue" header by default
// http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
ServicePointManager.Expect100Continue = false;
Batteries_V2.Init();
if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK)
{
_logger.LogWarning("Failed to enable shared cache for SQLite");
}
}
/// <summary>
/// Configure the web host builder.
/// </summary>
/// <param name="builder">The builder to configure.</param>
/// <param name="appHost">The application host.</param>
/// <param name="serviceCollection">The application service collection.</param>
/// <param name="commandLineOpts">The command line options passed to the application.</param>
/// <param name="startupConfig">The application configuration.</param>
/// <param name="appPaths">The application paths.</param>
/// <returns>The configured web host builder.</returns>
public static IWebHostBuilder ConfigureWebHostBuilder(
this IWebHostBuilder builder,
ApplicationHost appHost,
IServiceCollection serviceCollection,
StartupOptions commandLineOpts,
IConfiguration startupConfig,
IApplicationPaths appPaths)
{
return new WebHostBuilder()
return builder
.UseKestrel((builderContext, options) =>
{
var addresses = appHost.ServerConfigurationManager
@ -278,7 +297,6 @@ namespace Jellyfin.Server
{
_logger.LogInformation("Kestrel listening on {IpAddress}", address);
options.Listen(address, appHost.HttpPort);
if (appHost.EnableHttps && appHost.Certificate != null)
{
options.Listen(address, appHost.HttpsPort, listenOptions =>
@ -289,11 +307,18 @@ namespace Jellyfin.Server
}
else if (builderContext.HostingEnvironment.IsDevelopment())
{
options.Listen(address, appHost.HttpsPort, listenOptions =>
try
{
listenOptions.UseHttps();
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
options.Listen(address, appHost.HttpsPort, listenOptions =>
{
listenOptions.UseHttps();
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "Failed to listen to HTTPS using the ASP.NET Core HTTPS development certificate. Please ensure it has been installed and set as trusted.");
}
}
}
}
@ -312,11 +337,18 @@ namespace Jellyfin.Server
}
else if (builderContext.HostingEnvironment.IsDevelopment())
{
options.ListenAnyIP(appHost.HttpsPort, listenOptions =>
try
{
listenOptions.UseHttps();
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
options.ListenAnyIP(appHost.HttpsPort, listenOptions =>
{
listenOptions.UseHttps();
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "Failed to listen to HTTPS using the ASP.NET Core HTTPS development certificate. Please ensure it has been installed and set as trusted.");
}
}
}
})
@ -496,7 +528,9 @@ namespace Jellyfin.Server
/// Initialize the logging configuration file using the bundled resource file as a default if it doesn't exist
/// already.
/// </summary>
private static async Task InitLoggingConfigFile(IApplicationPaths appPaths)
/// <param name="appPaths">The application paths.</param>
/// <returns>A task representing the creation of the configuration file, or a completed task if the file already exists.</returns>
public static async Task InitLoggingConfigFile(IApplicationPaths appPaths)
{
// Do nothing if the config file already exists
string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, LoggingConfigFileDefault);
@ -516,7 +550,13 @@ namespace Jellyfin.Server
await resource.CopyToAsync(dst).ConfigureAwait(false);
}
private static IConfiguration CreateAppConfiguration(StartupOptions commandLineOpts, IApplicationPaths appPaths)
/// <summary>
/// Create the application configuration.
/// </summary>
/// <param name="commandLineOpts">The command line options passed to the program.</param>
/// <param name="appPaths">The application paths.</param>
/// <returns>The application configuration.</returns>
public static IConfiguration CreateAppConfiguration(StartupOptions commandLineOpts, IApplicationPaths appPaths)
{
return new ConfigurationBuilder()
.ConfigureAppConfiguration(commandLineOpts, appPaths)

@ -265,17 +265,20 @@ namespace MediaBrowser.Api.Images
{
Url = url,
BufferContent = false
}).ConfigureAwait(false);
var ext = result.ContentType.Split('/').Last();
var ext = result.ContentType.Split('/')[^1];
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
using (var stream = result.Content)
var stream = result.Content;
await using (stream.ConfigureAwait(false))
{
using var filestream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
await stream.CopyToAsync(filestream).ConfigureAwait(false);
var filestream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
await using (filestream.ConfigureAwait(false))
{
await stream.CopyToAsync(filestream).ConfigureAwait(false);
}
}
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));

@ -299,22 +299,26 @@ namespace MediaBrowser.Api
{
var result = await _providerManager.GetSearchImage(providerName, url, CancellationToken.None).ConfigureAwait(false);
var ext = result.ContentType.Split('/').Last();
var ext = result.ContentType.Split('/')[^1];
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
using (var stream = result.Content)
var stream = result.Content;
await using (stream.ConfigureAwait(false))
{
using var fileStream = new FileStream(
var fileStream = new FileStream(
fullCachePath,
FileMode.Create,
FileAccess.Write,
FileShare.Read,
IODefaults.FileStreamBufferSize,
true);
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
await using (fileStream.ConfigureAwait(false))
{
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));

@ -209,24 +209,28 @@ namespace MediaBrowser.Api.Playback.Hls
try
{
// Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
using var fileStream = GetPlaylistFileStream(playlist);
using var reader = new StreamReader(fileStream);
var count = 0;
while (!reader.EndOfStream)
var fileStream = GetPlaylistFileStream(playlist);
await using (fileStream.ConfigureAwait(false))
{
var line = reader.ReadLine();
using var reader = new StreamReader(fileStream);
var count = 0;
if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
while (!reader.EndOfStream)
{
count++;
if (count >= segmentCount)
var line = await reader.ReadLineAsync().ConfigureAwait(false);
if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
{
Logger.LogDebug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
return;
count++;
if (count >= segmentCount)
{
Logger.LogDebug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
return;
}
}
}
}
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
catch (IOException)

@ -720,22 +720,203 @@ namespace MediaBrowser.Api.Playback.Hls
//return state.VideoRequest.VideoBitRate.HasValue;
}
/// <summary>
/// Get the H.26X level of the output video stream.
/// </summary>
/// <param name="state">StreamState of the current stream.</param>
/// <returns>H.26X level of the output video stream.</returns>
private int? GetOutputVideoCodecLevel(StreamState state)
{
string levelString;
if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)
&& state.VideoStream.Level.HasValue)
{
levelString = state.VideoStream?.Level.ToString();
}
else
{
levelString = state.GetRequestedLevel(state.ActualOutputVideoCodec);
}
if (int.TryParse(levelString, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedLevel))
{
return parsedLevel;
}
return null;
}
/// <summary>
/// Gets a formatted string of the output audio codec, for use in the CODECS field.
/// </summary>
/// <seealso cref="AppendPlaylistCodecsField(StringBuilder, StreamState)"/>
/// <seealso cref="GetPlaylistVideoCodecs(StreamState, string, int)"/>
/// <param name="state">StreamState of the current stream.</param>
/// <returns>Formatted audio codec string.</returns>
private string GetPlaylistAudioCodecs(StreamState state)
{
if (string.Equals(state.ActualOutputAudioCodec, "aac", StringComparison.OrdinalIgnoreCase))
{
string profile = state.GetRequestedProfiles("aac").FirstOrDefault();
return HlsCodecStringFactory.GetAACString(profile);
}
else if (string.Equals(state.ActualOutputAudioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
{
return HlsCodecStringFactory.GetMP3String();
}
else if (string.Equals(state.ActualOutputAudioCodec, "ac3", StringComparison.OrdinalIgnoreCase))
{
return HlsCodecStringFactory.GetAC3String();
}
else if (string.Equals(state.ActualOutputAudioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
{
return HlsCodecStringFactory.GetEAC3String();
}
return string.Empty;
}
/// <summary>
/// Gets a formatted string of the output video codec, for use in the CODECS field.
/// </summary>
/// <seealso cref="AppendPlaylistCodecsField(StringBuilder, StreamState)"/>
/// <seealso cref="GetPlaylistAudioCodecs(StreamState)"/>
/// <param name="state">StreamState of the current stream.</param>
/// <returns>Formatted video codec string.</returns>
private string GetPlaylistVideoCodecs(StreamState state, string codec, int level)
{
if (level == 0)
{
// This is 0 when there's no requested H.26X level in the device profile
// and the source is not encoded in H.26X
Logger.LogError("Got invalid H.26X level when building CODECS field for HLS master playlist");
return string.Empty;
}
if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
{
string profile = state.GetRequestedProfiles("h264").FirstOrDefault();
return HlsCodecStringFactory.GetH264String(profile, level);
}
else if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
{
string profile = state.GetRequestedProfiles("h265").FirstOrDefault();
return HlsCodecStringFactory.GetH265String(profile, level);
}
return string.Empty;
}
/// <summary>
/// Appends a CODECS field containing formatted strings of
/// the active streams output video and audio codecs.
/// </summary>
/// <seealso cref="AppendPlaylist(StringBuilder, StreamState, string, int, string)"/>
/// <seealso cref="GetPlaylistVideoCodecs(StreamState, string, int)"/>
/// <seealso cref="GetPlaylistAudioCodecs(StreamState)"/>
/// <param name="builder">StringBuilder to append the field to.</param>
/// <param name="state">StreamState of the current stream.</param>
private void AppendPlaylistCodecsField(StringBuilder builder, StreamState state)
{
// Video
string videoCodecs = string.Empty;
int? videoCodecLevel = GetOutputVideoCodecLevel(state);
if (!string.IsNullOrEmpty(state.ActualOutputVideoCodec) && videoCodecLevel.HasValue)
{
videoCodecs = GetPlaylistVideoCodecs(state, state.ActualOutputVideoCodec, videoCodecLevel.Value);
}
// Audio
string audioCodecs = string.Empty;
if (!string.IsNullOrEmpty(state.ActualOutputAudioCodec))
{
audioCodecs = GetPlaylistAudioCodecs(state);
}
StringBuilder codecs = new StringBuilder();
codecs.Append(videoCodecs)
.Append(',')
.Append(audioCodecs);
if (codecs.Length > 1)
{
builder.Append(",CODECS=\"")
.Append(codecs)
.Append('"');
}
}
/// <summary>
/// Appends a FRAME-RATE field containing the framerate of the output stream.
/// </summary>
/// <seealso cref="AppendPlaylist(StringBuilder, StreamState, string, int, string)"/>
/// <param name="builder">StringBuilder to append the field to.</param>
/// <param name="state">StreamState of the current stream.</param>
private void AppendPlaylistFramerateField(StringBuilder builder, StreamState state)
{
double? framerate = null;
if (state.TargetFramerate.HasValue)
{
framerate = Math.Round(state.TargetFramerate.GetValueOrDefault(), 3);
}
else if (state.VideoStream.RealFrameRate.HasValue)
{
framerate = Math.Round(state.VideoStream.RealFrameRate.GetValueOrDefault(), 3);
}
if (framerate.HasValue)
{
builder.Append(",FRAME-RATE=\"")
.Append(framerate.Value)
.Append('"');
}
}
/// <summary>
/// Appends a RESOLUTION field containing the resolution of the output stream.
/// </summary>
/// <seealso cref="AppendPlaylist(StringBuilder, StreamState, string, int, string)"/>
/// <param name="builder">StringBuilder to append the field to.</param>
/// <param name="state">StreamState of the current stream.</param>
private void AppendPlaylistResolutionField(StringBuilder builder, StreamState state)
{
if (state.OutputWidth.HasValue && state.OutputHeight.HasValue)
{
builder.Append(",RESOLUTION=\"")
.Append(state.OutputWidth.GetValueOrDefault())
.Append('x')
.Append(state.OutputHeight.GetValueOrDefault())
.Append('"');
}
}
private void AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string subtitleGroup)
{
var header = "#EXT-X-STREAM-INF:BANDWIDTH=" + bitrate.ToString(CultureInfo.InvariantCulture) + ",AVERAGE-BANDWIDTH=" + bitrate.ToString(CultureInfo.InvariantCulture);
builder.Append("#EXT-X-STREAM-INF:BANDWIDTH=")
.Append(bitrate.ToString(CultureInfo.InvariantCulture))
.Append(",AVERAGE-BANDWIDTH=")
.Append(bitrate.ToString(CultureInfo.InvariantCulture));
// tvos wants resolution, codecs, framerate
//if (state.TargetFramerate.HasValue)
//{
// header += string.Format(",FRAME-RATE=\"{0}\"", state.TargetFramerate.Value.ToString(CultureInfo.InvariantCulture));
//}
AppendPlaylistCodecsField(builder, state);
AppendPlaylistResolutionField(builder, state);
AppendPlaylistFramerateField(builder, state);
if (!string.IsNullOrWhiteSpace(subtitleGroup))
{
header += string.Format(",SUBTITLES=\"{0}\"", subtitleGroup);
builder.Append(",SUBTITLES=\"")
.Append(subtitleGroup)
.Append('"');
}
builder.AppendLine(header);
builder.Append(Environment.NewLine);
builder.AppendLine(url);
}

@ -0,0 +1,126 @@
using System;
using System.Text;
namespace MediaBrowser.Api.Playback
{
/// <summary>
/// Get various codec strings for use in HLS playlists.
/// </summary>
static class HlsCodecStringFactory
{
/// <summary>
/// Gets a MP3 codec string.
/// </summary>
/// <returns>MP3 codec string.</returns>
public static string GetMP3String()
{
return "mp4a.40.34";
}
/// <summary>
/// Gets an AAC codec string.
/// </summary>
/// <param name="profile">AAC profile.</param>
/// <returns>AAC codec string.</returns>
public static string GetAACString(string profile)
{
StringBuilder result = new StringBuilder("mp4a", 9);
if (string.Equals(profile, "HE", StringComparison.OrdinalIgnoreCase))
{
result.Append(".40.5");
}
else
{
// Default to LC if profile is invalid
result.Append(".40.2");
}
return result.ToString();
}
/// <summary>
/// Gets a H.264 codec string.
/// </summary>
/// <param name="profile">H.264 profile.</param>
/// <param name="level">H.264 level.</param>
/// <returns>H.264 string.</returns>
public static string GetH264String(string profile, int level)
{
StringBuilder result = new StringBuilder("avc1", 11);
if (string.Equals(profile, "high", StringComparison.OrdinalIgnoreCase))
{
result.Append(".6400");
}
else if (string.Equals(profile, "main", StringComparison.OrdinalIgnoreCase))
{
result.Append(".4D40");
}
else if (string.Equals(profile, "baseline", StringComparison.OrdinalIgnoreCase))
{
result.Append(".42E0");
}
else
{
// Default to constrained baseline if profile is invalid
result.Append(".4240");
}
string levelHex = level.ToString("X2");
result.Append(levelHex);
return result.ToString();
}
/// <summary>
/// Gets a H.265 codec string.
/// </summary>
/// <param name="profile">H.265 profile.</param>
/// <param name="level">H.265 level.</param>
/// <returns>H.265 string.</returns>
public static string GetH265String(string profile, int level)
{
// The h265 syntax is a bit of a mystery at the time this comment was written.
// This is what I've found through various sources:
// FORMAT: [codecTag].[profile].[constraint?].L[level * 30].[UNKNOWN]
StringBuilder result = new StringBuilder("hev1", 16);
if (string.Equals(profile, "main10", StringComparison.OrdinalIgnoreCase))
{
result.Append(".2.6");
}
else
{
// Default to main if profile is invalid
result.Append(".1.6");
}
result.Append(".L")
.Append(level * 3)
.Append(".B0");
return result.ToString();
}
/// <summary>
/// Gets an AC-3 codec string.
/// </summary>
/// <returns>AC-3 codec string.</returns>
public static string GetAC3String()
{
return "mp4a.a5";
}
/// <summary>
/// Gets an E-AC-3 codec string.
/// </summary>
/// <returns>E-AC-3 codec string.</returns>
public static string GetEAC3String()
{
return "mp4a.a6";
}
}
}

@ -36,7 +36,7 @@ namespace MediaBrowser.Api
}
[Route("/Users/Public", "GET", Summary = "Gets a list of publicly visible users for display on a login screen.")]
public class GetPublicUsers : IReturn<UserDto[]>
public class GetPublicUsers : IReturn<PublicUserDto[]>
{
}
@ -267,22 +267,38 @@ namespace MediaBrowser.Api
_authContext = authContext;
}
/// <summary>
/// Gets the public available Users information
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetPublicUsers request)
{
// If the startup wizard hasn't been completed then just return all users
if (!ServerConfigurationManager.Configuration.IsStartupWizardCompleted)
var result = _userManager
.Users
.Where(item => !item.Policy.IsDisabled);
if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted)
{
return Get(new GetUsers
var deviceId = _authContext.GetAuthorizationInfo(Request).DeviceId;
result = result.Where(item => !item.Policy.IsHidden);
if (!string.IsNullOrWhiteSpace(deviceId))
{
IsDisabled = false
});
result = result.Where(i => _deviceManager.CanAccessDevice(i, deviceId));
}
if (!_networkManager.IsInLocalNetwork(Request.RemoteIp))
{
result = result.Where(i => i.Policy.EnableRemoteAccess);
}
}
return Get(new GetUsers
{
IsHidden = false,
IsDisabled = false
}, true, true);
return ToOptimizedResult(result
.OrderBy(u => u.Name)
.Select(i => _userManager.GetPublicUserDto(i, Request.RemoteIp))
.ToArray()
);
}
/// <summary>

@ -141,6 +141,14 @@ namespace MediaBrowser.Controller.Library
/// <returns>UserDto.</returns>
UserDto GetUserDto(User user, string remoteEndPoint = null);
/// <summary>
/// Gets the user public dto.
/// </summary>
/// <param name="user">Ther user.</param>\
/// <param name="remoteEndPoint">The remote end point.</param>
/// <returns>A public UserDto, aka a UserDto stripped of personal data.</returns>
PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null);
/// <summary>
/// Authenticates the user.
/// </summary>

@ -1,8 +1,8 @@
#pragma warning disable CS1591
using System;
using System.Linq;
using System.Xml.Serialization;
using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Dlna
{
@ -57,7 +57,7 @@ namespace MediaBrowser.Model.Dlna
foreach (var val in codec)
{
if (ListHelper.ContainsIgnoreCase(codecs, val))
if (codecs.Contains(val, StringComparer.OrdinalIgnoreCase))
{
return true;
}

@ -1,8 +1,8 @@
#pragma warning disable CS1591
using System;
using System.Linq;
using System.Globalization;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Model.Dlna
@ -167,9 +167,7 @@ namespace MediaBrowser.Model.Dlna
switch (condition.Condition)
{
case ProfileConditionType.EqualsAny:
{
return ListHelper.ContainsIgnoreCase(expected.Split('|'), currentValue);
}
return expected.Split('|').Contains(currentValue, StringComparer.OrdinalIgnoreCase);
case ProfileConditionType.Equals:
return string.Equals(currentValue, expected, StringComparison.OrdinalIgnoreCase);
case ProfileConditionType.NotEquals:

@ -1,8 +1,8 @@
#pragma warning disable CS1591
using System;
using System.Linq;
using System.Xml.Serialization;
using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Dlna
{
@ -45,7 +45,7 @@ namespace MediaBrowser.Model.Dlna
public static bool ContainsContainer(string profileContainers, string inputContainer)
{
var isNegativeList = false;
if (profileContainers != null && profileContainers.StartsWith("-"))
if (profileContainers != null && profileContainers.StartsWith("-", StringComparison.Ordinal))
{
isNegativeList = true;
profileContainers = profileContainers.Substring(1);
@ -72,7 +72,7 @@ namespace MediaBrowser.Model.Dlna
foreach (var container in allInputContainers)
{
if (ListHelper.ContainsIgnoreCase(profileContainers, container))
if (profileContainers.Contains(container, StringComparer.OrdinalIgnoreCase))
{
return false;
}
@ -86,7 +86,7 @@ namespace MediaBrowser.Model.Dlna
foreach (var container in allInputContainers)
{
if (ListHelper.ContainsIgnoreCase(profileContainers, container))
if (profileContainers.Contains(container, StringComparer.OrdinalIgnoreCase))
{
return true;
}

@ -1,8 +1,8 @@
#pragma warning disable CS1591
using System;
using System.Linq;
using System.Xml.Serialization;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Model.Dlna
@ -93,14 +93,14 @@ namespace MediaBrowser.Model.Dlna
public DeviceProfile()
{
DirectPlayProfiles = new DirectPlayProfile[] { };
TranscodingProfiles = new TranscodingProfile[] { };
ResponseProfiles = new ResponseProfile[] { };
CodecProfiles = new CodecProfile[] { };
ContainerProfiles = new ContainerProfile[] { };
DirectPlayProfiles = Array.Empty<DirectPlayProfile>();
TranscodingProfiles = Array.Empty<TranscodingProfile>();
ResponseProfiles = Array.Empty<ResponseProfile>();
CodecProfiles = Array.Empty<CodecProfile>();
ContainerProfiles = Array.Empty<ContainerProfile>();
SubtitleProfiles = Array.Empty<SubtitleProfile>();
XmlRootAttributes = new XmlAttribute[] { };
XmlRootAttributes = Array.Empty<XmlAttribute>();
SupportedMediaTypes = "Audio,Photo,Video";
MaxStreamingBitrate = 8000000;
@ -129,13 +129,14 @@ namespace MediaBrowser.Model.Dlna
continue;
}
if (!ListHelper.ContainsIgnoreCase(i.GetAudioCodecs(), audioCodec ?? string.Empty))
if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
continue;
}
return i;
}
return null;
}
@ -155,7 +156,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
if (!ListHelper.ContainsIgnoreCase(i.GetAudioCodecs(), audioCodec ?? string.Empty))
if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
continue;
}
@ -185,7 +186,7 @@ namespace MediaBrowser.Model.Dlna
}
var audioCodecs = i.GetAudioCodecs();
if (audioCodecs.Length > 0 && !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec ?? string.Empty))
if (audioCodecs.Length > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
continue;
}
@ -288,13 +289,13 @@ namespace MediaBrowser.Model.Dlna
}
var audioCodecs = i.GetAudioCodecs();
if (audioCodecs.Length > 0 && !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec ?? string.Empty))
if (audioCodecs.Length > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
continue;
}
var videoCodecs = i.GetVideoCodecs();
if (videoCodecs.Length > 0 && !ListHelper.ContainsIgnoreCase(videoCodecs, videoCodec ?? string.Empty))
if (videoCodecs.Length > 0 && !videoCodecs.Contains(videoCodec ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
continue;
}

@ -1,7 +1,8 @@
#pragma warning disable CS1591
using System;
using System.Linq;
using System.Xml.Serialization;
using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Dlna
{
@ -40,7 +41,7 @@ namespace MediaBrowser.Model.Dlna
}
var languages = GetLanguages();
return languages.Length == 0 || ListHelper.ContainsIgnoreCase(languages, subLanguage);
return languages.Length == 0 || languages.Contains(subLanguage, StringComparer.OrdinalIgnoreCase);
}
}
}

@ -0,0 +1,48 @@
using System;
namespace MediaBrowser.Model.Dto
{
/// <summary>
/// Class PublicUserDto. Its goal is to show only public information about a user
/// </summary>
public class PublicUserDto : IItemDto
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the primary image tag.
/// </summary>
/// <value>The primary image tag.</value>
public string PrimaryImageTag { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance has password.
/// </summary>
/// <value><c>true</c> if this instance has password; otherwise, <c>false</c>.</value>
public bool HasPassword { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance has configured password.
/// Note that in this case this method should not be here, but it is necessary when changing password at the
/// first login.
/// </summary>
/// <value><c>true</c> if this instance has configured password; otherwise, <c>false</c>.</value>
public bool HasConfiguredPassword { get; set; }
/// <summary>
/// Gets or sets the primary image aspect ratio.
/// </summary>
/// <value>The primary image aspect ratio.</value>
public double? PrimaryImageAspectRatio { get; set; }
/// <inheritdoc />
public override string ToString()
{
return Name ?? base.ToString();
}
}
}

@ -1,27 +0,0 @@
#pragma warning disable CS1591
using System;
namespace MediaBrowser.Model.Extensions
{
// TODO: @bond remove
public static class ListHelper
{
public static bool ContainsIgnoreCase(string[] list, string value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
foreach (var item in list)
{
if (string.Equals(item, value, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
}
}

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

Loading…
Cancel
Save