Merge branch 'api-migration' of https://github.com/jellyfin/jellyfin into redoc

This commit is contained in:
crobibero 2020-04-21 15:12:04 -06:00
commit 14c94b4e5f
34 changed files with 360 additions and 500 deletions

14
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,14 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"ms-dotnettools.csharp",
"editorconfig.editorconfig"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [
]
}

View File

@ -1,5 +1,4 @@
ARG DOTNET_VERSION=3.1 ARG DOTNET_VERSION=3.1
ARG FFMPEG_VERSION=latest
FROM node:alpine as web-builder FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master ARG JELLYFIN_WEB_VERSION=master
@ -17,7 +16,6 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting # see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg
FROM debian:buster-slim FROM debian:buster-slim
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable # https://askubuntu.com/questions/972516/debian-frontend-environment-variable
@ -27,32 +25,26 @@ ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support) # https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
COPY --from=ffmpeg /opt/ffmpeg /opt/ffmpeg
COPY --from=builder /jellyfin /jellyfin COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web COPY --from=web-builder /dist /jellyfin/jellyfin-web
# Install dependencies: # Install dependencies:
# libfontconfig1: needed for Skia # mesa-va-drivers: needed for AMD VAAPI
# libgomp1: needed for ffmpeg
# libva-drm2: needed for ffmpeg
# mesa-va-drivers: needed for VAAPI
RUN apt-get update \ RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg wget apt-transport-https \
&& wget -O - https://repo.jellyfin.org/jellyfin_team.gpg.key | apt-key add - \
&& echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | tee /etc/apt/sources.list.d/jellyfin.list \
&& apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y \ && apt-get install --no-install-recommends --no-install-suggests -y \
libfontconfig1 \
libgomp1 \
libva-drm2 \
mesa-va-drivers \ mesa-va-drivers \
jellyfin-ffmpeg \
openssl \ openssl \
ca-certificates \
vainfo \
i965-va-driver \
locales \ locales \
&& apt-get remove gnupg wget apt-transport-https -y \
&& apt-get clean autoclean -y \ && apt-get clean autoclean -y \
&& apt-get autoremove -y \ && apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& mkdir -p /cache /config /media \ && mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media \ && chmod 777 /cache /config /media \
&& ln -s /opt/ffmpeg/bin/ffmpeg /usr/local/bin \
&& ln -s /opt/ffmpeg/bin/ffprobe /usr/local/bin \
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
@ -65,4 +57,4 @@ VOLUME /cache /config /media
ENTRYPOINT ["./jellyfin/jellyfin", \ ENTRYPOINT ["./jellyfin/jellyfin", \
"--datadir", "/config", \ "--datadir", "/config", \
"--cachedir", "/cache", \ "--cachedir", "/cache", \
"--ffmpeg", "/usr/local/bin/ffmpeg"] "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"]

View File

@ -1018,19 +1018,58 @@ namespace Emby.Dlna.Didl
} }
} }
item = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Primary)); // For audio tracks without art use album art if available.
if (item is Audio audioItem)
if (item != null)
{ {
if (item.HasImage(ImageType.Primary)) var album = audioItem.AlbumEntity;
{ return album != null && album.HasImage(ImageType.Primary)
return GetImageInfo(item, ImageType.Primary); ? GetImageInfo(album, ImageType.Primary)
: null;
} }
// Don't look beyond album/playlist level. Metadata service may assign an image from a different album/show to the parent folder.
if (item is MusicAlbum || item is Playlist)
{
return null;
}
// For other item types check parents, but be aware that image retrieved from a parent may be not suitable for this media item.
var parentWithImage = GetFirstParentWithImageBelowUserRoot(item);
if (parentWithImage != null)
{
return GetImageInfo(parentWithImage, ImageType.Primary);
} }
return null; return null;
} }
private BaseItem GetFirstParentWithImageBelowUserRoot(BaseItem item)
{
if (item == null)
{
return null;
}
if (item.HasImage(ImageType.Primary))
{
return item;
}
var parent = item.GetParent();
if (parent is UserRootFolder)
{
return null;
}
// terminate in case we went past user root folder (unlikely?)
if (parent is Folder folder && folder.IsRoot)
{
return null;
}
return GetFirstParentWithImageBelowUserRoot(parent);
}
private ImageDownloadInfo GetImageInfo(BaseItem item, ImageType type) private ImageDownloadInfo GetImageInfo(BaseItem item, ImageType type)
{ {
var imageInfo = item.GetImageInfo(type, 0); var imageInfo = item.GetImageInfo(type, 0);

View File

@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.Activity
}); });
} }
private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, PackageVersionInfo)> e) private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, VersionInfo)> e)
{ {
CreateLogEntry(new ActivityLogEntry CreateLogEntry(new ActivityLogEntry
{ {
@ -434,8 +434,8 @@ namespace Emby.Server.Implementations.Activity
ShortOverview = string.Format( ShortOverview = string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"), _localization.GetLocalizedString("VersionNumber"),
e.Argument.Item2.versionStr), e.Argument.Item2.version),
Overview = e.Argument.Item2.description Overview = e.Argument.Item2.changelog
}); });
} }
@ -451,7 +451,7 @@ namespace Emby.Server.Implementations.Activity
}); });
} }
private void OnPluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> e) private void OnPluginInstalled(object sender, GenericEventArgs<VersionInfo> e)
{ {
CreateLogEntry(new ActivityLogEntry CreateLogEntry(new ActivityLogEntry
{ {
@ -463,7 +463,7 @@ namespace Emby.Server.Implementations.Activity
ShortOverview = string.Format( ShortOverview = string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"), _localization.GetLocalizedString("VersionNumber"),
e.Argument.versionStr) e.Argument.version)
}); });
} }

View File

@ -211,19 +211,6 @@ namespace Emby.Server.Implementations
public IFileSystem FileSystemManager { get; set; } public IFileSystem FileSystemManager { get; set; }
/// <inheritdoc />
public PackageVersionClass SystemUpdateLevel
{
get
{
#if BETA
return PackageVersionClass.Beta;
#else
return PackageVersionClass.Release;
#endif
}
}
/// <summary> /// <summary>
/// Gets or sets the service provider. /// Gets or sets the service provider.
/// </summary> /// </summary>
@ -1390,7 +1377,6 @@ namespace Emby.Server.Implementations
SupportsLibraryMonitor = true, SupportsLibraryMonitor = true,
EncoderLocation = MediaEncoder.EncoderLocation, EncoderLocation = MediaEncoder.EncoderLocation,
SystemArchitecture = RuntimeInformation.OSArchitecture, SystemArchitecture = RuntimeInformation.OSArchitecture,
SystemUpdateLevel = SystemUpdateLevel,
PackageName = StartupOptions.PackageName PackageName = StartupOptions.PackageName
}; };
} }

View File

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

View File

@ -39,7 +39,7 @@ namespace Emby.Server.Implementations.Library
// for imdbid we also accept pattern matching // for imdbid we also accept pattern matching
if (string.Equals(attrib, "imdbid", StringComparison.OrdinalIgnoreCase)) if (string.Equals(attrib, "imdbid", StringComparison.OrdinalIgnoreCase))
{ {
var m = Regex.Match(str, "tt\\d{7}", RegexOptions.IgnoreCase); var m = Regex.Match(str, "tt([0-9]{7,8})", RegexOptions.IgnoreCase);
return m.Success ? m.Value : null; return m.Success ? m.Value : null;
} }

View File

@ -3,7 +3,7 @@
"AppDeviceValues": "App: {0}, Gerät: {1}", "AppDeviceValues": "App: {0}, Gerät: {1}",
"Application": "Anwendung", "Application": "Anwendung",
"Artists": "Interpreten", "Artists": "Interpreten",
"AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich authentifziert", "AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich authentifiziert",
"Books": "Bücher", "Books": "Bücher",
"CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen", "CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen",
"Channels": "Kanäle", "Channels": "Kanäle",
@ -99,11 +99,11 @@
"TaskRefreshChannels": "Erneuere Kanäle", "TaskRefreshChannels": "Erneuere Kanäle",
"TaskCleanTranscodeDescription": "Löscht Transkodierdateien welche älter als ein Tag sind.", "TaskCleanTranscodeDescription": "Löscht Transkodierdateien welche älter als ein Tag sind.",
"TaskCleanTranscode": "Lösche Transkodier Pfad", "TaskCleanTranscode": "Lösche Transkodier Pfad",
"TaskUpdatePluginsDescription": "Läd Updates für Plugins herunter, welche dazu eingestellt sind automatisch zu updaten und installiert sie.", "TaskUpdatePluginsDescription": "Lädt Updates für Plugins herunter, welche dazu eingestellt sind automatisch zu updaten und installiert sie.",
"TaskUpdatePlugins": "Update Plugins", "TaskUpdatePlugins": "Update Plugins",
"TaskRefreshPeopleDescription": "Erneuert Metadaten für Schausteller und Regisseure in deinen Bibliotheken.", "TaskRefreshPeopleDescription": "Erneuert Metadaten für Schausteller und Regisseure in deinen Bibliotheken.",
"TaskRefreshPeople": "Erneuere Schausteller", "TaskRefreshPeople": "Erneuere Schausteller",
"TaskCleanLogsDescription": "Lösche Log Datein die älter als {0} Tage sind.", "TaskCleanLogsDescription": "Lösche Log Dateien die älter als {0} Tage sind.",
"TaskCleanLogs": "Lösche Log Pfad", "TaskCleanLogs": "Lösche Log Pfad",
"TaskRefreshLibraryDescription": "Scanne alle Bibliotheken für hinzugefügte Datein und erneuere Metadaten.", "TaskRefreshLibraryDescription": "Scanne alle Bibliotheken für hinzugefügte Datein und erneuere Metadaten.",
"TaskRefreshLibrary": "Scanne alle Bibliotheken", "TaskRefreshLibrary": "Scanne alle Bibliotheken",

View File

@ -23,7 +23,7 @@
"HeaderFavoriteEpisodes": "قسمت‌های مورد علاقه", "HeaderFavoriteEpisodes": "قسمت‌های مورد علاقه",
"HeaderFavoriteShows": "سریال‌های مورد علاقه", "HeaderFavoriteShows": "سریال‌های مورد علاقه",
"HeaderFavoriteSongs": "آهنگ‌های مورد علاقه", "HeaderFavoriteSongs": "آهنگ‌های مورد علاقه",
"HeaderLiveTV": "تلویزیون زنده", "HeaderLiveTV": "پخش زنده",
"HeaderNextUp": "قسمت بعدی", "HeaderNextUp": "قسمت بعدی",
"HeaderRecordingGroups": "گروه‌های ضبط", "HeaderRecordingGroups": "گروه‌های ضبط",
"HomeVideos": "ویدیوهای خانگی", "HomeVideos": "ویدیوهای خانگی",

View File

@ -11,7 +11,7 @@
"Collections": "Collections", "Collections": "Collections",
"DeviceOfflineWithName": "{0} s'est déconnecté", "DeviceOfflineWithName": "{0} s'est déconnecté",
"DeviceOnlineWithName": "{0} est connecté", "DeviceOnlineWithName": "{0} est connecté",
"FailedLoginAttemptWithUserName": "Échec de connexion de {0}", "FailedLoginAttemptWithUserName": "Échec de connexion depuis {0}",
"Favorites": "Favoris", "Favorites": "Favoris",
"Folders": "Dossiers", "Folders": "Dossiers",
"Genres": "Genres", "Genres": "Genres",
@ -69,7 +69,7 @@
"PluginUpdatedWithName": "{0} a été mis à jour", "PluginUpdatedWithName": "{0} a été mis à jour",
"ProviderValue": "Fournisseur : {0}", "ProviderValue": "Fournisseur : {0}",
"ScheduledTaskFailedWithName": "{0} a échoué", "ScheduledTaskFailedWithName": "{0} a échoué",
"ScheduledTaskStartedWithName": "{0} a commencé", "ScheduledTaskStartedWithName": "{0} a démarré",
"ServerNameNeedsToBeRestarted": "{0} doit être redémarré", "ServerNameNeedsToBeRestarted": "{0} doit être redémarré",
"Shows": "Émissions", "Shows": "Émissions",
"Songs": "Chansons", "Songs": "Chansons",
@ -95,21 +95,21 @@
"VersionNumber": "Version {0}", "VersionNumber": "Version {0}",
"TasksChannelsCategory": "Chaines en ligne", "TasksChannelsCategory": "Chaines en ligne",
"TaskDownloadMissingSubtitlesDescription": "Cherche les sous-titres manquant sur internet en se basant sur la configuration des métadonnées.", "TaskDownloadMissingSubtitlesDescription": "Cherche les sous-titres manquant sur internet en se basant sur la configuration des métadonnées.",
"TaskDownloadMissingSubtitles": "Télécharge les sous-titres manquant", "TaskDownloadMissingSubtitles": "Télécharger les sous-titres manquant",
"TaskRefreshChannelsDescription": "Rafraîchit les informations des chaines en ligne.", "TaskRefreshChannelsDescription": "Rafraîchit les informations des chaines en ligne.",
"TaskRefreshChannels": "Rafraîchit les chaines", "TaskRefreshChannels": "Rafraîchir les chaines",
"TaskCleanTranscodeDescription": "Supprime les fichiers transcodés de plus d'un jour.", "TaskCleanTranscodeDescription": "Supprime les fichiers transcodés de plus d'un jour.",
"TaskCleanTranscode": "Nettoie les dossier des transcodages", "TaskCleanTranscode": "Nettoyer les dossier des transcodages",
"TaskUpdatePluginsDescription": "Télécharge et installe les mises à jours des plugins configurés pour être mis à jour automatiquement.", "TaskUpdatePluginsDescription": "Télécharge et installe les mises à jours des extensions configurés pour être mises à jour automatiquement.",
"TaskUpdatePlugins": "Mettre à jour les plugins", "TaskUpdatePlugins": "Mettre à jour les extensions",
"TaskRefreshPeopleDescription": "Met à jour les métadonnées pour les acteurs et directeurs dans votre bibliothèque.", "TaskRefreshPeopleDescription": "Met à jour les métadonnées pour les acteurs et réalisateurs dans votre bibliothèque.",
"TaskRefreshPeople": "Rafraîchit les acteurs", "TaskRefreshPeople": "Rafraîchir les acteurs",
"TaskCleanLogsDescription": "Supprime les journaux de plus de {0} jours.", "TaskCleanLogsDescription": "Supprime les journaux de plus de {0} jours.",
"TaskCleanLogs": "Nettoie le répertoire des journaux", "TaskCleanLogs": "Nettoyer le répertoire des journaux",
"TaskRefreshLibraryDescription": "Scanne toute les bibliothèques pour trouver les nouveaux fichiers et rafraîchit les métadonnées.", "TaskRefreshLibraryDescription": "Scanne toute les bibliothèques pour trouver les nouveaux fichiers et rafraîchit les métadonnées.",
"TaskRefreshLibrary": "Scanne toute les Bibliothèques", "TaskRefreshLibrary": "Scanner toute les Bibliothèques",
"TaskRefreshChapterImagesDescription": "Crée des images de miniature pour les vidéos ayant des chapitres.", "TaskRefreshChapterImagesDescription": "Crée des images de miniature pour les vidéos ayant des chapitres.",
"TaskRefreshChapterImages": "Extrait les images de chapitre", "TaskRefreshChapterImages": "Extraire les images de chapitre",
"TaskCleanCacheDescription": "Supprime les fichiers de cache dont le système n'a plus besoin.", "TaskCleanCacheDescription": "Supprime les fichiers de cache dont le système n'a plus besoin.",
"TaskCleanCache": "Vider le répertoire cache", "TaskCleanCache": "Vider le répertoire cache",
"TasksApplicationCategory": "Application", "TasksApplicationCategory": "Application",

View File

@ -26,7 +26,7 @@
"HeaderLiveTV": "TV em Direto", "HeaderLiveTV": "TV em Direto",
"HeaderNextUp": "A Seguir", "HeaderNextUp": "A Seguir",
"HeaderRecordingGroups": "Grupos de Gravação", "HeaderRecordingGroups": "Grupos de Gravação",
"HomeVideos": "Home videos", "HomeVideos": "Videos caseiros",
"Inherit": "Herdar", "Inherit": "Herdar",
"ItemAddedWithName": "{0} foi adicionado à biblioteca", "ItemAddedWithName": "{0} foi adicionado à biblioteca",
"ItemRemovedWithName": "{0} foi removido da biblioteca", "ItemRemovedWithName": "{0} foi removido da biblioteca",
@ -92,5 +92,27 @@
"UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}", "UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}",
"ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia", "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia",
"ValueSpecialEpisodeName": "Especial - {0}", "ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versão {0}" "VersionNumber": "Versão {0}",
"TaskDownloadMissingSubtitlesDescription": "Procurar na internet por legendas em falta baseado na configuração de metadados.",
"TaskDownloadMissingSubtitles": "Fazer download de legendas em falta",
"TaskRefreshChannelsDescription": "Atualizar informação sobre canais da Internet.",
"TaskRefreshChannels": "Atualizar Canais",
"TaskCleanTranscodeDescription": "Apagar ficheiros de transcode com mais de um dia.",
"TaskCleanTranscode": "Limpar a Diretoria de Transcode",
"TaskUpdatePluginsDescription": "Faz o download e instala updates para os plugins que estão configurados para atualizar automaticamente.",
"TaskUpdatePlugins": "Atualizar Plugins",
"TaskRefreshPeopleDescription": "Atualizar metadados para atores e diretores na biblioteca.",
"TaskRefreshPeople": "Atualizar Pessoas",
"TaskCleanLogsDescription": "Apagar ficheiros de log que têm mais de {0} dias.",
"TaskCleanLogs": "Limpar a Diretoria de Logs",
"TaskRefreshLibraryDescription": "Scannear a biblioteca de música para novos ficheiros e atualizar os metadados.",
"TaskRefreshLibrary": "Scannear Biblioteca de Música",
"TaskRefreshChapterImagesDescription": "Criar thumbnails para os vídeos que têm capítulos.",
"TaskRefreshChapterImages": "Extrair Imagens dos Capítulos",
"TaskCleanCacheDescription": "Apagar ficheiros em cache que já não são necessários.",
"TaskCleanCache": "Limpar Cache",
"TasksChannelsCategory": "Canais da Internet",
"TasksApplicationCategory": "Aplicação",
"TasksLibraryCategory": "Biblioteca",
"TasksMaintenanceCategory": "Manutenção"
} }

View File

@ -91,5 +91,18 @@
"VersionNumber": "版本 {0}", "VersionNumber": "版本 {0}",
"HeaderRecordingGroups": "錄製組", "HeaderRecordingGroups": "錄製組",
"Inherit": "繼承", "Inherit": "繼承",
"SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕" "SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕",
"TaskDownloadMissingSubtitlesDescription": "在網路上透過描述資料搜尋遺失的字幕。",
"TaskDownloadMissingSubtitles": "下載遺失的字幕",
"TaskRefreshChannels": "重新整理頻道",
"TaskUpdatePlugins": "更新插件",
"TaskRefreshPeople": "重新整理人員",
"TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔案。",
"TaskCleanLogs": "清空紀錄資料夾",
"TaskRefreshLibraryDescription": "掃描媒體庫內新的檔案並重新整理描述資料。",
"TaskRefreshLibrary": "掃描媒體庫",
"TaskRefreshChapterImages": "擷取章節圖片",
"TaskCleanCacheDescription": "刪除系統長時間不需要的快取。",
"TaskCleanCache": "清除快取資料夾",
"TasksLibraryCategory": "媒體庫"
} }

View File

@ -26,7 +26,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Updates namespace Emby.Server.Implementations.Updates
{ {
/// <summary> /// <summary>
/// Manages all install, uninstall and update operations (both plugins and system). /// Manages all install, uninstall, and update operations for the system and individual plugins.
/// </summary> /// </summary>
public class InstallationManager : IInstallationManager public class InstallationManager : IInstallationManager
{ {
@ -36,7 +36,7 @@ namespace Emby.Server.Implementations.Updates
public const string PluginManifestUrlKey = "InstallationManager:PluginManifestUrl"; public const string PluginManifestUrlKey = "InstallationManager:PluginManifestUrl";
/// <summary> /// <summary>
/// The _logger. /// The logger.
/// </summary> /// </summary>
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
@ -112,10 +112,10 @@ namespace Emby.Server.Implementations.Updates
public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled; public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
/// <inheritdoc /> /// <inheritdoc />
public event EventHandler<GenericEventArgs<(IPlugin, PackageVersionInfo)>> PluginUpdated; public event EventHandler<GenericEventArgs<(IPlugin, VersionInfo)>> PluginUpdated;
/// <inheritdoc /> /// <inheritdoc />
public event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled; public event EventHandler<GenericEventArgs<VersionInfo>> PluginInstalled;
/// <inheritdoc /> /// <inheritdoc />
public IEnumerable<InstallationInfo> CompletedInstallations => _completedInstallationsInternal; public IEnumerable<InstallationInfo> CompletedInstallations => _completedInstallationsInternal;
@ -183,61 +183,56 @@ namespace Emby.Server.Implementations.Updates
} }
/// <inheritdoc /> /// <inheritdoc />
public IEnumerable<PackageVersionInfo> GetCompatibleVersions( public IEnumerable<VersionInfo> GetCompatibleVersions(
IEnumerable<PackageVersionInfo> availableVersions, IEnumerable<VersionInfo> availableVersions,
Version minVersion = null, Version minVersion = null)
PackageVersionClass classification = PackageVersionClass.Release)
{ {
var appVer = _applicationHost.ApplicationVersion; var appVer = _applicationHost.ApplicationVersion;
availableVersions = availableVersions availableVersions = availableVersions
.Where(x => x.classification == classification .Where(x => Version.Parse(x.targetAbi) <= appVer);
&& Version.Parse(x.requiredVersionStr) <= appVer);
if (minVersion != null) if (minVersion != null)
{ {
availableVersions = availableVersions.Where(x => x.Version >= minVersion); availableVersions = availableVersions.Where(x => x.version >= minVersion);
} }
return availableVersions.OrderByDescending(x => x.Version); return availableVersions.OrderByDescending(x => x.version);
} }
/// <inheritdoc /> /// <inheritdoc />
public IEnumerable<PackageVersionInfo> GetCompatibleVersions( public IEnumerable<VersionInfo> GetCompatibleVersions(
IEnumerable<PackageInfo> availablePackages, IEnumerable<PackageInfo> availablePackages,
string name = null, string name = null,
Guid guid = default, Guid guid = default,
Version minVersion = null, Version minVersion = null)
PackageVersionClass classification = PackageVersionClass.Release)
{ {
var package = FilterPackages(availablePackages, name, guid).FirstOrDefault(); var package = FilterPackages(availablePackages, name, guid).FirstOrDefault();
// Package not found. // Package not found in repository
if (package == null) if (package == null)
{ {
return Enumerable.Empty<PackageVersionInfo>(); return Enumerable.Empty<VersionInfo>();
} }
return GetCompatibleVersions( return GetCompatibleVersions(
package.versions, package.versions,
minVersion, minVersion);
classification);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default) public async Task<IEnumerable<VersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default)
{ {
var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false); var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false);
return GetAvailablePluginUpdates(catalog); return GetAvailablePluginUpdates(catalog);
} }
private IEnumerable<PackageVersionInfo> GetAvailablePluginUpdates(IReadOnlyList<PackageInfo> pluginCatalog) private IEnumerable<VersionInfo> GetAvailablePluginUpdates(IReadOnlyList<PackageInfo> pluginCatalog)
{ {
foreach (var plugin in _applicationHost.Plugins) foreach (var plugin in _applicationHost.Plugins)
{ {
var compatibleversions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, plugin.Version, _applicationHost.SystemUpdateLevel); var compatibleversions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, plugin.Version);
var version = compatibleversions.FirstOrDefault(y => y.Version > plugin.Version); var version = compatibleversions.FirstOrDefault(y => y.version > plugin.Version);
if (version != null if (version != null && !CompletedInstallations.Any(x => string.Equals(x.Guid, version.guid, StringComparison.OrdinalIgnoreCase)))
&& !CompletedInstallations.Any(x => string.Equals(x.AssemblyGuid, version.guid, StringComparison.OrdinalIgnoreCase)))
{ {
yield return version; yield return version;
} }
@ -245,7 +240,7 @@ namespace Emby.Server.Implementations.Updates
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken) public async Task InstallPackage(VersionInfo package, CancellationToken cancellationToken)
{ {
if (package == null) if (package == null)
{ {
@ -254,11 +249,9 @@ namespace Emby.Server.Implementations.Updates
var installationInfo = new InstallationInfo var installationInfo = new InstallationInfo
{ {
Id = Guid.NewGuid(), Guid = package.guid,
Name = package.name, Name = package.name,
AssemblyGuid = package.guid, Version = package.version.ToString()
UpdateClass = package.classification,
Version = package.versionStr
}; };
var innerCancellationTokenSource = new CancellationTokenSource(); var innerCancellationTokenSource = new CancellationTokenSource();
@ -276,7 +269,7 @@ namespace Emby.Server.Implementations.Updates
var installationEventArgs = new InstallationEventArgs var installationEventArgs = new InstallationEventArgs
{ {
InstallationInfo = installationInfo, InstallationInfo = installationInfo,
PackageVersionInfo = package VersionInfo = package
}; };
PackageInstalling?.Invoke(this, installationEventArgs); PackageInstalling?.Invoke(this, installationEventArgs);
@ -301,7 +294,7 @@ namespace Emby.Server.Implementations.Updates
_currentInstallations.Remove(tuple); _currentInstallations.Remove(tuple);
} }
_logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.versionStr); _logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.version);
PackageInstallationCancelled?.Invoke(this, installationEventArgs); PackageInstallationCancelled?.Invoke(this, installationEventArgs);
@ -337,7 +330,7 @@ namespace Emby.Server.Implementations.Updates
/// <param name="package">The package.</param> /// <param name="package">The package.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns><see cref="Task" />.</returns> /// <returns><see cref="Task" />.</returns>
private async Task InstallPackageInternal(PackageVersionInfo package, CancellationToken cancellationToken) private async Task InstallPackageInternal(VersionInfo package, CancellationToken cancellationToken)
{ {
// Set last update time if we were installed before // Set last update time if we were installed before
IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase)) IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
@ -349,26 +342,26 @@ namespace Emby.Server.Implementations.Updates
// Do plugin-specific processing // Do plugin-specific processing
if (plugin == null) if (plugin == null)
{ {
_logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification); _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.version);
PluginInstalled?.Invoke(this, new GenericEventArgs<PackageVersionInfo>(package)); PluginInstalled?.Invoke(this, new GenericEventArgs<VersionInfo>(package));
} }
else else
{ {
_logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification); _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.version);
PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, PackageVersionInfo)>((plugin, package))); PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, VersionInfo)>((plugin, package)));
} }
_applicationHost.NotifyPendingRestart(); _applicationHost.NotifyPendingRestart();
} }
private async Task PerformPackageInstallation(PackageVersionInfo package, CancellationToken cancellationToken) private async Task PerformPackageInstallation(VersionInfo package, CancellationToken cancellationToken)
{ {
var extension = Path.GetExtension(package.targetFilename); var extension = Path.GetExtension(package.filename);
if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase)) if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase))
{ {
_logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.targetFilename); _logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.filename);
return; return;
} }
@ -415,7 +408,7 @@ namespace Emby.Server.Implementations.Updates
} }
/// <summary> /// <summary>
/// Uninstalls a plugin /// Uninstalls a plugin.
/// </summary> /// </summary>
/// <param name="plugin">The plugin.</param> /// <param name="plugin">The plugin.</param>
public void UninstallPlugin(IPlugin plugin) public void UninstallPlugin(IPlugin plugin)
@ -473,7 +466,7 @@ namespace Emby.Server.Implementations.Updates
{ {
lock (_currentInstallationsLock) lock (_currentInstallationsLock)
{ {
var install = _currentInstallations.Find(x => x.info.Id == id); var install = _currentInstallations.Find(x => x.info.Guid == id.ToString());
if (install == default((InstallationInfo, CancellationTokenSource))) if (install == default((InstallationInfo, CancellationTokenSource)))
{ {
return false; return false;

View File

@ -42,23 +42,6 @@ namespace MediaBrowser.Api
[Authenticated] [Authenticated]
public class GetPackages : IReturn<PackageInfo[]> public class GetPackages : IReturn<PackageInfo[]>
{ {
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[ApiMember(Name = "PackageType", Description = "Optional package type filter (System/UserInstalled)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string PackageType { get; set; }
[ApiMember(Name = "TargetSystems", Description = "Optional. Filter by target system type. Allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string TargetSystems { get; set; }
[ApiMember(Name = "IsPremium", Description = "Optional. Filter by premium status", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? IsPremium { get; set; }
[ApiMember(Name = "IsAdult", Description = "Optional. Filter by package that contain adult content.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? IsAdult { get; set; }
public bool? IsAppStoreEnabled { get; set; }
} }
/// <summary> /// <summary>
@ -88,13 +71,6 @@ namespace MediaBrowser.Api
/// <value>The version.</value> /// <value>The version.</value>
[ApiMember(Name = "Version", Description = "Optional version. Defaults to latest version.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] [ApiMember(Name = "Version", Description = "Optional version. Defaults to latest version.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Version { get; set; } public string Version { get; set; }
/// <summary>
/// Gets or sets the update class.
/// </summary>
/// <value>The update class.</value>
[ApiMember(Name = "UpdateClass", Description = "Optional update class (Dev, Beta, Release). Defaults to Release.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public PackageVersionClass UpdateClass { get; set; }
} }
/// <summary> /// <summary>
@ -154,23 +130,6 @@ namespace MediaBrowser.Api
{ {
IEnumerable<PackageInfo> packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false); IEnumerable<PackageInfo> packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false);
if (!string.IsNullOrEmpty(request.TargetSystems))
{
var apps = request.TargetSystems.Split(',').Select(i => (PackageTargetSystem)Enum.Parse(typeof(PackageTargetSystem), i, true));
packages = packages.Where(p => apps.Contains(p.targetSystem));
}
if (request.IsAdult.HasValue)
{
packages = packages.Where(p => p.adult == request.IsAdult.Value);
}
if (request.IsAppStoreEnabled.HasValue)
{
packages = packages.Where(p => p.enableInAppStore == request.IsAppStoreEnabled.Value);
}
return ToOptimizedResult(packages.ToArray()); return ToOptimizedResult(packages.ToArray());
} }
@ -186,8 +145,7 @@ namespace MediaBrowser.Api
packages, packages,
request.Name, request.Name,
string.IsNullOrEmpty(request.AssemblyGuid) ? Guid.Empty : Guid.Parse(request.AssemblyGuid), string.IsNullOrEmpty(request.AssemblyGuid) ? Guid.Empty : Guid.Parse(request.AssemblyGuid),
string.IsNullOrEmpty(request.Version) ? null : Version.Parse(request.Version), string.IsNullOrEmpty(request.Version) ? null : Version.Parse(request.Version)).FirstOrDefault();
request.UpdateClass).FirstOrDefault();
if (package == null) if (package == null)
{ {

View File

@ -134,7 +134,7 @@ namespace MediaBrowser.Api.Playback
var data = $"{state.MediaPath}-{state.UserAgent}-{state.Request.DeviceId}-{state.Request.PlaySessionId}"; var data = $"{state.MediaPath}-{state.UserAgent}-{state.Request.DeviceId}-{state.Request.PlaySessionId}";
var filename = data.GetMD5().ToString("N", CultureInfo.InvariantCulture); var filename = data.GetMD5().ToString("N", CultureInfo.InvariantCulture);
var ext = outputFileExtension.ToLowerInvariant(); var ext = outputFileExtension?.ToLowerInvariant();
var folder = ServerConfigurationManager.GetTranscodePath(); var folder = ServerConfigurationManager.GetTranscodePath();
return EnableOutputInSubFolder return EnableOutputInSubFolder

View File

@ -242,11 +242,11 @@ namespace MediaBrowser.Api.UserLibrary
return folder.GetItems(GetItemsQuery(request, dtoOptions, user)); return folder.GetItems(GetItemsQuery(request, dtoOptions, user));
} }
var itemsArray = folder.GetChildren(user, true).ToArray(); var itemsArray = folder.GetChildren(user, true);
return new QueryResult<BaseItem> return new QueryResult<BaseItem>
{ {
Items = itemsArray, Items = itemsArray,
TotalRecordCount = itemsArray.Length, TotalRecordCount = itemsArray.Count,
StartIndex = 0 StartIndex = 0
}; };
} }

View File

@ -48,12 +48,6 @@ namespace MediaBrowser.Common
/// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
bool CanSelfRestart { get; } bool CanSelfRestart { get; }
/// <summary>
/// Gets the version class of the system.
/// </summary>
/// <value><see cref="PackageVersionClass.Release" /> or <see cref="PackageVersionClass.Beta" />.</value>
PackageVersionClass SystemUpdateLevel { get; }
/// <summary> /// <summary>
/// Gets the application version. /// Gets the application version.
/// </summary> /// </summary>

View File

@ -67,7 +67,7 @@ namespace MediaBrowser.Common.Plugins
} }
/// <summary> /// <summary>
/// Called when just before the plugin is uninstalled from the server. /// Called just before the plugin is uninstalled from the server.
/// </summary> /// </summary>
public virtual void OnUninstalling() public virtual void OnUninstalling()
{ {
@ -101,7 +101,7 @@ namespace MediaBrowser.Common.Plugins
private readonly object _configurationSyncLock = new object(); private readonly object _configurationSyncLock = new object();
/// <summary> /// <summary>
/// The save lock. /// The configuration save lock.
/// </summary> /// </summary>
private readonly object _configurationSaveLock = new object(); private readonly object _configurationSaveLock = new object();
@ -148,7 +148,7 @@ namespace MediaBrowser.Common.Plugins
protected string AssemblyFileName => Path.GetFileName(AssemblyFilePath); protected string AssemblyFileName => Path.GetFileName(AssemblyFilePath);
/// <summary> /// <summary>
/// Gets or sets the plugin's configuration. /// Gets or sets the plugin configuration.
/// </summary> /// </summary>
/// <value>The configuration.</value> /// <value>The configuration.</value>
public TConfigurationType Configuration public TConfigurationType Configuration
@ -186,7 +186,7 @@ namespace MediaBrowser.Common.Plugins
public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName); public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName);
/// <summary> /// <summary>
/// Gets the plugin's configuration. /// Gets the plugin configuration.
/// </summary> /// </summary>
/// <value>The configuration.</value> /// <value>The configuration.</value>
BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration; BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration;

View File

@ -28,12 +28,12 @@ namespace MediaBrowser.Common.Updates
/// <summary> /// <summary>
/// Occurs when a plugin is updated. /// Occurs when a plugin is updated.
/// </summary> /// </summary>
event EventHandler<GenericEventArgs<(IPlugin, PackageVersionInfo)>> PluginUpdated; event EventHandler<GenericEventArgs<(IPlugin, VersionInfo)>> PluginUpdated;
/// <summary> /// <summary>
/// Occurs when a plugin is installed. /// Occurs when a plugin is installed.
/// </summary> /// </summary>
event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled; event EventHandler<GenericEventArgs<VersionInfo>> PluginInstalled;
/// <summary> /// <summary>
/// Gets the completed installations. /// Gets the completed installations.
@ -64,12 +64,10 @@ namespace MediaBrowser.Common.Updates
/// </summary> /// </summary>
/// <param name="availableVersions">The available version of the plugin.</param> /// <param name="availableVersions">The available version of the plugin.</param>
/// <param name="minVersion">The minimum required version of the plugin.</param> /// <param name="minVersion">The minimum required version of the plugin.</param>
/// <param name="classification">The classification of updates.</param>
/// <returns>All compatible versions ordered from newest to oldest.</returns> /// <returns>All compatible versions ordered from newest to oldest.</returns>
IEnumerable<PackageVersionInfo> GetCompatibleVersions( IEnumerable<VersionInfo> GetCompatibleVersions(
IEnumerable<PackageVersionInfo> availableVersions, IEnumerable<VersionInfo> availableVersions,
Version minVersion = null, Version minVersion = null);
PackageVersionClass classification = PackageVersionClass.Release);
/// <summary> /// <summary>
/// Returns all compatible versions ordered from newest to oldest. /// Returns all compatible versions ordered from newest to oldest.
@ -78,21 +76,19 @@ namespace MediaBrowser.Common.Updates
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="guid">The guid of the plugin.</param> /// <param name="guid">The guid of the plugin.</param>
/// <param name="minVersion">The minimum required version of the plugin.</param> /// <param name="minVersion">The minimum required version of the plugin.</param>
/// <param name="classification">The classification.</param>
/// <returns>All compatible versions ordered from newest to oldest.</returns> /// <returns>All compatible versions ordered from newest to oldest.</returns>
IEnumerable<PackageVersionInfo> GetCompatibleVersions( IEnumerable<VersionInfo> GetCompatibleVersions(
IEnumerable<PackageInfo> availablePackages, IEnumerable<PackageInfo> availablePackages,
string name = null, string name = null,
Guid guid = default, Guid guid = default,
Version minVersion = null, Version minVersion = null);
PackageVersionClass classification = PackageVersionClass.Release);
/// <summary> /// <summary>
/// Returns the available plugin updates. /// Returns the available plugin updates.
/// </summary> /// </summary>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The available plugin updates.</returns> /// <returns>The available plugin updates.</returns>
Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default); Task<IEnumerable<VersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Installs the package. /// Installs the package.
@ -100,7 +96,7 @@ namespace MediaBrowser.Common.Updates
/// <param name="package">The package.</param> /// <param name="package">The package.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns><see cref="Task" />.</returns> /// <returns><see cref="Task" />.</returns>
Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken = default); Task InstallPackage(VersionInfo package, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Uninstalls a plugin. /// Uninstalls a plugin.

View File

@ -8,6 +8,6 @@ namespace MediaBrowser.Common.Updates
{ {
public InstallationInfo InstallationInfo { get; set; } public InstallationInfo InstallationInfo { get; set; }
public PackageVersionInfo PackageVersionInfo { get; set; } public VersionInfo VersionInfo { get; set; }
} }
} }

View File

@ -549,7 +549,6 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore] [JsonIgnore]
public DateTime DateModified { get; set; } public DateTime DateModified { get; set; }
[JsonIgnore]
public DateTime DateLastSaved { get; set; } public DateTime DateLastSaved { get; set; }
[JsonIgnore] [JsonIgnore]
@ -2741,7 +2740,7 @@ namespace MediaBrowser.Controller.Entities
{ {
var list = GetEtagValues(user); var list = GetEtagValues(user);
return string.Join("|", list.ToArray()).GetMD5().ToString("N", CultureInfo.InvariantCulture); return string.Join("|", list).GetMD5().ToString("N", CultureInfo.InvariantCulture);
} }
protected virtual List<string> GetEtagValues(User user) protected virtual List<string> GetEtagValues(User user)
@ -2784,8 +2783,7 @@ namespace MediaBrowser.Controller.Entities
return true; return true;
} }
var view = this as IHasCollectionType; if (this is IHasCollectionType view)
if (view != null)
{ {
if (string.Equals(view.CollectionType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase)) if (string.Equals(view.CollectionType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
{ {

View File

@ -864,7 +864,7 @@ namespace MediaBrowser.Controller.Entities
return SortItemsByRequest(query, result); return SortItemsByRequest(query, result);
} }
return result.ToArray(); return result;
} }
return GetItemsInternal(query).Items; return GetItemsInternal(query).Items;

View File

@ -478,7 +478,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "I-frame image extraction failed, will attempt standard way. Input: {arguments}", inputArgument); _logger.LogError(ex, "I-frame image extraction failed, will attempt standard way. Input: {Arguments}", inputArgument);
} }
} }
@ -969,7 +969,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
public int? ExitCode { get; private set; } public int? ExitCode { get; private set; }
void OnProcessExited(object sender, EventArgs e) private void OnProcessExited(object sender, EventArgs e)
{ {
var process = (Process)sender; var process = (Process)sender;

View File

@ -8,17 +8,17 @@ namespace MediaBrowser.Model.Services
{ {
/// <summary> /// <summary>
/// Order in which Request Filters are executed. /// Order in which Request Filters are executed.
/// &lt;0 Executed before global request filters /// &lt;0 Executed before global request filters.
/// &gt;0 Executed after global request filters /// &gt;0 Executed after global request filters.
/// </summary> /// </summary>
int Priority { get; } int Priority { get; }
/// <summary> /// <summary>
/// The request filter is executed before the service. /// The request filter is executed before the service.
/// </summary> /// </summary>
/// <param name="req">The http request wrapper</param> /// <param name="req">The http request wrapper.</param>
/// <param name="res">The http response wrapper</param> /// <param name="res">The http response wrapper.</param>
/// <param name="requestDto">The request DTO</param> /// <param name="requestDto">The request DTO.</param>
void RequestFilter(IRequest req, HttpResponse res, object requestDto); void RequestFilter(IRequest req, HttpResponse res, object requestDto);
} }
} }

View File

@ -26,8 +26,6 @@ namespace MediaBrowser.Model.System
/// </summary> /// </summary>
public class SystemInfo : PublicSystemInfo public class SystemInfo : PublicSystemInfo
{ {
public PackageVersionClass SystemUpdateLevel { get; set; }
/// <summary> /// <summary>
/// Gets or sets the display name of the operating system. /// Gets or sets the display name of the operating system.
/// </summary> /// </summary>

View File

@ -1,29 +0,0 @@
namespace MediaBrowser.Model.Updates
{
/// <summary>
/// Class CheckForUpdateResult.
/// </summary>
public class CheckForUpdateResult
{
/// <summary>
/// Gets or sets a value indicating whether this instance is update available.
/// </summary>
/// <value><c>true</c> if this instance is update available; otherwise, <c>false</c>.</value>
public bool IsUpdateAvailable { get; set; }
/// <summary>
/// Gets or sets the available version.
/// </summary>
/// <value>The available version.</value>
public string AvailableVersion
{
get => Package != null ? Package.versionStr : "0.0.0.1";
set { } // need this for the serializer
}
/// <summary>
/// Get or sets package information for an available update
/// </summary>
public PackageVersionInfo Package { get; set; }
}
}

View File

@ -8,10 +8,10 @@ namespace MediaBrowser.Model.Updates
public class InstallationInfo public class InstallationInfo
{ {
/// <summary> /// <summary>
/// Gets or sets the id. /// Gets or sets the guid.
/// </summary> /// </summary>
/// <value>The id.</value> /// <value>The guid.</value>
public Guid Id { get; set; } public string Guid { get; set; }
/// <summary> /// <summary>
/// Gets or sets the name. /// Gets or sets the name.
@ -19,22 +19,10 @@ namespace MediaBrowser.Model.Updates
/// <value>The name.</value> /// <value>The name.</value>
public string Name { get; set; } public string Name { get; set; }
/// <summary>
/// Gets or sets the assembly guid.
/// </summary>
/// <value>The guid of the assembly.</value>
public string AssemblyGuid { get; set; }
/// <summary> /// <summary>
/// Gets or sets the version. /// Gets or sets the version.
/// </summary> /// </summary>
/// <value>The version.</value> /// <value>The version.</value>
public string Version { get; set; } public string Version { get; set; }
/// <summary>
/// Gets or sets the update class.
/// </summary>
/// <value>The update class.</value>
public PackageVersionClass UpdateClass { get; set; }
} }
} }

View File

@ -8,12 +8,6 @@ namespace MediaBrowser.Model.Updates
/// </summary> /// </summary>
public class PackageInfo public class PackageInfo
{ {
/// <summary>
/// The internal id of this package.
/// </summary>
/// <value>The id.</value>
public string id { get; set; }
/// <summary> /// <summary>
/// Gets or sets the name. /// Gets or sets the name.
/// </summary> /// </summary>
@ -21,59 +15,17 @@ namespace MediaBrowser.Model.Updates
public string name { get; set; } public string name { get; set; }
/// <summary> /// <summary>
/// Gets or sets the short description. /// Gets or sets a long description of the plugin containing features or helpful explanations.
/// </summary> /// </summary>
/// <value>The short description.</value> /// <value>The description.</value>
public string shortDescription { get; set; } public string description { get; set; }
/// <summary> /// <summary>
/// Gets or sets the overview. /// Gets or sets a short overview of what the plugin does.
/// </summary> /// </summary>
/// <value>The overview.</value> /// <value>The overview.</value>
public string overview { get; set; } public string overview { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is premium.
/// </summary>
/// <value><c>true</c> if this instance is premium; otherwise, <c>false</c>.</value>
public bool isPremium { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is adult only content.
/// </summary>
/// <value><c>true</c> if this instance is adult; otherwise, <c>false</c>.</value>
public bool adult { get; set; }
/// <summary>
/// Gets or sets the rich desc URL.
/// </summary>
/// <value>The rich desc URL.</value>
public string richDescUrl { get; set; }
/// <summary>
/// Gets or sets the thumb image.
/// </summary>
/// <value>The thumb image.</value>
public string thumbImage { get; set; }
/// <summary>
/// Gets or sets the preview image.
/// </summary>
/// <value>The preview image.</value>
public string previewImage { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public string type { get; set; }
/// <summary>
/// Gets or sets the target filename.
/// </summary>
/// <value>The target filename.</value>
public string targetFilename { get; set; }
/// <summary> /// <summary>
/// Gets or sets the owner. /// Gets or sets the owner.
/// </summary> /// </summary>
@ -87,90 +39,24 @@ namespace MediaBrowser.Model.Updates
public string category { get; set; } public string category { get; set; }
/// <summary> /// <summary>
/// Gets or sets the catalog tile color. /// The guid of the assembly associated with this plugin.
/// </summary>
/// <value>The owner.</value>
public string tileColor { get; set; }
/// <summary>
/// Gets or sets the feature id of this package (if premium).
/// </summary>
/// <value>The feature id.</value>
public string featureId { get; set; }
/// <summary>
/// Gets or sets the registration info for this package (if premium).
/// </summary>
/// <value>The registration info.</value>
public string regInfo { get; set; }
/// <summary>
/// Gets or sets the price for this package (if premium).
/// </summary>
/// <value>The price.</value>
public float price { get; set; }
/// <summary>
/// Gets or sets the target system for this plug-in (Server, MBTheater, MBClassic).
/// </summary>
/// <value>The target system.</value>
public PackageTargetSystem targetSystem { get; set; }
/// <summary>
/// The guid of the assembly associated with this package (if a plug-in).
/// This is used to identify the proper item for automatic updates. /// This is used to identify the proper item for automatic updates.
/// </summary> /// </summary>
/// <value>The name.</value> /// <value>The name.</value>
public string guid { get; set; } public string guid { get; set; }
/// <summary>
/// Gets or sets the total number of ratings for this package.
/// </summary>
/// <value>The total ratings.</value>
public int? totalRatings { get; set; }
/// <summary>
/// Gets or sets the average rating for this package .
/// </summary>
/// <value>The rating.</value>
public float avgRating { get; set; }
/// <summary>
/// Gets or sets whether or not this package is registered.
/// </summary>
/// <value>True if registered.</value>
public bool isRegistered { get; set; }
/// <summary>
/// Gets or sets the expiration date for this package.
/// </summary>
/// <value>Expiration Date.</value>
public DateTime expDate { get; set; }
/// <summary> /// <summary>
/// Gets or sets the versions. /// Gets or sets the versions.
/// </summary> /// </summary>
/// <value>The versions.</value> /// <value>The versions.</value>
public IReadOnlyList<PackageVersionInfo> versions { get; set; } public IReadOnlyList<VersionInfo> versions { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [enable in application store].
/// </summary>
/// <value><c>true</c> if [enable in application store]; otherwise, <c>false</c>.</value>
public bool enableInAppStore { get; set; }
/// <summary>
/// Gets or sets the installs.
/// </summary>
/// <value>The installs.</value>
public int installs { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PackageInfo"/> class. /// Initializes a new instance of the <see cref="PackageInfo"/> class.
/// </summary> /// </summary>
public PackageInfo() public PackageInfo()
{ {
versions = Array.Empty<PackageVersionInfo>(); versions = Array.Empty<VersionInfo>();
} }
} }
} }

View File

@ -1,23 +0,0 @@
namespace MediaBrowser.Model.Updates
{
/// <summary>
/// Enum PackageType.
/// </summary>
public enum PackageTargetSystem
{
/// <summary>
/// Server.
/// </summary>
Server,
/// <summary>
/// MB Theater.
/// </summary>
MBTheater,
/// <summary>
/// MB Classic.
/// </summary>
MBClassic
}
}

View File

@ -1,23 +0,0 @@
namespace MediaBrowser.Model.Updates
{
/// <summary>
/// Enum PackageVersionClass.
/// </summary>
public enum PackageVersionClass
{
/// <summary>
/// The release.
/// </summary>
Release = 0,
/// <summary>
/// The beta.
/// </summary>
Beta = 1,
/// <summary>
/// The dev.
/// </summary>
Dev = 2
}
}

View File

@ -1,96 +0,0 @@
#pragma warning disable CS1591
using System;
using System.Text.Json.Serialization;
namespace MediaBrowser.Model.Updates
{
/// <summary>
/// Class PackageVersionInfo.
/// </summary>
public class PackageVersionInfo
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string name { get; set; }
/// <summary>
/// Gets or sets the guid.
/// </summary>
/// <value>The guid.</value>
public string guid { get; set; }
/// <summary>
/// Gets or sets the version STR.
/// </summary>
/// <value>The version STR.</value>
public string versionStr { get; set; }
/// <summary>
/// The _version
/// </summary>
private Version _version;
/// <summary>
/// Gets or sets the version.
/// Had to make this an interpreted property since Protobuf can't handle Version
/// </summary>
/// <value>The version.</value>
[JsonIgnore]
public Version Version
{
get
{
if (_version == null)
{
var ver = versionStr;
_version = new Version(string.IsNullOrEmpty(ver) ? "0.0.0.1" : ver);
}
return _version;
}
}
/// <summary>
/// Gets or sets the classification.
/// </summary>
/// <value>The classification.</value>
public PackageVersionClass classification { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
/// <value>The description.</value>
public string description { get; set; }
/// <summary>
/// Gets or sets the required version STR.
/// </summary>
/// <value>The required version STR.</value>
public string requiredVersionStr { get; set; }
/// <summary>
/// Gets or sets the source URL.
/// </summary>
/// <value>The source URL.</value>
public string sourceUrl { get; set; }
/// <summary>
/// Gets or sets the source URL.
/// </summary>
/// <value>The source URL.</value>
public string checksum { get; set; }
/// <summary>
/// Gets or sets the target filename.
/// </summary>
/// <value>The target filename.</value>
public string targetFilename { get; set; }
public string infoUrl { get; set; }
public string runtimes { get; set; }
}
}

View File

@ -0,0 +1,58 @@
using System;
namespace MediaBrowser.Model.Updates
{
/// <summary>
/// Class PackageVersionInfo.
/// </summary>
public class VersionInfo
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string name { get; set; }
/// <summary>
/// Gets or sets the guid.
/// </summary>
/// <value>The guid.</value>
public string guid { get; set; }
/// <summary>
/// Gets or sets the version.
/// </summary>
/// <value>The version.</value>
public Version version { get; set; }
/// <summary>
/// Gets or sets the changelog for this version.
/// </summary>
/// <value>The changelog.</value>
public string changelog { get; set; }
/// <summary>
/// Gets or sets the ABI that this version was built against.
/// </summary>
/// <value>The target ABI version.</value>
public string targetAbi { get; set; }
/// <summary>
/// Gets or sets the source URL.
/// </summary>
/// <value>The source URL.</value>
public string sourceUrl { get; set; }
/// <summary>
/// Gets or sets a checksum for the binary.
/// </summary>
/// <value>The checksum.</value>
public string checksum { get; set; }
/// <summary>
/// Gets or sets the target filename for the downloaded binary.
/// </summary>
/// <value>The target filename.</value>
public string filename { get; set; }
}
}

View File

@ -208,8 +208,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers
protected void ParseProviderLinks(T item, string xml) protected void ParseProviderLinks(T item, string xml)
{ {
// Look for a match for the Regex pattern "tt" followed by 7 digits // Look for a match for the Regex pattern "tt" followed by 7 or 8 digits
var m = Regex.Match(xml, @"tt([0-9]{7})", RegexOptions.IgnoreCase); var m = Regex.Match(xml, "tt([0-9]{7,8})", RegexOptions.IgnoreCase);
if (m.Success) if (m.Success)
{ {
item.SetProviderId(MetadataProviders.Imdb, m.Value); item.SetProviderId(MetadataProviders.Imdb, m.Value);

View File

@ -69,3 +69,99 @@ Most of the translations can be found in the web client but we have several othe
<a href="https://translate.jellyfin.org/engage/jellyfin/?utm_source=widget"> <a href="https://translate.jellyfin.org/engage/jellyfin/?utm_source=widget">
<img src="https://translate.jellyfin.org/widgets/jellyfin/-/jellyfin-web/multi-auto.svg" alt="Detailed Translation Status"/> <img src="https://translate.jellyfin.org/widgets/jellyfin/-/jellyfin-web/multi-auto.svg" alt="Detailed Translation Status"/>
</a> </a>
## Jellyfin Server
This repository contains the code for Jellyfin's backend server. Note that this is only one of many projects under the Jellyfin GitHub [organization](https://github.com/jellyfin/) on GitHub. If you want to contribute, you can start by checking out our [documentation](https://jellyfin.org/docs/general/contributing/index.html) to see what to work on.
## Server Development
These instructions will help you get set up with a local development environment in order to contribute to this repository. Before you start, please be sure to completely read our [guidelines on development contributions](https://jellyfin.org/docs/general/contributing/development.html). Note that this project is supported on all major operating systems except FreeBSD, which is still incompatible.
### Prerequisites
Before the project can be built, you must first install the [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download) on your system.
Instructions to run this project from the command line are included here, but you will also need to install an IDE if you want to debug the server while it is running. Any IDE that supports .NET Core development will work, but two options are recent versions of [Visual Studio](https://visualstudio.microsoft.com/downloads/) (at least 2017) and [Visual Studio Code](https://code.visualstudio.com/Download).
### Cloning the Repository
After dependencies are installed you will need to clone a local copy of this repository. If you just want to run the server from source you can clone this repository directly, but if you are intending to contribute code changes to the project, you should [set up your own fork](https://jellyfin.org/docs/general/contributing/development.html#set-up-your-copy-of-the-repo) of the repository. The following example shows how you can clone the repository directly over HTTPS.
```bash
git clone https://github.com/jellyfin/jellyfin.git
```
### Installing the Web Client
The server is configured to host the static files required for the [web client](https://github.com/jellyfin/jellyfin-web) in addition to serving the backend by default. Before you can run the server, you will need to get a copy of the web client since they are not included in this repository directly.
Note that it is also possible to [host the web client separately](#hosting-the-web-client-separately) from the web server with some additional configuration, in which case you can skip this step.
There are three options to get the files for the web client.
1. Download one of the finished builds from the [Azure DevOps pipeline](https://dev.azure.com/jellyfin-project/jellyfin/_build?definitionId=11). You can download the build for a specific release by looking at the [branches tab](https://dev.azure.com/jellyfin-project/jellyfin/_build?definitionId=11&_a=summary&repositoryFilter=6&view=branches) of the pipelines page.
2. Build them from source following the instructions on the [jellyfin-web repository](https://github.com/jellyfin/jellyfin-web)
3. Get the pre-built files from an existing installation of the server. For example, with a Windows server installation the client files are located at `C:\Program Files\Jellyfin\Server\jellyfin-web`
Once you have a copy of the built web client files, you need to copy them into a specific directory.
> `<repository root>/Mediabrowser.WebDashboard/jellyfin-web`
As part of the build process, this folder will be copied to the build output directory, where it can be accessed by the server.
### Running The Server
The following instructions will help you get the project up and running via the command line, or your preferred IDE.
#### Running With Visual Studio
To run the project with Visual Studio you can open the Solution (`.sln`) file and then press `F5` to run the server.
#### Running With Visual Studio Code
To run the project with Visual Studio Code you will first need to open the repository directory with Visual Studio Code using the `Open Folder...` option.
Second, you need to [install the recommended extensions for the workspace](https://code.visualstudio.com/docs/editor/extension-gallery#_recommended-extensions). Note that extension recommendations are classified as either "Workspace Recommendations" or "Other Recommendations", but only the "Workspace Recommendations" are required.
After the required extensions are installed, you can can run the server by pressing `F5`.
#### Running From The Command Line
To run the server from the command line you can use the `dotnet run` command. The example below shows how to do this if you have cloned the repository into a directory named `jellyfin` (the default directory name) and should work on all operating systems.
```bash
cd jellyfin # Move into the repository directory
dotnet run --project Jellyfin.Server # Run the server startup project
```
A second option is to build the project and then run the resulting executable file directly. When running the executable directly you can easily add command line options. Add the `--help` flag to list details on all the supported command line options.
1. Build the project
```bash
dotnet build # Build the project
cd bin/Debug/netcoreapp3.1 # Change into the build output directory
```
2. Execute the build output. On Linux, Mac, etc. use `./jellyfin` and on Windows use `jellyfin.exe`.
### Running The Tests
This repository also includes unit tests that are used to validate functionality as part of a CI pipeline on Azure. There are several ways to run these tests.
1. Run tests from the command line using `dotnet test`
2. Run tests in Visual Studio using the [Test Explorer](https://docs.microsoft.com/en-us/visualstudio/test/run-unit-tests-with-test-explorer)
3. Run individual tests in Visual Studio Code using the associated [CodeLens annotation](https://github.com/OmniSharp/omnisharp-vscode/wiki/How-to-run-and-debug-unit-tests)
### Advanced Configuration
The following sections describe some more advanced scenarios for running the server from source that build upon the standard instructions above.
#### Hosting The Web Client Separately
It is not necessary to host the frontend web client as part of the backend server. Hosting these two components separately may be useful for frontend developers who would prefer to host the client in a separate webpack development server for a tighter development loop. See the [jellyfin-web](https://github.com/jellyfin/jellyfin-web#getting-started) repo for instructions on how to do this.
To instruct the server not to host the web content, there is a `nowebcontent` configuration flag that must be set. This can specified using the command line switch `--nowebcontent` or the environment variable `JELLYFIN_NOWEBCONTENT=true`.
Since this is a common scenario, there is also a separate launch profile defined for Visual Studio called `Jellyfin.Server (nowebcontent)` that can be selected from the 'Start Debugging' dropdown in the main toolbar.