mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-05-24 02:02:29 -04:00
Merge remote-tracking branch 'upstream/master' into authenticationdb-efcore
This commit is contained in:
commit
ff9d14c811
3
.gitignore
vendored
3
.gitignore
vendored
@ -278,3 +278,6 @@ web/
|
|||||||
web-src.*
|
web-src.*
|
||||||
MediaBrowser.WebDashboard/jellyfin-web
|
MediaBrowser.WebDashboard/jellyfin-web
|
||||||
apiclient/generated
|
apiclient/generated
|
||||||
|
|
||||||
|
# Omnisharp crash logs
|
||||||
|
mono_crash.*.json
|
||||||
|
@ -212,4 +212,5 @@
|
|||||||
- [Tim Hobbs](https://github.com/timhobbs)
|
- [Tim Hobbs](https://github.com/timhobbs)
|
||||||
- [SvenVandenbrande](https://github.com/SvenVandenbrande)
|
- [SvenVandenbrande](https://github.com/SvenVandenbrande)
|
||||||
- [olsh](https://github.com/olsh)
|
- [olsh](https://github.com/olsh)
|
||||||
|
- [lbenini](https://github.com/lbenini)
|
||||||
- [gnuyent](https://github.com/gnuyent)
|
- [gnuyent](https://github.com/gnuyent)
|
||||||
|
@ -8,7 +8,7 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
|
|||||||
&& npm ci --no-audit --unsafe-perm \
|
&& npm ci --no-audit --unsafe-perm \
|
||||||
&& mv dist /dist
|
&& mv dist /dist
|
||||||
|
|
||||||
FROM debian:buster-slim as app
|
FROM debian:bullseye-slim as app
|
||||||
|
|
||||||
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
||||||
ARG DEBIAN_FRONTEND="noninteractive"
|
ARG DEBIAN_FRONTEND="noninteractive"
|
||||||
|
@ -14,7 +14,7 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
|
|||||||
&& mv dist /dist
|
&& mv dist /dist
|
||||||
|
|
||||||
FROM multiarch/qemu-user-static:x86_64-arm as qemu
|
FROM multiarch/qemu-user-static:x86_64-arm as qemu
|
||||||
FROM arm32v7/debian:buster-slim as app
|
FROM arm32v7/debian:bullseye-slim as app
|
||||||
|
|
||||||
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
||||||
ARG DEBIAN_FRONTEND="noninteractive"
|
ARG DEBIAN_FRONTEND="noninteractive"
|
||||||
|
@ -14,7 +14,7 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
|
|||||||
&& mv dist /dist
|
&& mv dist /dist
|
||||||
|
|
||||||
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
|
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
|
||||||
FROM arm64v8/debian:buster-slim as app
|
FROM arm64v8/debian:bullseye-slim as app
|
||||||
|
|
||||||
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
||||||
ARG DEBIAN_FRONTEND="noninteractive"
|
ARG DEBIAN_FRONTEND="noninteractive"
|
||||||
|
@ -17,7 +17,7 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
{
|
{
|
||||||
Item = item;
|
Item = item;
|
||||||
|
|
||||||
if (item is IItemByName && !(item is Folder))
|
if (item is IItemByName && item is not Folder)
|
||||||
{
|
{
|
||||||
StubType = Dlna.ContentDirectory.StubType.Folder;
|
StubType = Dlna.ContentDirectory.StubType.Folder;
|
||||||
}
|
}
|
||||||
|
@ -748,7 +748,7 @@ namespace Emby.Dlna.Didl
|
|||||||
AddValue(writer, "upnp", "publisher", studio, NsUpnp);
|
AddValue(writer, "upnp", "publisher", studio, NsUpnp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(item is Folder))
|
if (item is not Folder)
|
||||||
{
|
{
|
||||||
if (filter.Contains("dc:description"))
|
if (filter.Contains("dc:description"))
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@ -96,12 +93,14 @@ namespace Emby.Dlna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public DeviceProfile GetDefaultProfile()
|
public DeviceProfile GetDefaultProfile()
|
||||||
{
|
{
|
||||||
return new DefaultProfile();
|
return new DefaultProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeviceProfile GetProfile(DeviceIdentification deviceInfo)
|
/// <inheritdoc />
|
||||||
|
public DeviceProfile? GetProfile(DeviceIdentification deviceInfo)
|
||||||
{
|
{
|
||||||
if (deviceInfo == null)
|
if (deviceInfo == null)
|
||||||
{
|
{
|
||||||
@ -111,13 +110,13 @@ namespace Emby.Dlna
|
|||||||
var profile = GetProfiles()
|
var profile = GetProfiles()
|
||||||
.FirstOrDefault(i => i.Identification != null && IsMatch(deviceInfo, i.Identification));
|
.FirstOrDefault(i => i.Identification != null && IsMatch(deviceInfo, i.Identification));
|
||||||
|
|
||||||
if (profile != null)
|
if (profile == null)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Found matching device profile: {ProfileName}", profile.Name);
|
LogUnmatchedProfile(deviceInfo);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogUnmatchedProfile(deviceInfo);
|
_logger.LogDebug("Found matching device profile: {ProfileName}", profile.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return profile;
|
return profile;
|
||||||
@ -187,7 +186,8 @@ namespace Emby.Dlna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeviceProfile GetProfile(IHeaderDictionary headers)
|
/// <inheritdoc />
|
||||||
|
public DeviceProfile? GetProfile(IHeaderDictionary headers)
|
||||||
{
|
{
|
||||||
if (headers == null)
|
if (headers == null)
|
||||||
{
|
{
|
||||||
@ -195,15 +195,13 @@ namespace Emby.Dlna
|
|||||||
}
|
}
|
||||||
|
|
||||||
var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification));
|
var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification));
|
||||||
|
if (profile == null)
|
||||||
if (profile != null)
|
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Found matching device profile: {0}", profile.Name);
|
_logger.LogDebug("No matching device profile found. {@Headers}", headers);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var headerString = string.Join(", ", headers.Select(i => string.Format(CultureInfo.InvariantCulture, "{0}={1}", i.Key, i.Value)));
|
_logger.LogDebug("Found matching device profile: {0}", profile.Name);
|
||||||
_logger.LogDebug("No matching device profile found. {0}", headerString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return profile;
|
return profile;
|
||||||
@ -253,19 +251,19 @@ namespace Emby.Dlna
|
|||||||
return xmlFies
|
return xmlFies
|
||||||
.Select(i => ParseProfileFile(i, type))
|
.Select(i => ParseProfileFile(i, type))
|
||||||
.Where(i => i != null)
|
.Where(i => i != null)
|
||||||
.ToList();
|
.ToList()!; // We just filtered out all the nulls
|
||||||
}
|
}
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
{
|
{
|
||||||
return new List<DeviceProfile>();
|
return Array.Empty<DeviceProfile>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DeviceProfile ParseProfileFile(string path, DeviceProfileType type)
|
private DeviceProfile? ParseProfileFile(string path, DeviceProfileType type)
|
||||||
{
|
{
|
||||||
lock (_profiles)
|
lock (_profiles)
|
||||||
{
|
{
|
||||||
if (_profiles.TryGetValue(path, out Tuple<InternalProfileInfo, DeviceProfile> profileTuple))
|
if (_profiles.TryGetValue(path, out Tuple<InternalProfileInfo, DeviceProfile>? profileTuple))
|
||||||
{
|
{
|
||||||
return profileTuple.Item2;
|
return profileTuple.Item2;
|
||||||
}
|
}
|
||||||
@ -293,7 +291,8 @@ namespace Emby.Dlna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeviceProfile GetProfile(string id)
|
/// <inheritdoc />
|
||||||
|
public DeviceProfile? GetProfile(string id)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(id))
|
if (string.IsNullOrEmpty(id))
|
||||||
{
|
{
|
||||||
@ -322,6 +321,7 @@ namespace Emby.Dlna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public IEnumerable<DeviceProfileInfo> GetProfileInfos()
|
public IEnumerable<DeviceProfileInfo> GetProfileInfos()
|
||||||
{
|
{
|
||||||
return GetProfileInfosInternal().Select(i => i.Info);
|
return GetProfileInfosInternal().Select(i => i.Info);
|
||||||
@ -329,17 +329,14 @@ namespace Emby.Dlna
|
|||||||
|
|
||||||
private InternalProfileInfo GetInternalProfileInfo(FileSystemMetadata file, DeviceProfileType type)
|
private InternalProfileInfo GetInternalProfileInfo(FileSystemMetadata file, DeviceProfileType type)
|
||||||
{
|
{
|
||||||
return new InternalProfileInfo
|
return new InternalProfileInfo(
|
||||||
{
|
new DeviceProfileInfo
|
||||||
Path = file.FullName,
|
|
||||||
|
|
||||||
Info = new DeviceProfileInfo
|
|
||||||
{
|
{
|
||||||
Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture),
|
Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture),
|
||||||
Name = _fileSystem.GetFileNameWithoutExtension(file),
|
Name = _fileSystem.GetFileNameWithoutExtension(file),
|
||||||
Type = type
|
Type = type
|
||||||
}
|
},
|
||||||
};
|
file.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExtractSystemProfilesAsync()
|
private async Task ExtractSystemProfilesAsync()
|
||||||
@ -359,7 +356,8 @@ namespace Emby.Dlna
|
|||||||
systemProfilesPath,
|
systemProfilesPath,
|
||||||
Path.GetFileName(name.AsSpan()).Slice(namespaceName.Length));
|
Path.GetFileName(name.AsSpan()).Slice(namespaceName.Length));
|
||||||
|
|
||||||
using (var stream = _assembly.GetManifestResourceStream(name))
|
// The stream should exist as we just got its name from GetManifestResourceNames
|
||||||
|
using (var stream = _assembly.GetManifestResourceStream(name)!)
|
||||||
{
|
{
|
||||||
var fileInfo = _fileSystem.GetFileInfo(path);
|
var fileInfo = _fileSystem.GetFileInfo(path);
|
||||||
|
|
||||||
@ -380,6 +378,7 @@ namespace Emby.Dlna
|
|||||||
Directory.CreateDirectory(UserProfilesPath);
|
Directory.CreateDirectory(UserProfilesPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public void DeleteProfile(string id)
|
public void DeleteProfile(string id)
|
||||||
{
|
{
|
||||||
var info = GetProfileInfosInternal().First(i => string.Equals(id, i.Info.Id, StringComparison.OrdinalIgnoreCase));
|
var info = GetProfileInfosInternal().First(i => string.Equals(id, i.Info.Id, StringComparison.OrdinalIgnoreCase));
|
||||||
@ -397,6 +396,7 @@ namespace Emby.Dlna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public void CreateProfile(DeviceProfile profile)
|
public void CreateProfile(DeviceProfile profile)
|
||||||
{
|
{
|
||||||
profile = ReserializeProfile(profile);
|
profile = ReserializeProfile(profile);
|
||||||
@ -412,6 +412,7 @@ namespace Emby.Dlna
|
|||||||
SaveProfile(profile, path, DeviceProfileType.User);
|
SaveProfile(profile, path, DeviceProfileType.User);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public void UpdateProfile(DeviceProfile profile)
|
public void UpdateProfile(DeviceProfile profile)
|
||||||
{
|
{
|
||||||
profile = ReserializeProfile(profile);
|
profile = ReserializeProfile(profile);
|
||||||
@ -470,9 +471,11 @@ namespace Emby.Dlna
|
|||||||
|
|
||||||
var json = JsonSerializer.Serialize(profile, _jsonOptions);
|
var json = JsonSerializer.Serialize(profile, _jsonOptions);
|
||||||
|
|
||||||
return JsonSerializer.Deserialize<DeviceProfile>(json, _jsonOptions);
|
// Output can't be null if the input isn't null
|
||||||
|
return JsonSerializer.Deserialize<DeviceProfile>(json, _jsonOptions)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)
|
public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)
|
||||||
{
|
{
|
||||||
var profile = GetDefaultProfile();
|
var profile = GetDefaultProfile();
|
||||||
@ -482,6 +485,7 @@ namespace Emby.Dlna
|
|||||||
return new DescriptionXmlBuilder(profile, serverUuId, serverAddress, _appHost.FriendlyName, serverId).GetXml();
|
return new DescriptionXmlBuilder(profile, serverUuId, serverAddress, _appHost.FriendlyName, serverId).GetXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public ImageStream GetIcon(string filename)
|
public ImageStream GetIcon(string filename)
|
||||||
{
|
{
|
||||||
var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
|
var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
|
||||||
@ -499,9 +503,15 @@ namespace Emby.Dlna
|
|||||||
|
|
||||||
private class InternalProfileInfo
|
private class InternalProfileInfo
|
||||||
{
|
{
|
||||||
internal DeviceProfileInfo Info { get; set; }
|
internal InternalProfileInfo(DeviceProfileInfo info, string path)
|
||||||
|
{
|
||||||
|
Info = info;
|
||||||
|
Path = path;
|
||||||
|
}
|
||||||
|
|
||||||
internal string Path { get; set; }
|
internal DeviceProfileInfo Info { get; }
|
||||||
|
|
||||||
|
internal string Path { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,6 +453,7 @@ namespace Emby.Server.Implementations
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs the startup tasks.
|
/// Runs the startup tasks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns><see cref="Task" />.</returns>
|
/// <returns><see cref="Task" />.</returns>
|
||||||
public async Task RunStartupTasksAsync(CancellationToken cancellationToken)
|
public async Task RunStartupTasksAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@ -466,7 +467,7 @@ namespace Emby.Server.Implementations
|
|||||||
|
|
||||||
_mediaEncoder.SetFFmpegPath();
|
_mediaEncoder.SetFFmpegPath();
|
||||||
|
|
||||||
Logger.LogInformation("ServerId: {0}", SystemId);
|
Logger.LogInformation("ServerId: {ServerId}", SystemId);
|
||||||
|
|
||||||
var entryPoints = GetExports<IServerEntryPoint>();
|
var entryPoints = GetExports<IServerEntryPoint>();
|
||||||
|
|
||||||
@ -1089,7 +1090,6 @@ namespace Emby.Server.Implementations
|
|||||||
ServerName = FriendlyName,
|
ServerName = FriendlyName,
|
||||||
LocalAddress = GetSmartApiUrl(source),
|
LocalAddress = GetSmartApiUrl(source),
|
||||||
SupportsLibraryMonitor = true,
|
SupportsLibraryMonitor = true,
|
||||||
EncoderLocation = _mediaEncoder.EncoderLocation,
|
|
||||||
SystemArchitecture = RuntimeInformation.OSArchitecture,
|
SystemArchitecture = RuntimeInformation.OSArchitecture,
|
||||||
PackageName = _startupOptions.PackageName
|
PackageName = _startupOptions.PackageName
|
||||||
};
|
};
|
||||||
|
@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Channels
|
|||||||
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
|
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
|
||||||
var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id));
|
var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id));
|
||||||
|
|
||||||
return !(channel is IDisableMediaSourceDisplay);
|
return channel is not IDisableMediaSourceDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -880,7 +880,7 @@ namespace Emby.Server.Implementations.Channels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CacheResponse(object result, string path)
|
private async Task CacheResponse(ChannelItemResult result, string path)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -1079,11 +1079,11 @@ namespace Emby.Server.Implementations.Channels
|
|||||||
|
|
||||||
// was used for status
|
// was used for status
|
||||||
// if (!string.Equals(item.ExternalEtag ?? string.Empty, info.Etag ?? string.Empty, StringComparison.Ordinal))
|
// if (!string.Equals(item.ExternalEtag ?? string.Empty, info.Etag ?? string.Empty, StringComparison.Ordinal))
|
||||||
//{
|
// {
|
||||||
// item.ExternalEtag = info.Etag;
|
// item.ExternalEtag = info.Etag;
|
||||||
// forceUpdate = true;
|
// forceUpdate = true;
|
||||||
// _logger.LogDebug("Forcing update due to ExternalEtag {0}", item.Name);
|
// _logger.LogDebug("Forcing update due to ExternalEtag {0}", item.Name);
|
||||||
//}
|
// }
|
||||||
|
|
||||||
if (!internalChannelId.Equals(item.ChannelId))
|
if (!internalChannelId.Equals(item.ChannelId))
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@ -63,13 +61,13 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
|
public event EventHandler<CollectionCreatedEventArgs>? CollectionCreated;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
|
public event EventHandler<CollectionModifiedEventArgs>? ItemsAddedToCollection;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
|
public event EventHandler<CollectionModifiedEventArgs>? ItemsRemovedFromCollection;
|
||||||
|
|
||||||
private IEnumerable<Folder> FindFolders(string path)
|
private IEnumerable<Folder> FindFolders(string path)
|
||||||
{
|
{
|
||||||
@ -80,7 +78,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
.Where(i => _fileSystem.AreEqual(path, i.Path) || _fileSystem.ContainsSubPath(i.Path, path));
|
.Where(i => _fileSystem.AreEqual(path, i.Path) || _fileSystem.ContainsSubPath(i.Path, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<Folder> EnsureLibraryFolder(string path, bool createIfNeeded)
|
internal async Task<Folder?> EnsureLibraryFolder(string path, bool createIfNeeded)
|
||||||
{
|
{
|
||||||
var existingFolder = FindFolders(path).FirstOrDefault();
|
var existingFolder = FindFolders(path).FirstOrDefault();
|
||||||
if (existingFolder != null)
|
if (existingFolder != null)
|
||||||
@ -97,7 +95,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
|
|
||||||
var libraryOptions = new LibraryOptions
|
var libraryOptions = new LibraryOptions
|
||||||
{
|
{
|
||||||
PathInfos = new[] { new MediaPathInfo { Path = path } },
|
PathInfos = new[] { new MediaPathInfo(path) },
|
||||||
EnableRealtimeMonitor = false,
|
EnableRealtimeMonitor = false,
|
||||||
SaveLocalMetadata = true
|
SaveLocalMetadata = true
|
||||||
};
|
};
|
||||||
@ -114,7 +112,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
return Path.Combine(_appPaths.DataPath, "collections");
|
return Path.Combine(_appPaths.DataPath, "collections");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<Folder> GetCollectionsFolder(bool createIfNeeded)
|
private Task<Folder?> GetCollectionsFolder(bool createIfNeeded)
|
||||||
{
|
{
|
||||||
return EnsureLibraryFolder(GetCollectionsFolderPath(), createIfNeeded);
|
return EnsureLibraryFolder(GetCollectionsFolderPath(), createIfNeeded);
|
||||||
}
|
}
|
||||||
@ -203,8 +201,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
|
|
||||||
private async Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
|
private async Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
|
||||||
{
|
{
|
||||||
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
|
if (_libraryManager.GetItemById(collectionId) is not BoxSet collection)
|
||||||
if (collection == null)
|
|
||||||
{
|
{
|
||||||
throw new ArgumentException("No collection exists with the supplied Id");
|
throw new ArgumentException("No collection exists with the supplied Id");
|
||||||
}
|
}
|
||||||
@ -256,9 +253,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds)
|
public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds)
|
||||||
{
|
{
|
||||||
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
|
if (_libraryManager.GetItemById(collectionId) is not BoxSet collection)
|
||||||
|
|
||||||
if (collection == null)
|
|
||||||
{
|
{
|
||||||
throw new ArgumentException("No collection exists with the supplied Id");
|
throw new ArgumentException("No collection exists with the supplied Id");
|
||||||
}
|
}
|
||||||
@ -312,11 +307,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
|
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
if (item is not ISupportsBoxSetGrouping)
|
if (item is ISupportsBoxSetGrouping)
|
||||||
{
|
|
||||||
results[item.Id] = item;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
var itemId = item.Id;
|
var itemId = item.Id;
|
||||||
|
|
||||||
@ -340,6 +331,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
var alreadyInResults = false;
|
var alreadyInResults = false;
|
||||||
|
|
||||||
// this is kind of a performance hack because only Video has alternate versions that should be in a box set?
|
// this is kind of a performance hack because only Video has alternate versions that should be in a box set?
|
||||||
if (item is Video video)
|
if (item is Video video)
|
||||||
{
|
{
|
||||||
@ -355,11 +347,13 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!alreadyInResults)
|
if (alreadyInResults)
|
||||||
{
|
{
|
||||||
results[itemId] = item;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
results[item.Id] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
return results.Values;
|
return results.Values;
|
||||||
|
@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.Data
|
|||||||
protected virtual int? CacheSize => null;
|
protected virtual int? CacheSize => null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the journal mode. <see href="https://www.sqlite.org/pragma.html#pragma_journal_mode" />
|
/// Gets the journal mode. <see href="https://www.sqlite.org/pragma.html#pragma_journal_mode" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The journal mode.</value>
|
/// <value>The journal mode.</value>
|
||||||
protected virtual string JournalMode => "TRUNCATE";
|
protected virtual string JournalMode => "TRUNCATE";
|
||||||
|
@ -75,6 +75,12 @@ namespace Emby.Server.Implementations.Data
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
|
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
||||||
|
/// <param name="appHost">Instance of the <see cref="IServerApplicationHost"/> interface.</param>
|
||||||
|
/// <param name="logger">Instance of the <see cref="ILogger{SqliteItemRepository}"/> interface.</param>
|
||||||
|
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
|
||||||
|
/// <param name="imageProcessor">Instance of the <see cref="IImageProcessor"/> interface.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">config is null.</exception>
|
||||||
public SqliteItemRepository(
|
public SqliteItemRepository(
|
||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
IServerApplicationHost appHost,
|
IServerApplicationHost appHost,
|
||||||
@ -1135,15 +1141,25 @@ namespace Emby.Server.Implementations.Data
|
|||||||
Path = RestorePath(path.ToString())
|
Path = RestorePath(path.ToString())
|
||||||
};
|
};
|
||||||
|
|
||||||
if (long.TryParse(dateModified, NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks))
|
if (long.TryParse(dateModified, NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks)
|
||||||
|
&& ticks >= DateTime.MinValue.Ticks
|
||||||
|
&& ticks <= DateTime.MaxValue.Ticks)
|
||||||
{
|
{
|
||||||
image.DateModified = new DateTime(ticks, DateTimeKind.Utc);
|
image.DateModified = new DateTime(ticks, DateTimeKind.Utc);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (Enum.TryParse(imageType.ToString(), true, out ImageType type))
|
if (Enum.TryParse(imageType.ToString(), true, out ImageType type))
|
||||||
{
|
{
|
||||||
image.Type = type;
|
image.Type = type;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Optional parameters: width*height*blurhash
|
// Optional parameters: width*height*blurhash
|
||||||
if (nextSegment + 1 < value.Length - 1)
|
if (nextSegment + 1 < value.Length - 1)
|
||||||
@ -4879,7 +4895,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||||||
|
|
||||||
foreach (var t in _knownTypes)
|
foreach (var t in _knownTypes)
|
||||||
{
|
{
|
||||||
dict[t.Name] = t.FullName ;
|
dict[t.Name] = t.FullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
dict["Program"] = typeof(LiveTvProgram).FullName;
|
dict["Program"] = typeof(LiveTvProgram).FullName;
|
||||||
|
@ -174,7 +174,6 @@ namespace Emby.Server.Implementations.Data
|
|||||||
/// <param name="key">The key.</param>
|
/// <param name="key">The key.</param>
|
||||||
/// <param name="userData">The user data.</param>
|
/// <param name="userData">The user data.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
|
||||||
public void PersistUserData(long internalUserId, string key, UserItemData userData, CancellationToken cancellationToken)
|
public void PersistUserData(long internalUserId, string key, UserItemData userData, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
@ -319,8 +318,8 @@ namespace Emby.Server.Implementations.Data
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return all user-data associated with the given user.
|
/// Return all user-data associated with the given user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="internalUserId"></param>
|
/// <param name="internalUserId">The internal user id.</param>
|
||||||
/// <returns></returns>
|
/// <returns>The list of user item data.</returns>
|
||||||
public List<UserItemData> GetAllUserData(long internalUserId)
|
public List<UserItemData> GetAllUserData(long internalUserId)
|
||||||
{
|
{
|
||||||
if (internalUserId <= 0)
|
if (internalUserId <= 0)
|
||||||
@ -349,7 +348,8 @@ namespace Emby.Server.Implementations.Data
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a row from the specified reader into the provided userData object.
|
/// Read a row from the specified reader into the provided userData object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="reader"></param>
|
/// <param name="reader">The list of result set values.</param>
|
||||||
|
/// <returns>The user item data.</returns>
|
||||||
private UserItemData ReadRow(IReadOnlyList<ResultSetValue> reader)
|
private UserItemData ReadRow(IReadOnlyList<ResultSetValue> reader)
|
||||||
{
|
{
|
||||||
var userData = new UserItemData();
|
var userData = new UserItemData();
|
||||||
|
@ -807,7 +807,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
|
|
||||||
dto.MediaType = item.MediaType;
|
dto.MediaType = item.MediaType;
|
||||||
|
|
||||||
if (!(item is LiveTvProgram))
|
if (item is not LiveTvProgram)
|
||||||
{
|
{
|
||||||
dto.LocationType = item.LocationType;
|
dto.LocationType = item.LocationType;
|
||||||
}
|
}
|
||||||
@ -928,9 +928,9 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if (options.ContainsField(ItemFields.MediaSourceCount))
|
// if (options.ContainsField(ItemFields.MediaSourceCount))
|
||||||
//{
|
// {
|
||||||
// Songs always have one
|
// Songs always have one
|
||||||
//}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item is IHasArtist hasArtist)
|
if (item is IHasArtist hasArtist)
|
||||||
@ -938,10 +938,10 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
dto.Artists = hasArtist.Artists;
|
dto.Artists = hasArtist.Artists;
|
||||||
|
|
||||||
// var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
|
// var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
|
||||||
//{
|
// {
|
||||||
// EnableTotalRecordCount = false,
|
// EnableTotalRecordCount = false,
|
||||||
// ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
|
// ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
|
||||||
//});
|
// });
|
||||||
|
|
||||||
// dto.ArtistItems = artistItems.Items
|
// dto.ArtistItems = artistItems.Items
|
||||||
// .Select(i =>
|
// .Select(i =>
|
||||||
@ -958,7 +958,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
// Include artists that are not in the database yet, e.g., just added via metadata editor
|
// Include artists that are not in the database yet, e.g., just added via metadata editor
|
||||||
// var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
|
// var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
|
||||||
dto.ArtistItems = hasArtist.Artists
|
dto.ArtistItems = hasArtist.Artists
|
||||||
//.Except(foundArtists, new DistinctNameComparer())
|
// .Except(foundArtists, new DistinctNameComparer())
|
||||||
.Select(i =>
|
.Select(i =>
|
||||||
{
|
{
|
||||||
// This should not be necessary but we're seeing some cases of it
|
// This should not be necessary but we're seeing some cases of it
|
||||||
@ -990,10 +990,10 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
dto.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
|
dto.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
|
||||||
|
|
||||||
// var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
|
// var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
|
||||||
//{
|
// {
|
||||||
// EnableTotalRecordCount = false,
|
// EnableTotalRecordCount = false,
|
||||||
// ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
|
// ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
|
||||||
//});
|
// });
|
||||||
|
|
||||||
// dto.AlbumArtists = artistItems.Items
|
// dto.AlbumArtists = artistItems.Items
|
||||||
// .Select(i =>
|
// .Select(i =>
|
||||||
@ -1008,7 +1008,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
// .ToList();
|
// .ToList();
|
||||||
|
|
||||||
dto.AlbumArtists = hasAlbumArtist.AlbumArtists
|
dto.AlbumArtists = hasAlbumArtist.AlbumArtists
|
||||||
//.Except(foundArtists, new DistinctNameComparer())
|
// .Except(foundArtists, new DistinctNameComparer())
|
||||||
.Select(i =>
|
.Select(i =>
|
||||||
{
|
{
|
||||||
// This should not be necessary but we're seeing some cases of it
|
// This should not be necessary but we're seeing some cases of it
|
||||||
@ -1035,8 +1035,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add video info
|
// Add video info
|
||||||
var video = item as Video;
|
if (item is Video video)
|
||||||
if (video != null)
|
|
||||||
{
|
{
|
||||||
dto.VideoType = video.VideoType;
|
dto.VideoType = video.VideoType;
|
||||||
dto.Video3DFormat = video.Video3DFormat;
|
dto.Video3DFormat = video.Video3DFormat;
|
||||||
@ -1075,9 +1074,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
if (options.ContainsField(ItemFields.MediaStreams))
|
if (options.ContainsField(ItemFields.MediaStreams))
|
||||||
{
|
{
|
||||||
// Add VideoInfo
|
// Add VideoInfo
|
||||||
var iHasMediaSources = item as IHasMediaSources;
|
if (item is IHasMediaSources)
|
||||||
|
|
||||||
if (iHasMediaSources != null)
|
|
||||||
{
|
{
|
||||||
MediaStream[] mediaStreams;
|
MediaStream[] mediaStreams;
|
||||||
|
|
||||||
@ -1146,7 +1143,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
// TODO maybe remove the if statement entirely
|
// TODO maybe remove the if statement entirely
|
||||||
// if (options.ContainsField(ItemFields.SeriesPrimaryImage))
|
// if (options.ContainsField(ItemFields.SeriesPrimaryImage))
|
||||||
{
|
{
|
||||||
episodeSeries = episodeSeries ?? episode.Series;
|
episodeSeries ??= episode.Series;
|
||||||
if (episodeSeries != null)
|
if (episodeSeries != null)
|
||||||
{
|
{
|
||||||
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary);
|
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary);
|
||||||
@ -1159,7 +1156,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
|
|
||||||
if (options.ContainsField(ItemFields.SeriesStudio))
|
if (options.ContainsField(ItemFields.SeriesStudio))
|
||||||
{
|
{
|
||||||
episodeSeries = episodeSeries ?? episode.Series;
|
episodeSeries ??= episode.Series;
|
||||||
if (episodeSeries != null)
|
if (episodeSeries != null)
|
||||||
{
|
{
|
||||||
dto.SeriesStudio = episodeSeries.Studios.FirstOrDefault();
|
dto.SeriesStudio = episodeSeries.Studios.FirstOrDefault();
|
||||||
@ -1172,7 +1169,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
{
|
{
|
||||||
dto.AirDays = series.AirDays;
|
dto.AirDays = series.AirDays;
|
||||||
dto.AirTime = series.AirTime;
|
dto.AirTime = series.AirTime;
|
||||||
dto.Status = series.Status.HasValue ? series.Status.Value.ToString() : null;
|
dto.Status = series.Status?.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add SeasonInfo
|
// Add SeasonInfo
|
||||||
@ -1185,7 +1182,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
|
|
||||||
if (options.ContainsField(ItemFields.SeriesStudio))
|
if (options.ContainsField(ItemFields.SeriesStudio))
|
||||||
{
|
{
|
||||||
series = series ?? season.Series;
|
series ??= season.Series;
|
||||||
if (series != null)
|
if (series != null)
|
||||||
{
|
{
|
||||||
dto.SeriesStudio = series.Studios.FirstOrDefault();
|
dto.SeriesStudio = series.Studios.FirstOrDefault();
|
||||||
@ -1196,7 +1193,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
// TODO maybe remove the if statement entirely
|
// TODO maybe remove the if statement entirely
|
||||||
// if (options.ContainsField(ItemFields.SeriesPrimaryImage))
|
// if (options.ContainsField(ItemFields.SeriesPrimaryImage))
|
||||||
{
|
{
|
||||||
series = series ?? season.Series;
|
series ??= season.Series;
|
||||||
if (series != null)
|
if (series != null)
|
||||||
{
|
{
|
||||||
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary);
|
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary);
|
||||||
@ -1283,7 +1280,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
|
|
||||||
var parent = currentItem.DisplayParent ?? currentItem.GetOwner() ?? currentItem.GetParent();
|
var parent = currentItem.DisplayParent ?? currentItem.GetOwner() ?? currentItem.GetParent();
|
||||||
|
|
||||||
if (parent == null && !(originalItem is UserRootFolder) && !(originalItem is UserView) && !(originalItem is AggregateFolder) && !(originalItem is ICollectionFolder) && !(originalItem is Channel))
|
if (parent == null && originalItem is not UserRootFolder && originalItem is not UserView && originalItem is not AggregateFolder && originalItem is not ICollectionFolder && originalItem is not Channel)
|
||||||
{
|
{
|
||||||
parent = _libraryManager.GetCollectionFolders(originalItem).FirstOrDefault();
|
parent = _libraryManager.GetCollectionFolders(originalItem).FirstOrDefault();
|
||||||
}
|
}
|
||||||
@ -1317,7 +1314,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
var imageTags = dto.ImageTags;
|
var imageTags = dto.ImageTags;
|
||||||
|
|
||||||
while (((!(imageTags != null && imageTags.ContainsKey(ImageType.Logo)) && logoLimit > 0) || (!(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && artLimit > 0) || (!(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && thumbLimit > 0) || parent is Series) &&
|
while (((!(imageTags != null && imageTags.ContainsKey(ImageType.Logo)) && logoLimit > 0) || (!(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && artLimit > 0) || (!(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && thumbLimit > 0) || parent is Series) &&
|
||||||
(parent = parent ?? (isFirst ? GetImageDisplayParent(item, item) ?? owner : parent)) != null)
|
(parent ??= (isFirst ? GetImageDisplayParent(item, item) ?? owner : parent)) != null)
|
||||||
{
|
{
|
||||||
if (parent == null)
|
if (parent == null)
|
||||||
{
|
{
|
||||||
@ -1348,7 +1345,7 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && !(parent is ICollectionFolder) && !(parent is UserView))
|
if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && parent is not ICollectionFolder && parent is not UserView)
|
||||||
{
|
{
|
||||||
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Thumb);
|
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Thumb);
|
||||||
|
|
||||||
|
@ -23,14 +23,15 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="DiscUtils.Udf" Version="0.16.4" />
|
||||||
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
|
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.9" />
|
||||||
<PackageReference Include="Mono.Nat" Version="3.0.1" />
|
<PackageReference Include="Mono.Nat" Version="3.0.1" />
|
||||||
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.1.0" />
|
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.0" />
|
||||||
<PackageReference Include="sharpcompress" Version="0.28.3" />
|
<PackageReference Include="sharpcompress" Version="0.28.3" />
|
||||||
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
|
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
|
||||||
<PackageReference Include="DotNet.Glob" Version="3.1.2" />
|
<PackageReference Include="DotNet.Glob" Version="3.1.2" />
|
||||||
|
@ -149,7 +149,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||||||
|
|
||||||
private static bool EnableRefreshMessage(BaseItem item)
|
private static bool EnableRefreshMessage(BaseItem item)
|
||||||
{
|
{
|
||||||
if (!(item is Folder folder))
|
if (item is not Folder folder)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -403,7 +403,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item is IItemByName && !(item is MusicArtist))
|
if (item is IItemByName && item is not MusicArtist)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,9 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="UdpServerEntryPoint" /> class.
|
/// Initializes a new instance of the <see cref="UdpServerEntryPoint" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="logger">Instance of the <see cref="ILogger{UdpServerEntryPoint}"/> interface.</param>
|
||||||
|
/// <param name="appHost">Instance of the <see cref="IServerApplicationHost"/> interface.</param>
|
||||||
|
/// <param name="configuration">Instance of the <see cref="IConfiguration"/> interface.</param>
|
||||||
public UdpServerEntryPoint(
|
public UdpServerEntryPoint(
|
||||||
ILogger<UdpServerEntryPoint> logger,
|
ILogger<UdpServerEntryPoint> logger,
|
||||||
IServerApplicationHost appHost,
|
IServerApplicationHost appHost,
|
||||||
|
@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
public event EventHandler<EventArgs>? Closed;
|
public event EventHandler<EventArgs>? Closed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the remote end point.
|
/// Gets the remote end point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPAddress? RemoteEndPoint { get; }
|
public IPAddress? RemoteEndPoint { get; }
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
public DateTime LastKeepAliveDate { get; set; }
|
public DateTime LastKeepAliveDate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the query string.
|
/// Gets the query string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The query string.</value>
|
/// <value>The query string.</value>
|
||||||
public IQueryCollection QueryString { get; }
|
public IQueryCollection QueryString { get; }
|
||||||
|
@ -10,7 +10,7 @@ namespace Emby.Server.Implementations
|
|||||||
string? FFmpegPath { get; }
|
string? FFmpegPath { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the value of the --service command line option.
|
/// Gets a value value indicating whether to run as service by the --service command line option.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsService { get; }
|
bool IsService { get; }
|
||||||
|
|
||||||
|
@ -30,27 +30,27 @@ namespace Emby.Server.Implementations.Images
|
|||||||
|
|
||||||
string[] includeItemTypes;
|
string[] includeItemTypes;
|
||||||
|
|
||||||
if (string.Equals(viewType, CollectionType.Movies))
|
if (string.Equals(viewType, CollectionType.Movies, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
includeItemTypes = new string[] { "Movie" };
|
includeItemTypes = new string[] { "Movie" };
|
||||||
}
|
}
|
||||||
else if (string.Equals(viewType, CollectionType.TvShows))
|
else if (string.Equals(viewType, CollectionType.TvShows, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
includeItemTypes = new string[] { "Series" };
|
includeItemTypes = new string[] { "Series" };
|
||||||
}
|
}
|
||||||
else if (string.Equals(viewType, CollectionType.Music))
|
else if (string.Equals(viewType, CollectionType.Music, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
includeItemTypes = new string[] { "MusicAlbum" };
|
includeItemTypes = new string[] { "MusicAlbum" };
|
||||||
}
|
}
|
||||||
else if (string.Equals(viewType, CollectionType.Books))
|
else if (string.Equals(viewType, CollectionType.Books, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
includeItemTypes = new string[] { "Book", "AudioBook" };
|
includeItemTypes = new string[] { "Book", "AudioBook" };
|
||||||
}
|
}
|
||||||
else if (string.Equals(viewType, CollectionType.BoxSets))
|
else if (string.Equals(viewType, CollectionType.BoxSets, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
includeItemTypes = new string[] { "BoxSet" };
|
includeItemTypes = new string[] { "BoxSet" };
|
||||||
}
|
}
|
||||||
else if (string.Equals(viewType, CollectionType.HomeVideos) || string.Equals(viewType, CollectionType.Photos))
|
else if (string.Equals(viewType, CollectionType.HomeVideos, StringComparison.Ordinal) || string.Equals(viewType, CollectionType.Photos, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
includeItemTypes = new string[] { "Video", "Photo" };
|
includeItemTypes = new string[] { "Video", "Photo" };
|
||||||
}
|
}
|
||||||
|
@ -287,14 +287,14 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
if (item is IItemByName)
|
if (item is IItemByName)
|
||||||
{
|
{
|
||||||
if (!(item is MusicArtist))
|
if (item is not MusicArtist)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!item.IsFolder)
|
else if (!item.IsFolder)
|
||||||
{
|
{
|
||||||
if (!(item is Video) && !(item is LiveTvChannel))
|
if (item is not Video && item is not LiveTvChannel)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -866,7 +866,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
{
|
{
|
||||||
var path = Person.GetPath(name);
|
var path = Person.GetPath(name);
|
||||||
var id = GetItemByNameId<Person>(path);
|
var id = GetItemByNameId<Person>(path);
|
||||||
if (!(GetItemById(id) is Person item))
|
if (GetItemById(id) is not Person item)
|
||||||
{
|
{
|
||||||
item = new Person
|
item = new Person
|
||||||
{
|
{
|
||||||
@ -2118,7 +2118,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
|
|
||||||
public LibraryOptions GetLibraryOptions(BaseItem item)
|
public LibraryOptions GetLibraryOptions(BaseItem item)
|
||||||
{
|
{
|
||||||
if (!(item is CollectionFolder collectionFolder))
|
if (item is not CollectionFolder collectionFolder)
|
||||||
{
|
{
|
||||||
// List.Find is more performant than FirstOrDefault due to enumerator allocation
|
// List.Find is more performant than FirstOrDefault due to enumerator allocation
|
||||||
collectionFolder = GetCollectionFolders(item)
|
collectionFolder = GetCollectionFolders(item)
|
||||||
@ -3173,10 +3173,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
{
|
{
|
||||||
if (!list.Any(i => string.Equals(i.Path, location, StringComparison.Ordinal)))
|
if (!list.Any(i => string.Equals(i.Path, location, StringComparison.Ordinal)))
|
||||||
{
|
{
|
||||||
list.Add(new MediaPathInfo
|
list.Add(new MediaPathInfo(location));
|
||||||
{
|
|
||||||
Path = location
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,11 +21,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class AudioResolver : ItemResolver<MediaBrowser.Controller.Entities.Audio.Audio>, IMultiItemResolver
|
public class AudioResolver : ItemResolver<MediaBrowser.Controller.Entities.Audio.Audio>, IMultiItemResolver
|
||||||
{
|
{
|
||||||
private readonly ILibraryManager LibraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
|
||||||
public AudioResolver(ILibraryManager libraryManager)
|
public AudioResolver(ILibraryManager libraryManager)
|
||||||
{
|
{
|
||||||
LibraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -88,13 +88,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
|||||||
}
|
}
|
||||||
|
|
||||||
var files = args.FileSystemChildren
|
var files = args.FileSystemChildren
|
||||||
.Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
|
.Where(i => !_libraryManager.IgnoreFile(i, args.Parent))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
return FindAudio<AudioBook>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
|
return FindAudio<AudioBook>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LibraryManager.IsAudioFile(args.Path))
|
if (_libraryManager.IsAudioFile(args.Path))
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(args.Path);
|
var extension = Path.GetExtension(args.Path);
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
|||||||
var isMixedCollectionType = string.IsNullOrEmpty(collectionType);
|
var isMixedCollectionType = string.IsNullOrEmpty(collectionType);
|
||||||
|
|
||||||
// For conflicting extensions, give priority to videos
|
// For conflicting extensions, give priority to videos
|
||||||
if (isMixedCollectionType && LibraryManager.IsVideoFile(args.Path))
|
if (isMixedCollectionType && _libraryManager.IsVideoFile(args.Path))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -182,7 +182,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
|
var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
|
||||||
|
|
||||||
var resolver = new AudioBookListResolver(namingOptions);
|
var resolver = new AudioBookListResolver(namingOptions);
|
||||||
var resolverResult = resolver.Resolve(files).ToList();
|
var resolverResult = resolver.Resolve(files).ToList();
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using DiscUtils.Udf;
|
||||||
using Emby.Naming.Video;
|
using Emby.Naming.Video;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
@ -16,7 +17,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves a Path into a Video or Video subclass.
|
/// Resolves a Path into a Video or Video subclass.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T">The type of item to resolve.</typeparam>
|
||||||
public abstract class BaseVideoResolver<T> : MediaBrowser.Controller.Resolvers.ItemResolver<T>
|
public abstract class BaseVideoResolver<T> : MediaBrowser.Controller.Resolvers.ItemResolver<T>
|
||||||
where T : Video, new()
|
where T : Video, new()
|
||||||
{
|
{
|
||||||
@ -80,7 +81,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsBluRayDirectory(child.FullName, filename, args.DirectoryService))
|
if (IsBluRayDirectory(filename))
|
||||||
{
|
{
|
||||||
videoInfo = VideoResolver.ResolveDirectory(args.Path, namingOptions);
|
videoInfo = VideoResolver.ResolveDirectory(args.Path, namingOptions);
|
||||||
|
|
||||||
@ -201,6 +202,22 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
|||||||
{
|
{
|
||||||
video.IsoType = IsoType.BluRay;
|
video.IsoType = IsoType.BluRay;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// use disc-utils, both DVDs and BDs use UDF filesystem
|
||||||
|
using (var videoFileStream = File.Open(video.Path, FileMode.Open, FileAccess.Read))
|
||||||
|
{
|
||||||
|
UdfReader udfReader = new UdfReader(videoFileStream);
|
||||||
|
if (udfReader.DirectoryExists("VIDEO_TS"))
|
||||||
|
{
|
||||||
|
video.IsoType = IsoType.Dvd;
|
||||||
|
}
|
||||||
|
else if (udfReader.DirectoryExists("BDMV"))
|
||||||
|
{
|
||||||
|
video.IsoType = IsoType.BluRay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,25 +296,13 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether [is blu ray directory] [the specified directory name].
|
/// Determines whether [is bluray directory] [the specified directory name].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool IsBluRayDirectory(string fullPath, string directoryName, IDirectoryService directoryService)
|
/// <param name="directoryName">The directory name.</param>
|
||||||
|
/// <returns>Whether the directory is a bluray directory.</returns>
|
||||||
|
protected bool IsBluRayDirectory(string directoryName)
|
||||||
{
|
{
|
||||||
if (!string.Equals(directoryName, "bdmv", StringComparison.OrdinalIgnoreCase))
|
return string.Equals(directoryName, "bdmv", StringComparison.OrdinalIgnoreCase);
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
// var blurayExtensions = new[]
|
|
||||||
//{
|
|
||||||
// ".mts",
|
|
||||||
// ".m2ts",
|
|
||||||
// ".bdmv",
|
|
||||||
// ".mpls"
|
|
||||||
//};
|
|
||||||
|
|
||||||
// return directoryService.GetFiles(fullPath).Any(i => blurayExtensions.Contains(i.Extension ?? string.Empty, StringComparer.OrdinalIgnoreCase));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class ItemResolver.
|
/// Class ItemResolver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T">The type of BaseItem.</typeparam>
|
||||||
public abstract class ItemResolver<T> : IItemResolver
|
public abstract class ItemResolver<T> : IItemResolver
|
||||||
where T : BaseItem, new()
|
where T : BaseItem, new()
|
||||||
{
|
{
|
||||||
|
@ -400,7 +400,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
|
|||||||
return movie;
|
return movie;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsBluRayDirectory(child.FullName, filename, directoryService))
|
if (IsBluRayDirectory(filename))
|
||||||
{
|
{
|
||||||
var movie = new T
|
var movie = new T
|
||||||
{
|
{
|
||||||
@ -481,7 +481,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subfolders.Any(s => IsBluRayDirectory(s.FullName, s.Name, directoryService)))
|
if (subfolders.Any(s => IsBluRayDirectory(s.Name)))
|
||||||
{
|
{
|
||||||
videoTypes.Add(VideoType.BluRay);
|
videoTypes.Add(VideoType.BluRay);
|
||||||
return true;
|
return true;
|
||||||
|
@ -18,7 +18,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class PlaylistResolver : FolderResolver<Playlist>
|
public class PlaylistResolver : FolderResolver<Playlist>
|
||||||
{
|
{
|
||||||
private string[] _musicPlaylistCollectionTypes = new string[] {
|
private string[] _musicPlaylistCollectionTypes =
|
||||||
|
{
|
||||||
string.Empty,
|
string.Empty,
|
||||||
CollectionType.Music
|
CollectionType.Music
|
||||||
};
|
};
|
||||||
|
@ -87,12 +87,15 @@ namespace Emby.Server.Implementations.Library.Validators
|
|||||||
|
|
||||||
foreach (var item in deadEntities)
|
foreach (var item in deadEntities)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name);
|
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
|
||||||
|
|
||||||
_libraryManager.DeleteItem(item, new DeleteOptions
|
_libraryManager.DeleteItem(
|
||||||
{
|
item,
|
||||||
DeleteFileLocation = false
|
new DeleteOptions
|
||||||
}, false);
|
{
|
||||||
|
DeleteFileLocation = false
|
||||||
|
},
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
progress.Report(100);
|
progress.Report(100);
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@ -33,7 +31,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
return targetFile;
|
return targetFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
|
public Task Record(IDirectStreamProvider? directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (directStreamProvider != null)
|
if (directStreamProvider != null)
|
||||||
{
|
{
|
||||||
@ -45,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
private async Task RecordFromDirectStreamProvider(IDirectStreamProvider directStreamProvider, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
|
private async Task RecordFromDirectStreamProvider(IDirectStreamProvider directStreamProvider, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
|
Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None))
|
using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||||
@ -71,7 +69,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
_logger.LogInformation("Opened recording stream from tuner provider");
|
_logger.LogInformation("Opened recording stream from tuner provider");
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
|
Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
||||||
await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None);
|
await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
|
@ -159,8 +159,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var recordingFolders = GetRecordingFolders().ToArray();
|
var recordingFolders = GetRecordingFolders().ToArray();
|
||||||
var virtualFolders = _libraryManager.GetVirtualFolders()
|
var virtualFolders = _libraryManager.GetVirtualFolders();
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var allExistingPaths = virtualFolders.SelectMany(i => i.Locations).ToList();
|
var allExistingPaths = virtualFolders.SelectMany(i => i.Locations).ToList();
|
||||||
|
|
||||||
@ -177,7 +176,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mediaPathInfos = pathsToCreate.Select(i => new MediaPathInfo { Path = i }).ToArray();
|
var mediaPathInfos = pathsToCreate.Select(i => new MediaPathInfo(i)).ToArray();
|
||||||
|
|
||||||
var libraryOptions = new LibraryOptions
|
var libraryOptions = new LibraryOptions
|
||||||
{
|
{
|
||||||
@ -210,7 +209,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
foreach (var path in pathsToRemove)
|
foreach (var path in pathsToRemove)
|
||||||
{
|
{
|
||||||
await RemovePathFromLibrary(path).ConfigureAwait(false);
|
await RemovePathFromLibraryAsync(path).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -219,13 +218,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RemovePathFromLibrary(string path)
|
private async Task RemovePathFromLibraryAsync(string path)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Removing path from library: {0}", path);
|
_logger.LogDebug("Removing path from library: {0}", path);
|
||||||
|
|
||||||
var requiresRefresh = false;
|
var requiresRefresh = false;
|
||||||
var virtualFolders = _libraryManager.GetVirtualFolders()
|
var virtualFolders = _libraryManager.GetVirtualFolders();
|
||||||
.ToList();
|
|
||||||
|
|
||||||
foreach (var virtualFolder in virtualFolders)
|
foreach (var virtualFolder in virtualFolders)
|
||||||
{
|
{
|
||||||
@ -460,7 +458,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
if (!string.IsNullOrWhiteSpace(tunerChannel.TunerChannelId))
|
if (!string.IsNullOrWhiteSpace(tunerChannel.TunerChannelId))
|
||||||
{
|
{
|
||||||
var tunerChannelId = tunerChannel.TunerChannelId;
|
var tunerChannelId = tunerChannel.TunerChannelId;
|
||||||
if (tunerChannelId.IndexOf(".json.schedulesdirect.org", StringComparison.OrdinalIgnoreCase) != -1)
|
if (tunerChannelId.Contains(".json.schedulesdirect.org", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
tunerChannelId = tunerChannelId.Replace(".json.schedulesdirect.org", string.Empty, StringComparison.OrdinalIgnoreCase).TrimStart('I');
|
tunerChannelId = tunerChannelId.Replace(".json.schedulesdirect.org", string.Empty, StringComparison.OrdinalIgnoreCase).TrimStart('I');
|
||||||
}
|
}
|
||||||
@ -620,8 +618,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
if (existingTimer != null)
|
if (existingTimer != null)
|
||||||
{
|
{
|
||||||
if (existingTimer.Status == RecordingStatus.Cancelled ||
|
if (existingTimer.Status == RecordingStatus.Cancelled
|
||||||
existingTimer.Status == RecordingStatus.Completed)
|
|| existingTimer.Status == RecordingStatus.Completed)
|
||||||
{
|
{
|
||||||
existingTimer.Status = RecordingStatus.New;
|
existingTimer.Status = RecordingStatus.New;
|
||||||
existingTimer.IsManual = true;
|
existingTimer.IsManual = true;
|
||||||
@ -913,18 +911,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
var epgChannel = await GetEpgChannelFromTunerChannel(provider.Item1, provider.Item2, channel, cancellationToken).ConfigureAwait(false);
|
var epgChannel = await GetEpgChannelFromTunerChannel(provider.Item1, provider.Item2, channel, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
List<ProgramInfo> programs;
|
|
||||||
|
|
||||||
if (epgChannel == null)
|
if (epgChannel == null)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("EPG channel not found for tuner channel {0}-{1} from {2}-{3}", channel.Number, channel.Name, provider.Item1.Name, provider.Item2.ListingsId ?? string.Empty);
|
_logger.LogDebug("EPG channel not found for tuner channel {0}-{1} from {2}-{3}", channel.Number, channel.Name, provider.Item1.Name, provider.Item2.ListingsId ?? string.Empty);
|
||||||
programs = new List<ProgramInfo>();
|
continue;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
List<ProgramInfo> programs = (await provider.Item1.GetProgramsAsync(provider.Item2, epgChannel.Id, startDateUtc, endDateUtc, cancellationToken)
|
||||||
programs = (await provider.Item1.GetProgramsAsync(provider.Item2, epgChannel.Id, startDateUtc, endDateUtc, cancellationToken)
|
|
||||||
.ConfigureAwait(false)).ToList();
|
.ConfigureAwait(false)).ToList();
|
||||||
}
|
|
||||||
|
|
||||||
// Replace the value that came from the provider with a normalized value
|
// Replace the value that came from the provider with a normalized value
|
||||||
foreach (var program in programs)
|
foreach (var program in programs)
|
||||||
@ -940,7 +934,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new List<ProgramInfo>();
|
return Enumerable.Empty<ProgramInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Tuple<IListingsProvider, ListingsProviderInfo>> GetListingProviders()
|
private List<Tuple<IListingsProvider, ListingsProviderInfo>> GetListingProviders()
|
||||||
@ -1292,7 +1286,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
_logger.LogInformation("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture));
|
_logger.LogInformation("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
_logger.LogInformation("Writing file to path: " + recordPath);
|
_logger.LogInformation("Writing file to: {Path}", recordPath);
|
||||||
|
|
||||||
Action onStarted = async () =>
|
Action onStarted = async () =>
|
||||||
{
|
{
|
||||||
@ -1417,13 +1411,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
private void TriggerRefresh(string path)
|
private void TriggerRefresh(string path)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Triggering refresh on {path}", path);
|
_logger.LogInformation("Triggering refresh on {Path}", path);
|
||||||
|
|
||||||
var item = GetAffectedBaseItem(Path.GetDirectoryName(path));
|
var item = GetAffectedBaseItem(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Refreshing recording parent {path}", item.Path);
|
_logger.LogInformation("Refreshing recording parent {Path}", item.Path);
|
||||||
|
|
||||||
_providerManager.QueueRefresh(
|
_providerManager.QueueRefresh(
|
||||||
item.Id,
|
item.Id,
|
||||||
@ -1458,7 +1452,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
if (item.GetType() == typeof(Folder) && string.Equals(item.Path, parentPath, StringComparison.OrdinalIgnoreCase))
|
if (item.GetType() == typeof(Folder) && string.Equals(item.Path, parentPath, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var parentItem = item.GetParent();
|
var parentItem = item.GetParent();
|
||||||
if (parentItem != null && !(parentItem is AggregateFolder))
|
if (parentItem != null && parentItem is not AggregateFolder)
|
||||||
{
|
{
|
||||||
item = parentItem;
|
item = parentItem;
|
||||||
}
|
}
|
||||||
@ -1512,8 +1506,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
DeleteLibraryItemsForTimers(timersToDelete);
|
DeleteLibraryItemsForTimers(timersToDelete);
|
||||||
|
|
||||||
var librarySeries = _libraryManager.FindByPath(seriesPath, true) as Folder;
|
if (_libraryManager.FindByPath(seriesPath, true) is not Folder librarySeries)
|
||||||
if (librarySeries == null)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1667,7 +1660,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
|
|
||||||
_logger.LogInformation("Running recording post processor {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
_logger.LogInformation("Running recording post processor {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
||||||
|
|
||||||
process.Exited += Process_Exited;
|
process.Exited += OnProcessExited;
|
||||||
process.Start();
|
process.Start();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -1681,7 +1674,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
return arguments.Replace("{path}", path, StringComparison.OrdinalIgnoreCase);
|
return arguments.Replace("{path}", path, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Process_Exited(object sender, EventArgs e)
|
private void OnProcessExited(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
using (var process = (Process)sender)
|
using (var process = (Process)sender)
|
||||||
{
|
{
|
||||||
@ -2239,7 +2232,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
var enabledTimersForSeries = new List<TimerInfo>();
|
var enabledTimersForSeries = new List<TimerInfo>();
|
||||||
foreach (var timer in allTimers)
|
foreach (var timer in allTimers)
|
||||||
{
|
{
|
||||||
var existingTimer = _timerProvider.GetTimer(timer.Id)
|
var existingTimer = _timerProvider.GetTimer(timer.Id)
|
||||||
?? (string.IsNullOrWhiteSpace(timer.ProgramId)
|
?? (string.IsNullOrWhiteSpace(timer.ProgramId)
|
||||||
? null
|
? null
|
||||||
: _timerProvider.GetTimerByProgramId(timer.ProgramId));
|
: _timerProvider.GetTimerByProgramId(timer.ProgramId));
|
||||||
|
@ -319,11 +319,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
// TODO Investigate and properly fix.
|
|
||||||
// Don't spam the log. This doesn't seem to throw in windows, but sometimes under linux
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error reading ffmpeg recording log");
|
_logger.LogError(ex, "Error reading ffmpeg recording log");
|
||||||
|
@ -8,7 +8,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
{
|
{
|
||||||
internal class EpgChannelData
|
internal class EpgChannelData
|
||||||
{
|
{
|
||||||
|
|
||||||
private readonly Dictionary<string, ChannelInfo> _channelsById;
|
private readonly Dictionary<string, ChannelInfo> _channelsById;
|
||||||
|
|
||||||
private readonly Dictionary<string, ChannelInfo> _channelsByNumber;
|
private readonly Dictionary<string, ChannelInfo> _channelsByNumber;
|
||||||
|
@ -13,7 +13,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Records the specified media source.
|
/// Records the specified media source.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken);
|
Task Record(IDirectStreamProvider? directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken);
|
||||||
|
|
||||||
string GetOutputPath(MediaSourceInfo mediaSource, string targetFile);
|
string GetOutputPath(MediaSourceInfo mediaSource, string targetFile);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@ -23,7 +21,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
|
public event EventHandler<GenericEventArgs<TimerInfo>>? TimerFired;
|
||||||
|
|
||||||
public void RestartTimers()
|
public void RestartTimers()
|
||||||
{
|
{
|
||||||
@ -145,9 +143,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TimerCallback(object state)
|
private void TimerCallback(object? state)
|
||||||
{
|
{
|
||||||
var timerId = (string)state;
|
var timerId = (string?)state ?? throw new ArgumentNullException(nameof(state));
|
||||||
|
|
||||||
var timer = GetAll().FirstOrDefault(i => string.Equals(i.Id, timerId, StringComparison.OrdinalIgnoreCase));
|
var timer = GetAll().FirstOrDefault(i => string.Equals(i.Id, timerId, StringComparison.OrdinalIgnoreCase));
|
||||||
if (timer != null)
|
if (timer != null)
|
||||||
@ -156,12 +154,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimerInfo GetTimer(string id)
|
public TimerInfo? GetTimer(string id)
|
||||||
{
|
{
|
||||||
return GetAll().FirstOrDefault(r => string.Equals(r.Id, id, StringComparison.OrdinalIgnoreCase));
|
return GetAll().FirstOrDefault(r => string.Equals(r.Id, id, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimerInfo GetTimerByProgramId(string programId)
|
public TimerInfo? GetTimerByProgramId(string programId)
|
||||||
{
|
{
|
||||||
return GetAll().FirstOrDefault(r => string.Equals(r.ProgramId, programId, StringComparison.OrdinalIgnoreCase));
|
return GetAll().FirstOrDefault(r => string.Equals(r.ProgramId, programId, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,9 @@ using System.Text;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common;
|
using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos;
|
||||||
using Jellyfin.Extensions.Json;
|
using Jellyfin.Extensions.Json;
|
||||||
|
using MediaBrowser.Common;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
using MediaBrowser.Model.Cryptography;
|
using MediaBrowser.Model.Cryptography;
|
||||||
@ -96,12 +97,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
var dates = GetScheduleRequestDates(startDateUtc, endDateUtc);
|
var dates = GetScheduleRequestDates(startDateUtc, endDateUtc);
|
||||||
|
|
||||||
_logger.LogInformation("Channel Station ID is: {ChannelID}", channelId);
|
_logger.LogInformation("Channel Station ID is: {ChannelID}", channelId);
|
||||||
var requestList = new List<ScheduleDirect.RequestScheduleForChannel>()
|
var requestList = new List<RequestScheduleForChannelDto>()
|
||||||
{
|
{
|
||||||
new ScheduleDirect.RequestScheduleForChannel()
|
new RequestScheduleForChannelDto()
|
||||||
{
|
{
|
||||||
stationID = channelId,
|
StationId = channelId,
|
||||||
date = dates
|
Date = dates
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -113,61 +114,61 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
options.Headers.TryAddWithoutValidation("token", token);
|
options.Headers.TryAddWithoutValidation("token", token);
|
||||||
using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
|
using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
|
||||||
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
var dailySchedules = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.Day>>(responseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
var dailySchedules = await JsonSerializer.DeserializeAsync<List<DayDto>>(responseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
_logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
|
_logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
|
||||||
|
|
||||||
using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs");
|
using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs");
|
||||||
programRequestOptions.Headers.TryAddWithoutValidation("token", token);
|
programRequestOptions.Headers.TryAddWithoutValidation("token", token);
|
||||||
|
|
||||||
var programsID = dailySchedules.SelectMany(d => d.programs.Select(s => s.programID)).Distinct();
|
var programsID = dailySchedules.SelectMany(d => d.Programs.Select(s => s.ProgramId)).Distinct();
|
||||||
programRequestOptions.Content = new StringContent("[\"" + string.Join("\", \"", programsID) + "\"]", Encoding.UTF8, MediaTypeNames.Application.Json);
|
programRequestOptions.Content = new StringContent("[\"" + string.Join("\", \"", programsID) + "\"]", Encoding.UTF8, MediaTypeNames.Application.Json);
|
||||||
|
|
||||||
using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false);
|
using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false);
|
||||||
await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
var programDetails = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.ProgramDetails>>(innerResponseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
var programDetails = await JsonSerializer.DeserializeAsync<List<ProgramDetailsDto>>(innerResponseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
var programDict = programDetails.ToDictionary(p => p.programID, y => y);
|
var programDict = programDetails.ToDictionary(p => p.ProgramId, y => y);
|
||||||
|
|
||||||
var programIdsWithImages = programDetails
|
var programIdsWithImages = programDetails
|
||||||
.Where(p => p.hasImageArtwork).Select(p => p.programID)
|
.Where(p => p.HasImageArtwork).Select(p => p.ProgramId)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var images = await GetImageForPrograms(info, programIdsWithImages, cancellationToken).ConfigureAwait(false);
|
var images = await GetImageForPrograms(info, programIdsWithImages, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var programsInfo = new List<ProgramInfo>();
|
var programsInfo = new List<ProgramInfo>();
|
||||||
foreach (ScheduleDirect.Program schedule in dailySchedules.SelectMany(d => d.programs))
|
foreach (ProgramDto schedule in dailySchedules.SelectMany(d => d.Programs))
|
||||||
{
|
{
|
||||||
// _logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
|
// _logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
|
||||||
// " which corresponds to channel " + channelNumber + " and program id " +
|
// " which corresponds to channel " + channelNumber + " and program id " +
|
||||||
// schedule.programID + " which says it has images? " +
|
// schedule.ProgramId + " which says it has images? " +
|
||||||
// programDict[schedule.programID].hasImageArtwork);
|
// programDict[schedule.ProgramId].hasImageArtwork);
|
||||||
|
|
||||||
if (images != null)
|
if (images != null)
|
||||||
{
|
{
|
||||||
var imageIndex = images.FindIndex(i => i.programID == schedule.programID.Substring(0, 10));
|
var imageIndex = images.FindIndex(i => i.ProgramId == schedule.ProgramId[..10]);
|
||||||
if (imageIndex > -1)
|
if (imageIndex > -1)
|
||||||
{
|
{
|
||||||
var programEntry = programDict[schedule.programID];
|
var programEntry = programDict[schedule.ProgramId];
|
||||||
|
|
||||||
var allImages = images[imageIndex].data ?? new List<ScheduleDirect.ImageData>();
|
var allImages = images[imageIndex].Data ?? new List<ImageDataDto>();
|
||||||
var imagesWithText = allImages.Where(i => string.Equals(i.text, "yes", StringComparison.OrdinalIgnoreCase));
|
var imagesWithText = allImages.Where(i => string.Equals(i.Text, "yes", StringComparison.OrdinalIgnoreCase));
|
||||||
var imagesWithoutText = allImages.Where(i => string.Equals(i.text, "no", StringComparison.OrdinalIgnoreCase));
|
var imagesWithoutText = allImages.Where(i => string.Equals(i.Text, "no", StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
const double DesiredAspect = 2.0 / 3;
|
const double DesiredAspect = 2.0 / 3;
|
||||||
|
|
||||||
programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
|
programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
|
||||||
GetProgramImage(ApiUrl, allImages, true, DesiredAspect);
|
GetProgramImage(ApiUrl, allImages, true, DesiredAspect);
|
||||||
|
|
||||||
const double WideAspect = 16.0 / 9;
|
const double WideAspect = 16.0 / 9;
|
||||||
|
|
||||||
programEntry.thumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
|
programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
|
||||||
|
|
||||||
// Don't supply the same image twice
|
// Don't supply the same image twice
|
||||||
if (string.Equals(programEntry.primaryImage, programEntry.thumbImage, StringComparison.Ordinal))
|
if (string.Equals(programEntry.PrimaryImage, programEntry.ThumbImage, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
programEntry.thumbImage = null;
|
programEntry.ThumbImage = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
programEntry.backdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
|
programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
|
||||||
|
|
||||||
// programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
|
// programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
|
||||||
// GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
|
// GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
|
||||||
@ -176,15 +177,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.programID]));
|
programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.ProgramId]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return programsInfo;
|
return programsInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetSizeOrder(ScheduleDirect.ImageData image)
|
private static int GetSizeOrder(ImageDataDto image)
|
||||||
{
|
{
|
||||||
if (int.TryParse(image.height, out int value))
|
if (int.TryParse(image.Height, out int value))
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@ -192,53 +193,53 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetChannelNumber(ScheduleDirect.Map map)
|
private static string GetChannelNumber(MapDto map)
|
||||||
{
|
{
|
||||||
var channelNumber = map.logicalChannelNumber;
|
var channelNumber = map.LogicalChannelNumber;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(channelNumber))
|
if (string.IsNullOrWhiteSpace(channelNumber))
|
||||||
{
|
{
|
||||||
channelNumber = map.channel;
|
channelNumber = map.Channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(channelNumber))
|
if (string.IsNullOrWhiteSpace(channelNumber))
|
||||||
{
|
{
|
||||||
channelNumber = map.atscMajor + "." + map.atscMinor;
|
channelNumber = map.AtscMajor + "." + map.AtscMinor;
|
||||||
}
|
}
|
||||||
|
|
||||||
return channelNumber.TrimStart('0');
|
return channelNumber.TrimStart('0');
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsMovie(ScheduleDirect.ProgramDetails programInfo)
|
private static bool IsMovie(ProgramDetailsDto programInfo)
|
||||||
{
|
{
|
||||||
return string.Equals(programInfo.entityType, "movie", StringComparison.OrdinalIgnoreCase);
|
return string.Equals(programInfo.EntityType, "movie", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProgramInfo GetProgram(string channelId, ScheduleDirect.Program programInfo, ScheduleDirect.ProgramDetails details)
|
private ProgramInfo GetProgram(string channelId, ProgramDto programInfo, ProgramDetailsDto details)
|
||||||
{
|
{
|
||||||
var startAt = GetDate(programInfo.airDateTime);
|
var startAt = GetDate(programInfo.AirDateTime);
|
||||||
var endAt = startAt.AddSeconds(programInfo.duration);
|
var endAt = startAt.AddSeconds(programInfo.Duration);
|
||||||
var audioType = ProgramAudio.Stereo;
|
var audioType = ProgramAudio.Stereo;
|
||||||
|
|
||||||
var programId = programInfo.programID ?? string.Empty;
|
var programId = programInfo.ProgramId ?? string.Empty;
|
||||||
|
|
||||||
string newID = programId + "T" + startAt.Ticks + "C" + channelId;
|
string newID = programId + "T" + startAt.Ticks + "C" + channelId;
|
||||||
|
|
||||||
if (programInfo.audioProperties != null)
|
if (programInfo.AudioProperties != null)
|
||||||
{
|
{
|
||||||
if (programInfo.audioProperties.Exists(item => string.Equals(item, "atmos", StringComparison.OrdinalIgnoreCase)))
|
if (programInfo.AudioProperties.Exists(item => string.Equals(item, "atmos", StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
audioType = ProgramAudio.Atmos;
|
audioType = ProgramAudio.Atmos;
|
||||||
}
|
}
|
||||||
else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase)))
|
else if (programInfo.AudioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
audioType = ProgramAudio.DolbyDigital;
|
audioType = ProgramAudio.DolbyDigital;
|
||||||
}
|
}
|
||||||
else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd", StringComparison.OrdinalIgnoreCase)))
|
else if (programInfo.AudioProperties.Exists(item => string.Equals(item, "dd", StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
audioType = ProgramAudio.DolbyDigital;
|
audioType = ProgramAudio.DolbyDigital;
|
||||||
}
|
}
|
||||||
else if (programInfo.audioProperties.Exists(item => string.Equals(item, "stereo", StringComparison.OrdinalIgnoreCase)))
|
else if (programInfo.AudioProperties.Exists(item => string.Equals(item, "stereo", StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
audioType = ProgramAudio.Stereo;
|
audioType = ProgramAudio.Stereo;
|
||||||
}
|
}
|
||||||
@ -249,9 +250,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
}
|
}
|
||||||
|
|
||||||
string episodeTitle = null;
|
string episodeTitle = null;
|
||||||
if (details.episodeTitle150 != null)
|
if (details.EpisodeTitle150 != null)
|
||||||
{
|
{
|
||||||
episodeTitle = details.episodeTitle150;
|
episodeTitle = details.EpisodeTitle150;
|
||||||
}
|
}
|
||||||
|
|
||||||
var info = new ProgramInfo
|
var info = new ProgramInfo
|
||||||
@ -260,22 +261,22 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
Id = newID,
|
Id = newID,
|
||||||
StartDate = startAt,
|
StartDate = startAt,
|
||||||
EndDate = endAt,
|
EndDate = endAt,
|
||||||
Name = details.titles[0].title120 ?? "Unknown",
|
Name = details.Titles[0].Title120 ?? "Unknown",
|
||||||
OfficialRating = null,
|
OfficialRating = null,
|
||||||
CommunityRating = null,
|
CommunityRating = null,
|
||||||
EpisodeTitle = episodeTitle,
|
EpisodeTitle = episodeTitle,
|
||||||
Audio = audioType,
|
Audio = audioType,
|
||||||
// IsNew = programInfo.@new ?? false,
|
// IsNew = programInfo.@new ?? false,
|
||||||
IsRepeat = programInfo.@new == null,
|
IsRepeat = programInfo.New == null,
|
||||||
IsSeries = string.Equals(details.entityType, "episode", StringComparison.OrdinalIgnoreCase),
|
IsSeries = string.Equals(details.EntityType, "episode", StringComparison.OrdinalIgnoreCase),
|
||||||
ImageUrl = details.primaryImage,
|
ImageUrl = details.PrimaryImage,
|
||||||
ThumbImageUrl = details.thumbImage,
|
ThumbImageUrl = details.ThumbImage,
|
||||||
IsKids = string.Equals(details.audience, "children", StringComparison.OrdinalIgnoreCase),
|
IsKids = string.Equals(details.Audience, "children", StringComparison.OrdinalIgnoreCase),
|
||||||
IsSports = string.Equals(details.entityType, "sports", StringComparison.OrdinalIgnoreCase),
|
IsSports = string.Equals(details.EntityType, "sports", StringComparison.OrdinalIgnoreCase),
|
||||||
IsMovie = IsMovie(details),
|
IsMovie = IsMovie(details),
|
||||||
Etag = programInfo.md5,
|
Etag = programInfo.Md5,
|
||||||
IsLive = string.Equals(programInfo.liveTapeDelay, "live", StringComparison.OrdinalIgnoreCase),
|
IsLive = string.Equals(programInfo.LiveTapeDelay, "live", StringComparison.OrdinalIgnoreCase),
|
||||||
IsPremiere = programInfo.premiere || (programInfo.isPremiereOrFinale ?? string.Empty).IndexOf("premiere", StringComparison.OrdinalIgnoreCase) != -1
|
IsPremiere = programInfo.Premiere || (programInfo.IsPremiereOrFinale ?? string.Empty).IndexOf("premiere", StringComparison.OrdinalIgnoreCase) != -1
|
||||||
};
|
};
|
||||||
|
|
||||||
var showId = programId;
|
var showId = programId;
|
||||||
@ -298,15 +299,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
|
|
||||||
info.ShowId = showId;
|
info.ShowId = showId;
|
||||||
|
|
||||||
if (programInfo.videoProperties != null)
|
if (programInfo.VideoProperties != null)
|
||||||
{
|
{
|
||||||
info.IsHD = programInfo.videoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase);
|
info.IsHD = programInfo.VideoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase);
|
||||||
info.Is3D = programInfo.videoProperties.Contains("3d", StringComparer.OrdinalIgnoreCase);
|
info.Is3D = programInfo.VideoProperties.Contains("3d", StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (details.contentRating != null && details.contentRating.Count > 0)
|
if (details.ContentRating != null && details.ContentRating.Count > 0)
|
||||||
{
|
{
|
||||||
info.OfficialRating = details.contentRating[0].code.Replace("TV", "TV-", StringComparison.Ordinal)
|
info.OfficialRating = details.ContentRating[0].Code.Replace("TV", "TV-", StringComparison.Ordinal)
|
||||||
.Replace("--", "-", StringComparison.Ordinal);
|
.Replace("--", "-", StringComparison.Ordinal);
|
||||||
|
|
||||||
var invalid = new[] { "N/A", "Approved", "Not Rated", "Passed" };
|
var invalid = new[] { "N/A", "Approved", "Not Rated", "Passed" };
|
||||||
@ -316,15 +317,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (details.descriptions != null)
|
if (details.Descriptions != null)
|
||||||
{
|
{
|
||||||
if (details.descriptions.description1000 != null && details.descriptions.description1000.Count > 0)
|
if (details.Descriptions.Description1000 != null && details.Descriptions.Description1000.Count > 0)
|
||||||
{
|
{
|
||||||
info.Overview = details.descriptions.description1000[0].description;
|
info.Overview = details.Descriptions.Description1000[0].Description;
|
||||||
}
|
}
|
||||||
else if (details.descriptions.description100 != null && details.descriptions.description100.Count > 0)
|
else if (details.Descriptions.Description100 != null && details.Descriptions.Description100.Count > 0)
|
||||||
{
|
{
|
||||||
info.Overview = details.descriptions.description100[0].description;
|
info.Overview = details.Descriptions.Description100[0].Description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,18 +335,18 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
|
|
||||||
info.SeriesProviderIds[MetadataProvider.Zap2It.ToString()] = info.SeriesId;
|
info.SeriesProviderIds[MetadataProvider.Zap2It.ToString()] = info.SeriesId;
|
||||||
|
|
||||||
if (details.metadata != null)
|
if (details.Metadata != null)
|
||||||
{
|
{
|
||||||
foreach (var metadataProgram in details.metadata)
|
foreach (var metadataProgram in details.Metadata)
|
||||||
{
|
{
|
||||||
var gracenote = metadataProgram.Gracenote;
|
var gracenote = metadataProgram.Gracenote;
|
||||||
if (gracenote != null)
|
if (gracenote != null)
|
||||||
{
|
{
|
||||||
info.SeasonNumber = gracenote.season;
|
info.SeasonNumber = gracenote.Season;
|
||||||
|
|
||||||
if (gracenote.episode > 0)
|
if (gracenote.Episode > 0)
|
||||||
{
|
{
|
||||||
info.EpisodeNumber = gracenote.episode;
|
info.EpisodeNumber = gracenote.Episode;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -354,25 +355,25 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(details.originalAirDate))
|
if (!string.IsNullOrWhiteSpace(details.OriginalAirDate))
|
||||||
{
|
{
|
||||||
info.OriginalAirDate = DateTime.Parse(details.originalAirDate, CultureInfo.InvariantCulture);
|
info.OriginalAirDate = DateTime.Parse(details.OriginalAirDate, CultureInfo.InvariantCulture);
|
||||||
info.ProductionYear = info.OriginalAirDate.Value.Year;
|
info.ProductionYear = info.OriginalAirDate.Value.Year;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (details.movie != null)
|
if (details.Movie != null)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(details.movie.year)
|
if (!string.IsNullOrEmpty(details.Movie.Year)
|
||||||
&& int.TryParse(details.movie.year, out int year))
|
&& int.TryParse(details.Movie.Year, out int year))
|
||||||
{
|
{
|
||||||
info.ProductionYear = year;
|
info.ProductionYear = year;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (details.genres != null)
|
if (details.Genres != null)
|
||||||
{
|
{
|
||||||
info.Genres = details.genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList();
|
info.Genres = details.Genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList();
|
||||||
info.IsNews = details.genres.Contains("news", StringComparer.OrdinalIgnoreCase);
|
info.IsNews = details.Genres.Contains("news", StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
if (info.Genres.Contains("children", StringComparer.OrdinalIgnoreCase))
|
if (info.Genres.Contains("children", StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
@ -395,11 +396,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetProgramImage(string apiUrl, IEnumerable<ScheduleDirect.ImageData> images, bool returnDefaultImage, double desiredAspect)
|
private string GetProgramImage(string apiUrl, IEnumerable<ImageDataDto> images, bool returnDefaultImage, double desiredAspect)
|
||||||
{
|
{
|
||||||
var match = images
|
var match = images
|
||||||
.OrderBy(i => Math.Abs(desiredAspect - GetAspectRatio(i)))
|
.OrderBy(i => Math.Abs(desiredAspect - GetAspectRatio(i)))
|
||||||
.ThenByDescending(GetSizeOrder)
|
.ThenByDescending(i => GetSizeOrder(i))
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
if (match == null)
|
if (match == null)
|
||||||
@ -407,7 +408,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var uri = match.uri;
|
var uri = match.Uri;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(uri))
|
if (string.IsNullOrWhiteSpace(uri))
|
||||||
{
|
{
|
||||||
@ -423,19 +424,19 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double GetAspectRatio(ScheduleDirect.ImageData i)
|
private static double GetAspectRatio(ImageDataDto i)
|
||||||
{
|
{
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(i.width))
|
if (!string.IsNullOrWhiteSpace(i.Width))
|
||||||
{
|
{
|
||||||
int.TryParse(i.width, out width);
|
_ = int.TryParse(i.Width, out width);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(i.height))
|
if (!string.IsNullOrWhiteSpace(i.Height))
|
||||||
{
|
{
|
||||||
int.TryParse(i.height, out height);
|
_ = int.TryParse(i.Height, out height);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (height == 0 || width == 0)
|
if (height == 0 || width == 0)
|
||||||
@ -448,14 +449,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<List<ScheduleDirect.ShowImages>> GetImageForPrograms(
|
private async Task<List<ShowImagesDto>> GetImageForPrograms(
|
||||||
ListingsProviderInfo info,
|
ListingsProviderInfo info,
|
||||||
IReadOnlyList<string> programIds,
|
IReadOnlyList<string> programIds,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (programIds.Count == 0)
|
if (programIds.Count == 0)
|
||||||
{
|
{
|
||||||
return new List<ScheduleDirect.ShowImages>();
|
return new List<ShowImagesDto>();
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder str = new StringBuilder("[", 1 + (programIds.Count * 13));
|
StringBuilder str = new StringBuilder("[", 1 + (programIds.Count * 13));
|
||||||
@ -479,13 +480,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
{
|
{
|
||||||
using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false);
|
using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false);
|
||||||
await using var response = await innerResponse2.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var response = await innerResponse2.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
return await JsonSerializer.DeserializeAsync<List<ScheduleDirect.ShowImages>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
return await JsonSerializer.DeserializeAsync<List<ShowImagesDto>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error getting image info from schedules direct");
|
_logger.LogError(ex, "Error getting image info from schedules direct");
|
||||||
|
|
||||||
return new List<ScheduleDirect.ShowImages>();
|
return new List<ShowImagesDto>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,18 +509,18 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
using var httpResponse = await Send(options, false, info, cancellationToken).ConfigureAwait(false);
|
using var httpResponse = await Send(options, false, info, cancellationToken).ConfigureAwait(false);
|
||||||
await using var response = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var response = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var root = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.Headends>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
var root = await JsonSerializer.DeserializeAsync<List<HeadendsDto>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (root != null)
|
if (root != null)
|
||||||
{
|
{
|
||||||
foreach (ScheduleDirect.Headends headend in root)
|
foreach (HeadendsDto headend in root)
|
||||||
{
|
{
|
||||||
foreach (ScheduleDirect.Lineup lineup in headend.lineups)
|
foreach (LineupDto lineup in headend.Lineups)
|
||||||
{
|
{
|
||||||
lineups.Add(new NameIdPair
|
lineups.Add(new NameIdPair
|
||||||
{
|
{
|
||||||
Name = string.IsNullOrWhiteSpace(lineup.name) ? lineup.lineup : lineup.name,
|
Name = string.IsNullOrWhiteSpace(lineup.Name) ? lineup.Lineup : lineup.Name,
|
||||||
Id = lineup.uri.Substring(18)
|
Id = lineup.Uri[18..]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -649,14 +650,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false);
|
using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Token>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
var root = await JsonSerializer.DeserializeAsync<TokenDto>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
if (string.Equals(root.message, "OK", StringComparison.Ordinal))
|
if (string.Equals(root.Message, "OK", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Authenticated with Schedules Direct token: " + root.token);
|
_logger.LogInformation("Authenticated with Schedules Direct token: {Token}", root.Token);
|
||||||
return root.token;
|
return root.Token;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception("Could not authenticate with Schedules Direct Error: " + root.message);
|
throw new Exception("Could not authenticate with Schedules Direct Error: " + root.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddLineupToAccount(ListingsProviderInfo info, CancellationToken cancellationToken)
|
private async Task AddLineupToAccount(ListingsProviderInfo info, CancellationToken cancellationToken)
|
||||||
@ -705,9 +706,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
httpResponse.EnsureSuccessStatusCode();
|
httpResponse.EnsureSuccessStatusCode();
|
||||||
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
using var response = httpResponse.Content;
|
using var response = httpResponse.Content;
|
||||||
var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Lineups>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
var root = await JsonSerializer.DeserializeAsync<LineupsDto>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
|
return root.Lineups.Any(i => string.Equals(info.ListingsId, i.Lineup, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
catch (HttpRequestException ex)
|
catch (HttpRequestException ex)
|
||||||
{
|
{
|
||||||
@ -777,35 +778,35 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
|
|
||||||
using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
|
using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Channel>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
var root = await JsonSerializer.DeserializeAsync<ChannelDto>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
_logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count);
|
_logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.Map.Count);
|
||||||
_logger.LogInformation("Mapping Stations to Channel");
|
_logger.LogInformation("Mapping Stations to Channel");
|
||||||
|
|
||||||
var allStations = root.stations ?? new List<ScheduleDirect.Station>();
|
var allStations = root.Stations ?? new List<StationDto>();
|
||||||
|
|
||||||
var map = root.map;
|
var map = root.Map;
|
||||||
var list = new List<ChannelInfo>(map.Count);
|
var list = new List<ChannelInfo>(map.Count);
|
||||||
foreach (var channel in map)
|
foreach (var channel in map)
|
||||||
{
|
{
|
||||||
var channelNumber = GetChannelNumber(channel);
|
var channelNumber = GetChannelNumber(channel);
|
||||||
|
|
||||||
var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase))
|
var station = allStations.Find(item => string.Equals(item.StationId, channel.StationId, StringComparison.OrdinalIgnoreCase))
|
||||||
?? new ScheduleDirect.Station
|
?? new StationDto
|
||||||
{
|
{
|
||||||
stationID = channel.stationID
|
StationId = channel.StationId
|
||||||
};
|
};
|
||||||
|
|
||||||
var channelInfo = new ChannelInfo
|
var channelInfo = new ChannelInfo
|
||||||
{
|
{
|
||||||
Id = station.stationID,
|
Id = station.StationId,
|
||||||
CallSign = station.callsign,
|
CallSign = station.Callsign,
|
||||||
Number = channelNumber,
|
Number = channelNumber,
|
||||||
Name = string.IsNullOrWhiteSpace(station.name) ? channelNumber : station.name
|
Name = string.IsNullOrWhiteSpace(station.Name) ? channelNumber : station.Name
|
||||||
};
|
};
|
||||||
|
|
||||||
if (station.logo != null)
|
if (station.Logo != null)
|
||||||
{
|
{
|
||||||
channelInfo.ImageUrl = station.logo.URL;
|
channelInfo.ImageUrl = station.Logo.Url;
|
||||||
}
|
}
|
||||||
|
|
||||||
list.Add(channelInfo);
|
list.Add(channelInfo);
|
||||||
@ -818,402 +819,5 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||||||
{
|
{
|
||||||
return value.Replace(" ", string.Empty, StringComparison.Ordinal).Replace("-", string.Empty, StringComparison.Ordinal);
|
return value.Replace(" ", string.Empty, StringComparison.Ordinal).Replace("-", string.Empty, StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ScheduleDirect
|
|
||||||
{
|
|
||||||
public class Token
|
|
||||||
{
|
|
||||||
public int code { get; set; }
|
|
||||||
|
|
||||||
public string message { get; set; }
|
|
||||||
|
|
||||||
public string serverID { get; set; }
|
|
||||||
|
|
||||||
public string token { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Lineup
|
|
||||||
{
|
|
||||||
public string lineup { get; set; }
|
|
||||||
|
|
||||||
public string name { get; set; }
|
|
||||||
|
|
||||||
public string transport { get; set; }
|
|
||||||
|
|
||||||
public string location { get; set; }
|
|
||||||
|
|
||||||
public string uri { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Lineups
|
|
||||||
{
|
|
||||||
public int code { get; set; }
|
|
||||||
|
|
||||||
public string serverID { get; set; }
|
|
||||||
|
|
||||||
public string datetime { get; set; }
|
|
||||||
|
|
||||||
public List<Lineup> lineups { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Headends
|
|
||||||
{
|
|
||||||
public string headend { get; set; }
|
|
||||||
|
|
||||||
public string transport { get; set; }
|
|
||||||
|
|
||||||
public string location { get; set; }
|
|
||||||
|
|
||||||
public List<Lineup> lineups { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Map
|
|
||||||
{
|
|
||||||
public string stationID { get; set; }
|
|
||||||
|
|
||||||
public string channel { get; set; }
|
|
||||||
|
|
||||||
public string logicalChannelNumber { get; set; }
|
|
||||||
|
|
||||||
public int uhfVhf { get; set; }
|
|
||||||
|
|
||||||
public int atscMajor { get; set; }
|
|
||||||
|
|
||||||
public int atscMinor { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Broadcaster
|
|
||||||
{
|
|
||||||
public string city { get; set; }
|
|
||||||
|
|
||||||
public string state { get; set; }
|
|
||||||
|
|
||||||
public string postalcode { get; set; }
|
|
||||||
|
|
||||||
public string country { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Logo
|
|
||||||
{
|
|
||||||
public string URL { get; set; }
|
|
||||||
|
|
||||||
public int height { get; set; }
|
|
||||||
|
|
||||||
public int width { get; set; }
|
|
||||||
|
|
||||||
public string md5 { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Station
|
|
||||||
{
|
|
||||||
public string stationID { get; set; }
|
|
||||||
|
|
||||||
public string name { get; set; }
|
|
||||||
|
|
||||||
public string callsign { get; set; }
|
|
||||||
|
|
||||||
public List<string> broadcastLanguage { get; set; }
|
|
||||||
|
|
||||||
public List<string> descriptionLanguage { get; set; }
|
|
||||||
|
|
||||||
public Broadcaster broadcaster { get; set; }
|
|
||||||
|
|
||||||
public string affiliate { get; set; }
|
|
||||||
|
|
||||||
public Logo logo { get; set; }
|
|
||||||
|
|
||||||
public bool? isCommercialFree { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Metadata
|
|
||||||
{
|
|
||||||
public string lineup { get; set; }
|
|
||||||
|
|
||||||
public string modified { get; set; }
|
|
||||||
|
|
||||||
public string transport { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Channel
|
|
||||||
{
|
|
||||||
public List<Map> map { get; set; }
|
|
||||||
|
|
||||||
public List<Station> stations { get; set; }
|
|
||||||
|
|
||||||
public Metadata metadata { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RequestScheduleForChannel
|
|
||||||
{
|
|
||||||
public string stationID { get; set; }
|
|
||||||
|
|
||||||
public List<string> date { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Rating
|
|
||||||
{
|
|
||||||
public string body { get; set; }
|
|
||||||
|
|
||||||
public string code { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Multipart
|
|
||||||
{
|
|
||||||
public int partNumber { get; set; }
|
|
||||||
|
|
||||||
public int totalParts { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Program
|
|
||||||
{
|
|
||||||
public string programID { get; set; }
|
|
||||||
|
|
||||||
public string airDateTime { get; set; }
|
|
||||||
|
|
||||||
public int duration { get; set; }
|
|
||||||
|
|
||||||
public string md5 { get; set; }
|
|
||||||
|
|
||||||
public List<string> audioProperties { get; set; }
|
|
||||||
|
|
||||||
public List<string> videoProperties { get; set; }
|
|
||||||
|
|
||||||
public List<Rating> ratings { get; set; }
|
|
||||||
|
|
||||||
public bool? @new { get; set; }
|
|
||||||
|
|
||||||
public Multipart multipart { get; set; }
|
|
||||||
|
|
||||||
public string liveTapeDelay { get; set; }
|
|
||||||
|
|
||||||
public bool premiere { get; set; }
|
|
||||||
|
|
||||||
public bool repeat { get; set; }
|
|
||||||
|
|
||||||
public string isPremiereOrFinale { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MetadataSchedule
|
|
||||||
{
|
|
||||||
public string modified { get; set; }
|
|
||||||
|
|
||||||
public string md5 { get; set; }
|
|
||||||
|
|
||||||
public string startDate { get; set; }
|
|
||||||
|
|
||||||
public string endDate { get; set; }
|
|
||||||
|
|
||||||
public int days { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Day
|
|
||||||
{
|
|
||||||
public string stationID { get; set; }
|
|
||||||
|
|
||||||
public List<Program> programs { get; set; }
|
|
||||||
|
|
||||||
public MetadataSchedule metadata { get; set; }
|
|
||||||
|
|
||||||
public Day()
|
|
||||||
{
|
|
||||||
programs = new List<Program>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Title
|
|
||||||
{
|
|
||||||
public string title120 { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class EventDetails
|
|
||||||
{
|
|
||||||
public string subType { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Description100
|
|
||||||
{
|
|
||||||
public string descriptionLanguage { get; set; }
|
|
||||||
|
|
||||||
public string description { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Description1000
|
|
||||||
{
|
|
||||||
public string descriptionLanguage { get; set; }
|
|
||||||
|
|
||||||
public string description { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DescriptionsProgram
|
|
||||||
{
|
|
||||||
public List<Description100> description100 { get; set; }
|
|
||||||
|
|
||||||
public List<Description1000> description1000 { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Gracenote
|
|
||||||
{
|
|
||||||
public int season { get; set; }
|
|
||||||
|
|
||||||
public int episode { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MetadataPrograms
|
|
||||||
{
|
|
||||||
public Gracenote Gracenote { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ContentRating
|
|
||||||
{
|
|
||||||
public string body { get; set; }
|
|
||||||
|
|
||||||
public string code { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Cast
|
|
||||||
{
|
|
||||||
public string billingOrder { get; set; }
|
|
||||||
|
|
||||||
public string role { get; set; }
|
|
||||||
|
|
||||||
public string nameId { get; set; }
|
|
||||||
|
|
||||||
public string personId { get; set; }
|
|
||||||
|
|
||||||
public string name { get; set; }
|
|
||||||
|
|
||||||
public string characterName { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Crew
|
|
||||||
{
|
|
||||||
public string billingOrder { get; set; }
|
|
||||||
|
|
||||||
public string role { get; set; }
|
|
||||||
|
|
||||||
public string nameId { get; set; }
|
|
||||||
|
|
||||||
public string personId { get; set; }
|
|
||||||
|
|
||||||
public string name { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class QualityRating
|
|
||||||
{
|
|
||||||
public string ratingsBody { get; set; }
|
|
||||||
|
|
||||||
public string rating { get; set; }
|
|
||||||
|
|
||||||
public string minRating { get; set; }
|
|
||||||
|
|
||||||
public string maxRating { get; set; }
|
|
||||||
|
|
||||||
public string increment { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Movie
|
|
||||||
{
|
|
||||||
public string year { get; set; }
|
|
||||||
|
|
||||||
public int duration { get; set; }
|
|
||||||
|
|
||||||
public List<QualityRating> qualityRating { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Recommendation
|
|
||||||
{
|
|
||||||
public string programID { get; set; }
|
|
||||||
|
|
||||||
public string title120 { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ProgramDetails
|
|
||||||
{
|
|
||||||
public string audience { get; set; }
|
|
||||||
|
|
||||||
public string programID { get; set; }
|
|
||||||
|
|
||||||
public List<Title> titles { get; set; }
|
|
||||||
|
|
||||||
public EventDetails eventDetails { get; set; }
|
|
||||||
|
|
||||||
public DescriptionsProgram descriptions { get; set; }
|
|
||||||
|
|
||||||
public string originalAirDate { get; set; }
|
|
||||||
|
|
||||||
public List<string> genres { get; set; }
|
|
||||||
|
|
||||||
public string episodeTitle150 { get; set; }
|
|
||||||
|
|
||||||
public List<MetadataPrograms> metadata { get; set; }
|
|
||||||
|
|
||||||
public List<ContentRating> contentRating { get; set; }
|
|
||||||
|
|
||||||
public List<Cast> cast { get; set; }
|
|
||||||
|
|
||||||
public List<Crew> crew { get; set; }
|
|
||||||
|
|
||||||
public string entityType { get; set; }
|
|
||||||
|
|
||||||
public string showType { get; set; }
|
|
||||||
|
|
||||||
public bool hasImageArtwork { get; set; }
|
|
||||||
|
|
||||||
public string primaryImage { get; set; }
|
|
||||||
|
|
||||||
public string thumbImage { get; set; }
|
|
||||||
|
|
||||||
public string backdropImage { get; set; }
|
|
||||||
|
|
||||||
public string bannerImage { get; set; }
|
|
||||||
|
|
||||||
public string imageID { get; set; }
|
|
||||||
|
|
||||||
public string md5 { get; set; }
|
|
||||||
|
|
||||||
public List<string> contentAdvisory { get; set; }
|
|
||||||
|
|
||||||
public Movie movie { get; set; }
|
|
||||||
|
|
||||||
public List<Recommendation> recommendations { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Caption
|
|
||||||
{
|
|
||||||
public string content { get; set; }
|
|
||||||
|
|
||||||
public string lang { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ImageData
|
|
||||||
{
|
|
||||||
public string width { get; set; }
|
|
||||||
|
|
||||||
public string height { get; set; }
|
|
||||||
|
|
||||||
public string uri { get; set; }
|
|
||||||
|
|
||||||
public string size { get; set; }
|
|
||||||
|
|
||||||
public string aspect { get; set; }
|
|
||||||
|
|
||||||
public string category { get; set; }
|
|
||||||
|
|
||||||
public string text { get; set; }
|
|
||||||
|
|
||||||
public string primary { get; set; }
|
|
||||||
|
|
||||||
public string tier { get; set; }
|
|
||||||
|
|
||||||
public Caption caption { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ShowImages
|
|
||||||
{
|
|
||||||
public string programID { get; set; }
|
|
||||||
|
|
||||||
public List<ImageData> data { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Broadcaster dto.
|
||||||
|
/// </summary>
|
||||||
|
public class BroadcasterDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the city.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("city")]
|
||||||
|
public string City { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the state.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("state")]
|
||||||
|
public string State { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the postal code.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("postalCode")]
|
||||||
|
public string Postalcode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the country.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("country")]
|
||||||
|
public string Country { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Caption dto.
|
||||||
|
/// </summary>
|
||||||
|
public class CaptionDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the content.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("content")]
|
||||||
|
public string Content { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the lang.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("lang")]
|
||||||
|
public string Lang { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Cast dto.
|
||||||
|
/// </summary>
|
||||||
|
public class CastDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the billing order.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("billingOrder")]
|
||||||
|
public string BillingOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the role.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("role")]
|
||||||
|
public string Role { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("nameId")]
|
||||||
|
public string NameId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the person id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("personId")]
|
||||||
|
public string PersonId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the character name.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("characterName")]
|
||||||
|
public string CharacterName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Channel dto.
|
||||||
|
/// </summary>
|
||||||
|
public class ChannelDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of maps.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("map")]
|
||||||
|
public List<MapDto> Map { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of stations.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("stations")]
|
||||||
|
public List<StationDto> Stations { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the metadata.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("metadata")]
|
||||||
|
public MetadataDto Metadata { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Content rating dto.
|
||||||
|
/// </summary>
|
||||||
|
public class ContentRatingDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the body.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("body")]
|
||||||
|
public string Body { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the code.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("code")]
|
||||||
|
public string Code { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Crew dto.
|
||||||
|
/// </summary>
|
||||||
|
public class CrewDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the billing order.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("billingOrder")]
|
||||||
|
public string BillingOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the role.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("role")]
|
||||||
|
public string Role { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("nameId")]
|
||||||
|
public string NameId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the person id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("personId")]
|
||||||
|
public string PersonId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Day dto.
|
||||||
|
/// </summary>
|
||||||
|
public class DayDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DayDto"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public DayDto()
|
||||||
|
{
|
||||||
|
Programs = new List<ProgramDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the station id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("stationID")]
|
||||||
|
public string StationId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of programs.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("programs")]
|
||||||
|
public List<ProgramDto> Programs { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the metadata schedule.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("metadata")]
|
||||||
|
public MetadataScheduleDto Metadata { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Description 1_000 dto.
|
||||||
|
/// </summary>
|
||||||
|
public class Description1000Dto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the description language.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("descriptionLanguage")]
|
||||||
|
public string DescriptionLanguage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the description.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("description")]
|
||||||
|
public string Description { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Description 100 dto.
|
||||||
|
/// </summary>
|
||||||
|
public class Description100Dto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the description language.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("descriptionLanguage")]
|
||||||
|
public string DescriptionLanguage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the description.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("description")]
|
||||||
|
public string Description { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Descriptions program dto.
|
||||||
|
/// </summary>
|
||||||
|
public class DescriptionsProgramDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of description 100.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("description100")]
|
||||||
|
public List<Description100Dto> Description100 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of description1000.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("description1000")]
|
||||||
|
public List<Description1000Dto> Description1000 { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event details dto.
|
||||||
|
/// </summary>
|
||||||
|
public class EventDetailsDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the sub type.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("subType")]
|
||||||
|
public string SubType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gracenote dto.
|
||||||
|
/// </summary>
|
||||||
|
public class GracenoteDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the season.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("season")]
|
||||||
|
public int Season { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the episode.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("episode")]
|
||||||
|
public int Episode { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Headends dto.
|
||||||
|
/// </summary>
|
||||||
|
public class HeadendsDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the headend.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("headend")]
|
||||||
|
public string Headend { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the transport.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("transport")]
|
||||||
|
public string Transport { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the location.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("location")]
|
||||||
|
public string Location { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of lineups.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("lineups")]
|
||||||
|
public List<LineupDto> Lineups { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Image data dto.
|
||||||
|
/// </summary>
|
||||||
|
public class ImageDataDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the width.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("width")]
|
||||||
|
public string Width { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the height.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("height")]
|
||||||
|
public string Height { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the uri.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("uri")]
|
||||||
|
public string Uri { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the size.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("size")]
|
||||||
|
public string Size { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the aspect.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("aspect")]
|
||||||
|
public string aspect { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the category.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("category")]
|
||||||
|
public string Category { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the text.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("text")]
|
||||||
|
public string Text { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the primary.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("primary")]
|
||||||
|
public string Primary { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the tier.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("tier")]
|
||||||
|
public string Tier { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the caption.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("caption")]
|
||||||
|
public CaptionDto Caption { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The lineup dto.
|
||||||
|
/// </summary>
|
||||||
|
public class LineupDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the linup.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("lineup")]
|
||||||
|
public string Lineup { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the lineup name.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the transport.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("transport")]
|
||||||
|
public string Transport { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the location.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("location")]
|
||||||
|
public string Location { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the uri.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("uri")]
|
||||||
|
public string Uri { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Lineups dto.
|
||||||
|
/// </summary>
|
||||||
|
public class LineupsDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the response code.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("code")]
|
||||||
|
public int Code { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the server id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("serverID")]
|
||||||
|
public string ServerId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the datetime.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("datetime")]
|
||||||
|
public string Datetime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of lineups.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("lineups")]
|
||||||
|
public List<LineupDto> Lineups { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Logo dto.
|
||||||
|
/// </summary>
|
||||||
|
public class LogoDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the url.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("URL")]
|
||||||
|
public string Url { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the height.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("height")]
|
||||||
|
public int Height { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the width.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("width")]
|
||||||
|
public int Width { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the md5.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("md5")]
|
||||||
|
public string Md5 { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Map dto.
|
||||||
|
/// </summary>
|
||||||
|
public class MapDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the station id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("stationID")]
|
||||||
|
public string StationId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the channel.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("channel")]
|
||||||
|
public string Channel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the logical channel number.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("logicalChannelNumber")]
|
||||||
|
public string LogicalChannelNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the uhfvhf.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("uhfVhf")]
|
||||||
|
public int UhfVhf { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the atsc major.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("atscMajor")]
|
||||||
|
public int AtscMajor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the atsc minor.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("atscMinor")]
|
||||||
|
public int AtscMinor { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Metadata dto.
|
||||||
|
/// </summary>
|
||||||
|
public class MetadataDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the linup.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("lineup")]
|
||||||
|
public string Lineup { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the modified timestamp.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("modified")]
|
||||||
|
public string Modified { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the transport.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("transport")]
|
||||||
|
public string Transport { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Metadata programs dto.
|
||||||
|
/// </summary>
|
||||||
|
public class MetadataProgramsDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the gracenote object.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("gracenote")]
|
||||||
|
public GracenoteDto Gracenote { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Metadata schedule dto.
|
||||||
|
/// </summary>
|
||||||
|
public class MetadataScheduleDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the modified timestamp.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("modified")]
|
||||||
|
public string Modified { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the md5.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("md5")]
|
||||||
|
public string Md5 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the start date.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("startDate")]
|
||||||
|
public string StartDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the end date.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("endDate")]
|
||||||
|
public string EndDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the days count.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("days")]
|
||||||
|
public int Days { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Movie dto.
|
||||||
|
/// </summary>
|
||||||
|
public class MovieDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the year.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("year")]
|
||||||
|
public string Year { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the duration.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("duration")]
|
||||||
|
public int Duration { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of quality rating.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("qualityRating")]
|
||||||
|
public List<QualityRatingDto> QualityRating { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Multipart dto.
|
||||||
|
/// </summary>
|
||||||
|
public class MultipartDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the part number.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("partNumber")]
|
||||||
|
public int PartNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the total parts.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("totalParts")]
|
||||||
|
public int TotalParts { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,157 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Program details dto.
|
||||||
|
/// </summary>
|
||||||
|
public class ProgramDetailsDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the audience.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("audience")]
|
||||||
|
public string Audience { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the program id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("programID")]
|
||||||
|
public string ProgramId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of titles.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("titles")]
|
||||||
|
public List<TitleDto> Titles { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the event details object.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("eventDetails")]
|
||||||
|
public EventDetailsDto EventDetails { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the descriptions.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("descriptions")]
|
||||||
|
public DescriptionsProgramDto Descriptions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the original air date.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("originalAirDate")]
|
||||||
|
public string OriginalAirDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of genres.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("genres")]
|
||||||
|
public List<string> Genres { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the episode title.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("episodeTitle150")]
|
||||||
|
public string EpisodeTitle150 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of metadata.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("metadata")]
|
||||||
|
public List<MetadataProgramsDto> Metadata { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of content raitings.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("contentRating")]
|
||||||
|
public List<ContentRatingDto> ContentRating { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of cast.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("cast")]
|
||||||
|
public List<CastDto> Cast { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of crew.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("crew")]
|
||||||
|
public List<CrewDto> Crew { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the entity type.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("entityType")]
|
||||||
|
public string EntityType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the show type.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("showType")]
|
||||||
|
public string ShowType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether there is image artwork.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("hasImageArtwork")]
|
||||||
|
public bool HasImageArtwork { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the primary image.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("primaryImage")]
|
||||||
|
public string PrimaryImage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the thumb image.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("thumbImage")]
|
||||||
|
public string ThumbImage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the backdrop image.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("backdropImage")]
|
||||||
|
public string BackdropImage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the banner image.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("bannerImage")]
|
||||||
|
public string BannerImage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the image id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("imageID")]
|
||||||
|
public string ImageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the md5.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("md5")]
|
||||||
|
public string Md5 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of content advisory.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("contentAdvisory")]
|
||||||
|
public List<string> ContentAdvisory { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the movie object.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("movie")]
|
||||||
|
public MovieDto Movie { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of recommendations.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("recommendations")]
|
||||||
|
public List<RecommendationDto> Recommendations { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Program dto.
|
||||||
|
/// </summary>
|
||||||
|
public class ProgramDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the program id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("programID")]
|
||||||
|
public string ProgramId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the air date time.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("airDateTime")]
|
||||||
|
public string AirDateTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the duration.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("duration")]
|
||||||
|
public int Duration { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the md5.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("md5")]
|
||||||
|
public string Md5 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of audio properties.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("audioProperties")]
|
||||||
|
public List<string> AudioProperties { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of video properties.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("videoProperties")]
|
||||||
|
public List<string> VideoProperties { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of ratings.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("ratings")]
|
||||||
|
public List<RatingDto> Ratings { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this program is new.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("new")]
|
||||||
|
public bool? New { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the multipart object.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("multipart")]
|
||||||
|
public MultipartDto Multipart { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the live tape delay.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("liveTapeDelay")]
|
||||||
|
public string LiveTapeDelay { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this is the premiere.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("premiere")]
|
||||||
|
public bool Premiere { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this is a repeat.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("repeat")]
|
||||||
|
public bool Repeat { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the premiere or finale.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("isPremiereOrFinale")]
|
||||||
|
public string IsPremiereOrFinale { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Quality rating dto.
|
||||||
|
/// </summary>
|
||||||
|
public class QualityRatingDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the ratings body.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("ratingsBody")]
|
||||||
|
public string RatingsBody { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the rating.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("rating")]
|
||||||
|
public string Rating { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the min rating.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("minRating")]
|
||||||
|
public string MinRating { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the max rating.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("maxRating")]
|
||||||
|
public string MaxRating { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the increment.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("increment")]
|
||||||
|
public string Increment { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Rating dto.
|
||||||
|
/// </summary>
|
||||||
|
public class RatingDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the body.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("body")]
|
||||||
|
public string Body { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the code.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("code")]
|
||||||
|
public string Code { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Recommendation dto.
|
||||||
|
/// </summary>
|
||||||
|
public class RecommendationDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the program id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("programID")]
|
||||||
|
public string ProgramId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the title.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("title120")]
|
||||||
|
public string Title120 { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Request schedule for channel dto.
|
||||||
|
/// </summary>
|
||||||
|
public class RequestScheduleForChannelDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the station id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("stationID")]
|
||||||
|
public string StationId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of dates.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("date")]
|
||||||
|
public List<string> Date { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Show image dto.
|
||||||
|
/// </summary>
|
||||||
|
public class ShowImagesDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the program id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("programID")]
|
||||||
|
public string ProgramId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the list of data.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("data")]
|
||||||
|
public List<ImageDataDto> Data { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Station dto.
|
||||||
|
/// </summary>
|
||||||
|
public class StationDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the station id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("stationID")]
|
||||||
|
public string StationId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the callsign.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("callsign")]
|
||||||
|
public string Callsign { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the broadcast language.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("broadcastLanguage")]
|
||||||
|
public List<string> BroadcastLanguage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the description language.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("descriptionLanguage")]
|
||||||
|
public List<string> DescriptionLanguage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the broadcaster.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("broadcaster")]
|
||||||
|
public BroadcasterDto Broadcaster { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the affiliate.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("affiliate")]
|
||||||
|
public string Affiliate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the logo.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("logo")]
|
||||||
|
public LogoDto Logo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or set a value indicating whether it is commercial free.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("isCommercialFree")]
|
||||||
|
public bool? IsCommercialFree { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Title dto.
|
||||||
|
/// </summary>
|
||||||
|
public class TitleDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the title.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("title120")]
|
||||||
|
public string Title120 { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The token dto.
|
||||||
|
/// </summary>
|
||||||
|
public class TokenDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the response code.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("code")]
|
||||||
|
public int Code { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the response message.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("message")]
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the server id.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("serverID")]
|
||||||
|
public string ServerId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the token.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("token")]
|
||||||
|
public string Token { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -403,7 +403,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||||||
// Set the total bitrate if not already supplied
|
// Set the total bitrate if not already supplied
|
||||||
mediaSource.InferTotalBitrate();
|
mediaSource.InferTotalBitrate();
|
||||||
|
|
||||||
if (!(service is EmbyTV.EmbyTV))
|
if (service is not EmbyTV.EmbyTV)
|
||||||
{
|
{
|
||||||
// We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
|
// We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
|
||||||
// mediaSource.SupportsDirectPlay = false;
|
// mediaSource.SupportsDirectPlay = false;
|
||||||
@ -1724,7 +1724,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||||||
|
|
||||||
await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
|
await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
if (!(service is EmbyTV.EmbyTV))
|
if (service is not EmbyTV.EmbyTV)
|
||||||
{
|
{
|
||||||
TimerCancelled?.Invoke(this, new GenericEventArgs<TimerEventInfo>(new TimerEventInfo(id)));
|
TimerCancelled?.Invoke(this, new GenericEventArgs<TimerEventInfo>(new TimerEventInfo(id)));
|
||||||
}
|
}
|
||||||
@ -2050,7 +2050,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||||||
|
|
||||||
_logger.LogInformation("New recording scheduled");
|
_logger.LogInformation("New recording scheduled");
|
||||||
|
|
||||||
if (!(service is EmbyTV.EmbyTV))
|
if (service is not EmbyTV.EmbyTV)
|
||||||
{
|
{
|
||||||
TimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>(
|
TimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>(
|
||||||
new TimerEventInfo(newTimerId)
|
new TimerEventInfo(newTimerId)
|
||||||
|
@ -27,6 +27,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
{
|
{
|
||||||
private string _channel;
|
private string _channel;
|
||||||
private string _program;
|
private string _program;
|
||||||
|
|
||||||
public LegacyHdHomerunChannelCommands(string url)
|
public LegacyHdHomerunChannelCommands(string url)
|
||||||
{
|
{
|
||||||
// parse url for channel and program
|
// parse url for channel and program
|
||||||
|
@ -13,7 +13,6 @@ using System.Threading.Tasks;
|
|||||||
using Jellyfin.Extensions;
|
using Jellyfin.Extensions;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller;
|
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@ -44,22 +43,29 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
|
|
||||||
public async Task<Stream> GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken)
|
public async Task<Stream> GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
if (info == null)
|
||||||
{
|
{
|
||||||
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
|
throw new ArgumentNullException(nameof(info));
|
||||||
if (!string.IsNullOrEmpty(info.UserAgent))
|
|
||||||
{
|
|
||||||
requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent);
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await _httpClientFactory.CreateClient(NamedClient.Default)
|
|
||||||
.SendAsync(requestMessage, cancellationToken)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
return await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return File.OpenRead(info.Url);
|
if (!info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return File.OpenRead(info.Url);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
|
||||||
|
if (!string.IsNullOrEmpty(info.UserAgent))
|
||||||
|
{
|
||||||
|
requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set HttpCompletionOption.ResponseHeadersRead to prevent timeouts on larger files
|
||||||
|
var response = await _httpClientFactory.CreateClient(NamedClient.Default)
|
||||||
|
.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
return await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<List<ChannelInfo>> GetChannelsAsync(TextReader reader, string channelIdPrefix, string tunerHostId)
|
private async Task<List<ChannelInfo>> GetChannelsAsync(TextReader reader, string channelIdPrefix, string tunerHostId)
|
||||||
@ -83,7 +89,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
if (trimmedLine.StartsWith(ExtInfPrefix, StringComparison.OrdinalIgnoreCase))
|
if (trimmedLine.StartsWith(ExtInfPrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
extInf = trimmedLine.Substring(ExtInfPrefix.Length).Trim();
|
extInf = trimmedLine.Substring(ExtInfPrefix.Length).Trim();
|
||||||
_logger.LogInformation("Found m3u channel: {0}", extInf);
|
|
||||||
}
|
}
|
||||||
else if (!string.IsNullOrWhiteSpace(extInf) && !trimmedLine.StartsWith('#'))
|
else if (!string.IsNullOrWhiteSpace(extInf) && !trimmedLine.StartsWith('#'))
|
||||||
{
|
{
|
||||||
@ -99,6 +104,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
|
|
||||||
channel.Path = trimmedLine;
|
channel.Path = trimmedLine;
|
||||||
channels.Add(channel);
|
channels.Add(channel);
|
||||||
|
_logger.LogInformation("Parsed channel: {ChannelName}", channel.Name);
|
||||||
extInf = string.Empty;
|
extInf = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,11 +295,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes.TryGetValue("tvg-name", out string name);
|
string name = nameInExtInf;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
{
|
{
|
||||||
name = nameInExtInf;
|
attributes.TryGetValue("tvg-name", out name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
|
@ -2,24 +2,24 @@
|
|||||||
"Artists": "Kunstenare",
|
"Artists": "Kunstenare",
|
||||||
"Channels": "Kanale",
|
"Channels": "Kanale",
|
||||||
"Folders": "Lêergidse",
|
"Folders": "Lêergidse",
|
||||||
"Favorites": "Gunstellinge",
|
"Favorites": "Gunstelinge",
|
||||||
"HeaderFavoriteShows": "Gunsteling Vertonings",
|
"HeaderFavoriteShows": "Gunsteling Vertonings",
|
||||||
"ValueSpecialEpisodeName": "Spesiale - {0}",
|
"ValueSpecialEpisodeName": "Spesiale - {0}",
|
||||||
"HeaderAlbumArtists": "Album Kunstenaars",
|
"HeaderAlbumArtists": "Kunstenaars se Album",
|
||||||
"Books": "Boeke",
|
"Books": "Boeke",
|
||||||
"HeaderNextUp": "Volgende",
|
"HeaderNextUp": "Volgende",
|
||||||
"Movies": "Flieks",
|
"Movies": "Flieks",
|
||||||
"Shows": "Televisie Reekse",
|
"Shows": "Televisie Reekse",
|
||||||
"HeaderContinueWatching": "Kyk Verder",
|
"HeaderContinueWatching": "Kyk Verder",
|
||||||
"HeaderFavoriteEpisodes": "Gunsteling Episodes",
|
"HeaderFavoriteEpisodes": "Gunsteling Episodes",
|
||||||
"Photos": "Fotos",
|
"Photos": "Foto's",
|
||||||
"Playlists": "Snitlyste",
|
"Playlists": "Snitlyste",
|
||||||
"HeaderFavoriteArtists": "Gunsteling Kunstenaars",
|
"HeaderFavoriteArtists": "Gunsteling Kunstenaars",
|
||||||
"HeaderFavoriteAlbums": "Gunsteling Albums",
|
"HeaderFavoriteAlbums": "Gunsteling Albums",
|
||||||
"Sync": "Sinkroniseer",
|
"Sync": "Sinkroniseer",
|
||||||
"HeaderFavoriteSongs": "Gunsteling Liedjies",
|
"HeaderFavoriteSongs": "Gunsteling Liedjies",
|
||||||
"Songs": "Liedjies",
|
"Songs": "Liedjies",
|
||||||
"DeviceOnlineWithName": "{0} gekoppel is",
|
"DeviceOnlineWithName": "{0} is gekoppel",
|
||||||
"DeviceOfflineWithName": "{0} is ontkoppel",
|
"DeviceOfflineWithName": "{0} is ontkoppel",
|
||||||
"Collections": "Versamelings",
|
"Collections": "Versamelings",
|
||||||
"Inherit": "Ontvang",
|
"Inherit": "Ontvang",
|
||||||
@ -71,7 +71,7 @@
|
|||||||
"NameSeasonUnknown": "Seisoen Onbekend",
|
"NameSeasonUnknown": "Seisoen Onbekend",
|
||||||
"NameSeasonNumber": "Seisoen {0}",
|
"NameSeasonNumber": "Seisoen {0}",
|
||||||
"NameInstallFailed": "{0} installering het misluk",
|
"NameInstallFailed": "{0} installering het misluk",
|
||||||
"MusicVideos": "Musiek videos",
|
"MusicVideos": "Musiek Videos",
|
||||||
"Music": "Musiek",
|
"Music": "Musiek",
|
||||||
"MixedContent": "Gemengde inhoud",
|
"MixedContent": "Gemengde inhoud",
|
||||||
"MessageServerConfigurationUpdated": "Bediener konfigurasie is opgedateer",
|
"MessageServerConfigurationUpdated": "Bediener konfigurasie is opgedateer",
|
||||||
@ -79,15 +79,15 @@
|
|||||||
"MessageApplicationUpdatedTo": "Jellyfin Bediener is opgedateer na {0}",
|
"MessageApplicationUpdatedTo": "Jellyfin Bediener is opgedateer na {0}",
|
||||||
"MessageApplicationUpdated": "Jellyfin Bediener is opgedateer",
|
"MessageApplicationUpdated": "Jellyfin Bediener is opgedateer",
|
||||||
"Latest": "Nuutste",
|
"Latest": "Nuutste",
|
||||||
"LabelRunningTimeValue": "Lopende tyd: {0}",
|
"LabelRunningTimeValue": "Werktyd: {0}",
|
||||||
"LabelIpAddressValue": "IP adres: {0}",
|
"LabelIpAddressValue": "IP adres: {0}",
|
||||||
"ItemRemovedWithName": "{0} is uit versameling verwyder",
|
"ItemRemovedWithName": "{0} is uit versameling verwyder",
|
||||||
"ItemAddedWithName": "{0} is in die versameling",
|
"ItemAddedWithName": "{0} is by die versameling gevoeg",
|
||||||
"HomeVideos": "Tuis opnames",
|
"HomeVideos": "Tuis Videos",
|
||||||
"HeaderRecordingGroups": "Groep Opnames",
|
"HeaderRecordingGroups": "Groep Opnames",
|
||||||
"Genres": "Genres",
|
"Genres": "Genres",
|
||||||
"FailedLoginAttemptWithUserName": "Mislukte aansluiting van {0}",
|
"FailedLoginAttemptWithUserName": "Mislukte aansluiting van {0}",
|
||||||
"ChapterNameValue": "Hoofstuk",
|
"ChapterNameValue": "Hoofstuk {0}",
|
||||||
"CameraImageUploadedFrom": "'n Nuwe kamera photo opgelaai van {0}",
|
"CameraImageUploadedFrom": "'n Nuwe kamera photo opgelaai van {0}",
|
||||||
"AuthenticationSucceededWithUserName": "{0} suksesvol geverifieer",
|
"AuthenticationSucceededWithUserName": "{0} suksesvol geverifieer",
|
||||||
"Albums": "Albums",
|
"Albums": "Albums",
|
||||||
@ -117,5 +117,7 @@
|
|||||||
"Forced": "Geforseer",
|
"Forced": "Geforseer",
|
||||||
"Default": "Oorspronklik",
|
"Default": "Oorspronklik",
|
||||||
"TaskCleanActivityLogDescription": "Verwyder aktiwiteitsaantekeninge ouer as die opgestelde ouderdom.",
|
"TaskCleanActivityLogDescription": "Verwyder aktiwiteitsaantekeninge ouer as die opgestelde ouderdom.",
|
||||||
"TaskCleanActivityLog": "Maak Aktiwiteitsaantekeninge Skoon"
|
"TaskCleanActivityLog": "Maak Aktiwiteitsaantekeninge Skoon",
|
||||||
|
"TaskOptimizeDatabaseDescription": "Komprimeer databasis en verkort vrye ruimte. As hierdie taak uitgevoer word nadat die media versameling geskandeer is of ander veranderings aangebring is wat databasisaanpassings impliseer, kan dit die prestasie verbeter.",
|
||||||
|
"TaskOptimizeDatabase": "Optimaliseer databasis"
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"HeaderLiveTV": "Телевизия на живо",
|
"HeaderLiveTV": "Телевизия на живо",
|
||||||
"HeaderNextUp": "Следва",
|
"HeaderNextUp": "Следва",
|
||||||
"HeaderRecordingGroups": "Запис групи",
|
"HeaderRecordingGroups": "Запис групи",
|
||||||
"HomeVideos": "Домашни клипове",
|
"HomeVideos": "Домашни Клипове",
|
||||||
"Inherit": "Наследяване",
|
"Inherit": "Наследяване",
|
||||||
"ItemAddedWithName": "{0} е добавено към библиотеката",
|
"ItemAddedWithName": "{0} е добавено към библиотеката",
|
||||||
"ItemRemovedWithName": "{0} е премахнато от библиотеката",
|
"ItemRemovedWithName": "{0} е премахнато от библиотеката",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"MixedContent": "Смесено съдържание",
|
"MixedContent": "Смесено съдържание",
|
||||||
"Movies": "Филми",
|
"Movies": "Филми",
|
||||||
"Music": "Музика",
|
"Music": "Музика",
|
||||||
"MusicVideos": "Музикални видеа",
|
"MusicVideos": "Музикални Видеа",
|
||||||
"NameInstallFailed": "{0} не можа да се инсталира",
|
"NameInstallFailed": "{0} не можа да се инсталира",
|
||||||
"NameSeasonNumber": "Сезон {0}",
|
"NameSeasonNumber": "Сезон {0}",
|
||||||
"NameSeasonUnknown": "Неразпознат сезон",
|
"NameSeasonUnknown": "Неразпознат сезон",
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"Artists": "Artistes",
|
"Artists": "Artistes",
|
||||||
"AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament",
|
"AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament",
|
||||||
"Books": "Llibres",
|
"Books": "Llibres",
|
||||||
"CameraImageUploadedFrom": "Una nova imatge de la càmera ha estat pujada des de {0}",
|
"CameraImageUploadedFrom": "S'ha pujat una nova imatge des de la camera desde {0}",
|
||||||
"Channels": "Canals",
|
"Channels": "Canals",
|
||||||
"ChapterNameValue": "Capítol {0}",
|
"ChapterNameValue": "Capítol {0}",
|
||||||
"Collections": "Col·leccions",
|
"Collections": "Col·leccions",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Oblíbené",
|
"Favorites": "Oblíbené",
|
||||||
"Folders": "Složky",
|
"Folders": "Složky",
|
||||||
"Genres": "Žánry",
|
"Genres": "Žánry",
|
||||||
"HeaderAlbumArtists": "Umělci alba",
|
"HeaderAlbumArtists": "Album umělce",
|
||||||
"HeaderContinueWatching": "Pokračovat ve sledování",
|
"HeaderContinueWatching": "Pokračovat ve sledování",
|
||||||
"HeaderFavoriteAlbums": "Oblíbená alba",
|
"HeaderFavoriteAlbums": "Oblíbená alba",
|
||||||
"HeaderFavoriteArtists": "Oblíbení interpreti",
|
"HeaderFavoriteArtists": "Oblíbení interpreti",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"Albums": "Άλμπουμς",
|
"Albums": "Άλμπουμ",
|
||||||
"AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}",
|
"AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}",
|
||||||
"Application": "Εφαρμογή",
|
"Application": "Εφαρμογή",
|
||||||
"Artists": "Καλλιτέχνες",
|
"Artists": "Καλλιτέχνες",
|
||||||
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Αγαπημένα",
|
"Favorites": "Αγαπημένα",
|
||||||
"Folders": "Φάκελοι",
|
"Folders": "Φάκελοι",
|
||||||
"Genres": "Είδη",
|
"Genres": "Είδη",
|
||||||
"HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ",
|
"HeaderAlbumArtists": "Άλμπουμ Καλλιτέχνη",
|
||||||
"HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
|
"HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
|
||||||
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
|
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
|
||||||
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
|
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"MixedContent": "Ανάμεικτο Περιεχόμενο",
|
"MixedContent": "Ανάμεικτο Περιεχόμενο",
|
||||||
"Movies": "Ταινίες",
|
"Movies": "Ταινίες",
|
||||||
"Music": "Μουσική",
|
"Music": "Μουσική",
|
||||||
"MusicVideos": "Μουσικά βίντεο",
|
"MusicVideos": "Μουσικά Βίντεο",
|
||||||
"NameInstallFailed": "{0} η εγκατάσταση απέτυχε",
|
"NameInstallFailed": "{0} η εγκατάσταση απέτυχε",
|
||||||
"NameSeasonNumber": "Κύκλος {0}",
|
"NameSeasonNumber": "Κύκλος {0}",
|
||||||
"NameSeasonUnknown": "Άγνωστος Κύκλος",
|
"NameSeasonUnknown": "Άγνωστος Κύκλος",
|
||||||
@ -62,7 +62,7 @@
|
|||||||
"NotificationOptionVideoPlaybackStopped": "Η αναπαραγωγή βίντεο σταμάτησε",
|
"NotificationOptionVideoPlaybackStopped": "Η αναπαραγωγή βίντεο σταμάτησε",
|
||||||
"Photos": "Φωτογραφίες",
|
"Photos": "Φωτογραφίες",
|
||||||
"Playlists": "Λίστες αναπαραγωγής",
|
"Playlists": "Λίστες αναπαραγωγής",
|
||||||
"Plugin": "Plugin",
|
"Plugin": "Πρόσθετο",
|
||||||
"PluginInstalledWithName": "{0} εγκαταστήθηκε",
|
"PluginInstalledWithName": "{0} εγκαταστήθηκε",
|
||||||
"PluginUninstalledWithName": "{0} έχει απεγκατασταθεί",
|
"PluginUninstalledWithName": "{0} έχει απεγκατασταθεί",
|
||||||
"PluginUpdatedWithName": "{0} έχει αναβαθμιστεί",
|
"PluginUpdatedWithName": "{0} έχει αναβαθμιστεί",
|
||||||
@ -118,5 +118,7 @@
|
|||||||
"TaskCleanActivityLog": "Καθαρό Αρχείο Καταγραφής Δραστηριοτήτων",
|
"TaskCleanActivityLog": "Καθαρό Αρχείο Καταγραφής Δραστηριοτήτων",
|
||||||
"Undefined": "Απροσδιόριστο",
|
"Undefined": "Απροσδιόριστο",
|
||||||
"Forced": "Εξαναγκασμένο",
|
"Forced": "Εξαναγκασμένο",
|
||||||
"Default": "Προεπιλογή"
|
"Default": "Προεπιλογή",
|
||||||
|
"TaskOptimizeDatabaseDescription": "Συμπιέζει τη βάση δεδομένων και δημιουργεί ελεύθερο χώρο. Η εκτέλεση αυτής της εργασίας μετά τη σάρωση της βιβλιοθήκης ή την πραγματοποίηση άλλων αλλαγών που συνεπάγονται τροποποιήσεις της βάσης δεδομένων μπορεί να βελτιώσει την απόδοση.",
|
||||||
|
"TaskOptimizeDatabase": "Βελτιστοποίηση βάσης δεδομένων"
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"Folders": "Folders",
|
"Folders": "Folders",
|
||||||
"Forced": "Forced",
|
"Forced": "Forced",
|
||||||
"Genres": "Genres",
|
"Genres": "Genres",
|
||||||
"HeaderAlbumArtists": "Album Artists",
|
"HeaderAlbumArtists": "Artist's Album",
|
||||||
"HeaderContinueWatching": "Continue Watching",
|
"HeaderContinueWatching": "Continue Watching",
|
||||||
"HeaderFavoriteAlbums": "Favorite Albums",
|
"HeaderFavoriteAlbums": "Favorite Albums",
|
||||||
"HeaderFavoriteArtists": "Favorite Artists",
|
"HeaderFavoriteArtists": "Favorite Artists",
|
||||||
@ -27,7 +27,7 @@
|
|||||||
"HeaderLiveTV": "Live TV",
|
"HeaderLiveTV": "Live TV",
|
||||||
"HeaderNextUp": "Next Up",
|
"HeaderNextUp": "Next Up",
|
||||||
"HeaderRecordingGroups": "Recording Groups",
|
"HeaderRecordingGroups": "Recording Groups",
|
||||||
"HomeVideos": "Home videos",
|
"HomeVideos": "Home Videos",
|
||||||
"Inherit": "Inherit",
|
"Inherit": "Inherit",
|
||||||
"ItemAddedWithName": "{0} was added to the library",
|
"ItemAddedWithName": "{0} was added to the library",
|
||||||
"ItemRemovedWithName": "{0} was removed from the library",
|
"ItemRemovedWithName": "{0} was removed from the library",
|
||||||
@ -41,7 +41,7 @@
|
|||||||
"MixedContent": "Mixed content",
|
"MixedContent": "Mixed content",
|
||||||
"Movies": "Movies",
|
"Movies": "Movies",
|
||||||
"Music": "Music",
|
"Music": "Music",
|
||||||
"MusicVideos": "Music videos",
|
"MusicVideos": "Music Videos",
|
||||||
"NameInstallFailed": "{0} installation failed",
|
"NameInstallFailed": "{0} installation failed",
|
||||||
"NameSeasonNumber": "Season {0}",
|
"NameSeasonNumber": "Season {0}",
|
||||||
"NameSeasonUnknown": "Season Unknown",
|
"NameSeasonUnknown": "Season Unknown",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Favoritos",
|
"Favorites": "Favoritos",
|
||||||
"Folders": "Carpetas",
|
"Folders": "Carpetas",
|
||||||
"Genres": "Géneros",
|
"Genres": "Géneros",
|
||||||
"HeaderAlbumArtists": "Artistas del álbum",
|
"HeaderAlbumArtists": "Artistas del Álbum",
|
||||||
"HeaderContinueWatching": "Continuar viendo",
|
"HeaderContinueWatching": "Continuar viendo",
|
||||||
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
||||||
"HeaderFavoriteArtists": "Artistas favoritos",
|
"HeaderFavoriteArtists": "Artistas favoritos",
|
||||||
@ -25,7 +25,7 @@
|
|||||||
"HeaderLiveTV": "TV en vivo",
|
"HeaderLiveTV": "TV en vivo",
|
||||||
"HeaderNextUp": "A continuación",
|
"HeaderNextUp": "A continuación",
|
||||||
"HeaderRecordingGroups": "Grupos de grabación",
|
"HeaderRecordingGroups": "Grupos de grabación",
|
||||||
"HomeVideos": "Videos caseros",
|
"HomeVideos": "Videos Caseros",
|
||||||
"Inherit": "Heredar",
|
"Inherit": "Heredar",
|
||||||
"ItemAddedWithName": "{0} fue agregado a la biblioteca",
|
"ItemAddedWithName": "{0} fue agregado a la biblioteca",
|
||||||
"ItemRemovedWithName": "{0} fue removido de la biblioteca",
|
"ItemRemovedWithName": "{0} fue removido de la biblioteca",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"MixedContent": "Contenido mezclado",
|
"MixedContent": "Contenido mezclado",
|
||||||
"Movies": "Películas",
|
"Movies": "Películas",
|
||||||
"Music": "Música",
|
"Music": "Música",
|
||||||
"MusicVideos": "Videos musicales",
|
"MusicVideos": "Videos Musicales",
|
||||||
"NameInstallFailed": "Falló la instalación de {0}",
|
"NameInstallFailed": "Falló la instalación de {0}",
|
||||||
"NameSeasonNumber": "Temporada {0}",
|
"NameSeasonNumber": "Temporada {0}",
|
||||||
"NameSeasonUnknown": "Temporada desconocida",
|
"NameSeasonUnknown": "Temporada desconocida",
|
||||||
@ -49,7 +49,7 @@
|
|||||||
"NotificationOptionAudioPlayback": "Reproducción de audio iniciada",
|
"NotificationOptionAudioPlayback": "Reproducción de audio iniciada",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida",
|
"NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida",
|
||||||
"NotificationOptionCameraImageUploaded": "Imagen de la cámara subida",
|
"NotificationOptionCameraImageUploaded": "Imagen de la cámara subida",
|
||||||
"NotificationOptionInstallationFailed": "Falla de instalación",
|
"NotificationOptionInstallationFailed": "Fallo en la instalación",
|
||||||
"NotificationOptionNewLibraryContent": "Nuevo contenido agregado",
|
"NotificationOptionNewLibraryContent": "Nuevo contenido agregado",
|
||||||
"NotificationOptionPluginError": "Falla de complemento",
|
"NotificationOptionPluginError": "Falla de complemento",
|
||||||
"NotificationOptionPluginInstalled": "Complemento instalado",
|
"NotificationOptionPluginInstalled": "Complemento instalado",
|
||||||
@ -69,7 +69,7 @@
|
|||||||
"ProviderValue": "Proveedor: {0}",
|
"ProviderValue": "Proveedor: {0}",
|
||||||
"ScheduledTaskFailedWithName": "{0} falló",
|
"ScheduledTaskFailedWithName": "{0} falló",
|
||||||
"ScheduledTaskStartedWithName": "{0} iniciado",
|
"ScheduledTaskStartedWithName": "{0} iniciado",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} debe ser reiniciado",
|
"ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
|
||||||
"Shows": "Programas",
|
"Shows": "Programas",
|
||||||
"Songs": "Canciones",
|
"Songs": "Canciones",
|
||||||
"StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.",
|
"StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.",
|
||||||
@ -94,9 +94,9 @@
|
|||||||
"VersionNumber": "Versión {0}",
|
"VersionNumber": "Versión {0}",
|
||||||
"TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.",
|
"TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.",
|
||||||
"TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes",
|
"TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes",
|
||||||
"TaskRefreshChannelsDescription": "Actualiza la información de canales de Internet.",
|
"TaskRefreshChannelsDescription": "Actualiza la información de los canales de Internet.",
|
||||||
"TaskRefreshChannels": "Actualizar canales",
|
"TaskRefreshChannels": "Actualizar canales",
|
||||||
"TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día.",
|
"TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día de antigüedad.",
|
||||||
"TaskCleanTranscode": "Limpiar directorio de transcodificado",
|
"TaskCleanTranscode": "Limpiar directorio de transcodificado",
|
||||||
"TaskUpdatePluginsDescription": "Descarga e instala actualizaciones para complementos que están configurados para actualizarse automáticamente.",
|
"TaskUpdatePluginsDescription": "Descarga e instala actualizaciones para complementos que están configurados para actualizarse automáticamente.",
|
||||||
"TaskUpdatePlugins": "Actualizar complementos",
|
"TaskUpdatePlugins": "Actualizar complementos",
|
||||||
@ -118,5 +118,7 @@
|
|||||||
"TaskCleanActivityLog": "Limpiar registro de actividades",
|
"TaskCleanActivityLog": "Limpiar registro de actividades",
|
||||||
"Undefined": "Sin definir",
|
"Undefined": "Sin definir",
|
||||||
"Forced": "Forzado",
|
"Forced": "Forzado",
|
||||||
"Default": "Predeterminado"
|
"Default": "Predeterminado",
|
||||||
|
"TaskOptimizeDatabase": "Optimizar base de datos",
|
||||||
|
"TaskOptimizeDatabaseDescription": "Compacta la base de datos y trunca el espacio libre. Puede mejorar el rendimiento si se realiza esta tarea después de escanear la biblioteca o después de realizar otros cambios que impliquen modificar la base de datos."
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Favoritos",
|
"Favorites": "Favoritos",
|
||||||
"Folders": "Carpetas",
|
"Folders": "Carpetas",
|
||||||
"Genres": "Géneros",
|
"Genres": "Géneros",
|
||||||
"HeaderAlbumArtists": "Artistas del álbum",
|
"HeaderAlbumArtists": "Artista del álbum",
|
||||||
"HeaderContinueWatching": "Continuar viendo",
|
"HeaderContinueWatching": "Continuar viendo",
|
||||||
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
||||||
"HeaderFavoriteArtists": "Artistas favoritos",
|
"HeaderFavoriteArtists": "Artistas favoritos",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Kedvencek",
|
"Favorites": "Kedvencek",
|
||||||
"Folders": "Könyvtárak",
|
"Folders": "Könyvtárak",
|
||||||
"Genres": "Műfajok",
|
"Genres": "Műfajok",
|
||||||
"HeaderAlbumArtists": "Album előadók",
|
"HeaderAlbumArtists": "Előadó albumai",
|
||||||
"HeaderContinueWatching": "Megtekintés folytatása",
|
"HeaderContinueWatching": "Megtekintés folytatása",
|
||||||
"HeaderFavoriteAlbums": "Kedvenc albumok",
|
"HeaderFavoriteAlbums": "Kedvenc albumok",
|
||||||
"HeaderFavoriteArtists": "Kedvenc előadók",
|
"HeaderFavoriteArtists": "Kedvenc előadók",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Preferiti",
|
"Favorites": "Preferiti",
|
||||||
"Folders": "Cartelle",
|
"Folders": "Cartelle",
|
||||||
"Genres": "Generi",
|
"Genres": "Generi",
|
||||||
"HeaderAlbumArtists": "Artisti degli Album",
|
"HeaderAlbumArtists": "Artisti dell'Album",
|
||||||
"HeaderContinueWatching": "Continua a guardare",
|
"HeaderContinueWatching": "Continua a guardare",
|
||||||
"HeaderFavoriteAlbums": "Album Preferiti",
|
"HeaderFavoriteAlbums": "Album Preferiti",
|
||||||
"HeaderFavoriteArtists": "Artisti Preferiti",
|
"HeaderFavoriteArtists": "Artisti Preferiti",
|
||||||
@ -25,7 +25,7 @@
|
|||||||
"HeaderLiveTV": "Diretta TV",
|
"HeaderLiveTV": "Diretta TV",
|
||||||
"HeaderNextUp": "Prossimo",
|
"HeaderNextUp": "Prossimo",
|
||||||
"HeaderRecordingGroups": "Gruppi di Registrazione",
|
"HeaderRecordingGroups": "Gruppi di Registrazione",
|
||||||
"HomeVideos": "Video personali",
|
"HomeVideos": "Video Personali",
|
||||||
"Inherit": "Eredita",
|
"Inherit": "Eredita",
|
||||||
"ItemAddedWithName": "{0} è stato aggiunto alla libreria",
|
"ItemAddedWithName": "{0} è stato aggiunto alla libreria",
|
||||||
"ItemRemovedWithName": "{0} è stato rimosso dalla libreria",
|
"ItemRemovedWithName": "{0} è stato rimosso dalla libreria",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"MixedContent": "Contenuto misto",
|
"MixedContent": "Contenuto misto",
|
||||||
"Movies": "Film",
|
"Movies": "Film",
|
||||||
"Music": "Musica",
|
"Music": "Musica",
|
||||||
"MusicVideos": "Video musicali",
|
"MusicVideos": "Video Musicali",
|
||||||
"NameInstallFailed": "{0} installazione fallita",
|
"NameInstallFailed": "{0} installazione fallita",
|
||||||
"NameSeasonNumber": "Stagione {0}",
|
"NameSeasonNumber": "Stagione {0}",
|
||||||
"NameSeasonUnknown": "Stagione sconosciuta",
|
"NameSeasonUnknown": "Stagione sconosciuta",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "お気に入り",
|
"Favorites": "お気に入り",
|
||||||
"Folders": "フォルダー",
|
"Folders": "フォルダー",
|
||||||
"Genres": "ジャンル",
|
"Genres": "ジャンル",
|
||||||
"HeaderAlbumArtists": "アルバムアーティスト",
|
"HeaderAlbumArtists": "アーティストのアルバム",
|
||||||
"HeaderContinueWatching": "視聴を続ける",
|
"HeaderContinueWatching": "視聴を続ける",
|
||||||
"HeaderFavoriteAlbums": "お気に入りのアルバム",
|
"HeaderFavoriteAlbums": "お気に入りのアルバム",
|
||||||
"HeaderFavoriteArtists": "お気に入りのアーティスト",
|
"HeaderFavoriteArtists": "お気に入りのアーティスト",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Tañdaulylar",
|
"Favorites": "Tañdaulylar",
|
||||||
"Folders": "Qaltalar",
|
"Folders": "Qaltalar",
|
||||||
"Genres": "Janrlar",
|
"Genres": "Janrlar",
|
||||||
"HeaderAlbumArtists": "Älbom oryndauşylary",
|
"HeaderAlbumArtists": "Oryndauşynyñ älbomy",
|
||||||
"HeaderContinueWatching": "Qaraudy jalğastyru",
|
"HeaderContinueWatching": "Qaraudy jalğastyru",
|
||||||
"HeaderFavoriteAlbums": "Tañdauly älbomdar",
|
"HeaderFavoriteAlbums": "Tañdauly älbomdar",
|
||||||
"HeaderFavoriteArtists": "Tañdauly oryndauşylar",
|
"HeaderFavoriteArtists": "Tañdauly oryndauşylar",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "즐겨찾기",
|
"Favorites": "즐겨찾기",
|
||||||
"Folders": "폴더",
|
"Folders": "폴더",
|
||||||
"Genres": "장르",
|
"Genres": "장르",
|
||||||
"HeaderAlbumArtists": "앨범 아티스트",
|
"HeaderAlbumArtists": "아티스트의 앨범",
|
||||||
"HeaderContinueWatching": "계속 시청하기",
|
"HeaderContinueWatching": "계속 시청하기",
|
||||||
"HeaderFavoriteAlbums": "즐겨찾는 앨범",
|
"HeaderFavoriteAlbums": "즐겨찾는 앨범",
|
||||||
"HeaderFavoriteArtists": "즐겨찾는 아티스트",
|
"HeaderFavoriteArtists": "즐겨찾는 아티스트",
|
||||||
|
@ -103,7 +103,7 @@
|
|||||||
"ValueSpecialEpisodeName": "പ്രത്യേക - {0}",
|
"ValueSpecialEpisodeName": "പ്രത്യേക - {0}",
|
||||||
"Collections": "ശേഖരങ്ങൾ",
|
"Collections": "ശേഖരങ്ങൾ",
|
||||||
"Folders": "ഫോൾഡറുകൾ",
|
"Folders": "ഫോൾഡറുകൾ",
|
||||||
"HeaderAlbumArtists": "ആൽബം ആർട്ടിസ്റ്റുകൾ",
|
"HeaderAlbumArtists": "കലാകാരന്റെ ആൽബം",
|
||||||
"Sync": "സമന്വയിപ്പിക്കുക",
|
"Sync": "സമന്വയിപ്പിക്കുക",
|
||||||
"Movies": "സിനിമകൾ",
|
"Movies": "സിനിമകൾ",
|
||||||
"Photos": "ഫോട്ടോകൾ",
|
"Photos": "ഫോട്ടോകൾ",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Ulubione",
|
"Favorites": "Ulubione",
|
||||||
"Folders": "Foldery",
|
"Folders": "Foldery",
|
||||||
"Genres": "Gatunki",
|
"Genres": "Gatunki",
|
||||||
"HeaderAlbumArtists": "Wykonawcy albumów",
|
"HeaderAlbumArtists": "Album artysty",
|
||||||
"HeaderContinueWatching": "Kontynuuj odtwarzanie",
|
"HeaderContinueWatching": "Kontynuuj odtwarzanie",
|
||||||
"HeaderFavoriteAlbums": "Ulubione albumy",
|
"HeaderFavoriteAlbums": "Ulubione albumy",
|
||||||
"HeaderFavoriteArtists": "Ulubieni wykonawcy",
|
"HeaderFavoriteArtists": "Ulubieni wykonawcy",
|
||||||
@ -25,7 +25,7 @@
|
|||||||
"HeaderLiveTV": "Telewizja",
|
"HeaderLiveTV": "Telewizja",
|
||||||
"HeaderNextUp": "Do obejrzenia",
|
"HeaderNextUp": "Do obejrzenia",
|
||||||
"HeaderRecordingGroups": "Grupy nagrań",
|
"HeaderRecordingGroups": "Grupy nagrań",
|
||||||
"HomeVideos": "Nagrania prywatne",
|
"HomeVideos": "Nagrania domowe",
|
||||||
"Inherit": "Dziedzicz",
|
"Inherit": "Dziedzicz",
|
||||||
"ItemAddedWithName": "{0} zostało dodane do biblioteki",
|
"ItemAddedWithName": "{0} zostało dodane do biblioteki",
|
||||||
"ItemRemovedWithName": "{0} zostało usunięte z biblioteki",
|
"ItemRemovedWithName": "{0} zostało usunięte z biblioteki",
|
||||||
@ -119,5 +119,6 @@
|
|||||||
"Undefined": "Nieustalony",
|
"Undefined": "Nieustalony",
|
||||||
"Forced": "Wymuszony",
|
"Forced": "Wymuszony",
|
||||||
"Default": "Domyślne",
|
"Default": "Domyślne",
|
||||||
"TaskOptimizeDatabase": "Optymalizuj bazę danych"
|
"TaskOptimizeDatabase": "Optymalizuj bazę danych",
|
||||||
|
"TaskOptimizeDatabaseDescription": "Kompaktuje bazę danych i obcina wolne miejsce. Uruchomienie tego zadania po przeskanowaniu biblioteki lub dokonaniu innych zmian, które pociągają za sobą modyfikacje bazy danych, może poprawić wydajność."
|
||||||
}
|
}
|
||||||
|
1
Emby.Server.Implementations/Localization/Core/pr.json
Normal file
1
Emby.Server.Implementations/Localization/Core/pr.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
@ -118,5 +118,7 @@
|
|||||||
"TaskCleanActivityLog": "Limpar Registro de Atividades",
|
"TaskCleanActivityLog": "Limpar Registro de Atividades",
|
||||||
"Undefined": "Indefinido",
|
"Undefined": "Indefinido",
|
||||||
"Forced": "Forçado",
|
"Forced": "Forçado",
|
||||||
"Default": "Padrão"
|
"Default": "Padrão",
|
||||||
|
"TaskOptimizeDatabaseDescription": "Compactar base de dados e liberar espaço livre. Executar esta tarefa após realizar mudanças que impliquem em modificações da base de dados pode trazer melhorias de desempenho.",
|
||||||
|
"TaskOptimizeDatabase": "Otimizar base de dados"
|
||||||
}
|
}
|
||||||
|
@ -117,5 +117,6 @@
|
|||||||
"Undefined": "Indefinido",
|
"Undefined": "Indefinido",
|
||||||
"Forced": "Forçado",
|
"Forced": "Forçado",
|
||||||
"Default": "Predefinição",
|
"Default": "Predefinição",
|
||||||
"TaskCleanActivityLogDescription": "Apaga itens no registro com idade acima do que é configurado."
|
"TaskCleanActivityLogDescription": "Apaga itens no registro com idade acima do que é configurado.",
|
||||||
|
"TaskOptimizeDatabase": "Otimizar base de dados"
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"HeaderLiveTV": "Эфир",
|
"HeaderLiveTV": "Эфир",
|
||||||
"HeaderNextUp": "Очередное",
|
"HeaderNextUp": "Очередное",
|
||||||
"HeaderRecordingGroups": "Группы записей",
|
"HeaderRecordingGroups": "Группы записей",
|
||||||
"HomeVideos": "Домашнее видео",
|
"HomeVideos": "Домашние видео",
|
||||||
"Inherit": "Наследуемое",
|
"Inherit": "Наследуемое",
|
||||||
"ItemAddedWithName": "{0} - добавлено в медиатеку",
|
"ItemAddedWithName": "{0} - добавлено в медиатеку",
|
||||||
"ItemRemovedWithName": "{0} - изъято из медиатеки",
|
"ItemRemovedWithName": "{0} - изъято из медиатеки",
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
"MixedContent": "Zmiešaný obsah",
|
"MixedContent": "Zmiešaný obsah",
|
||||||
"Movies": "Filmy",
|
"Movies": "Filmy",
|
||||||
"Music": "Hudba",
|
"Music": "Hudba",
|
||||||
"MusicVideos": "Hudobné videoklipy",
|
"MusicVideos": "Hudobné videá",
|
||||||
"NameInstallFailed": "Inštalácia {0} zlyhala",
|
"NameInstallFailed": "Inštalácia {0} zlyhala",
|
||||||
"NameSeasonNumber": "Séria {0}",
|
"NameSeasonNumber": "Séria {0}",
|
||||||
"NameSeasonUnknown": "Neznáma séria",
|
"NameSeasonUnknown": "Neznáma séria",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Favoriter",
|
"Favorites": "Favoriter",
|
||||||
"Folders": "Mappar",
|
"Folders": "Mappar",
|
||||||
"Genres": "Genrer",
|
"Genres": "Genrer",
|
||||||
"HeaderAlbumArtists": "Albumartister",
|
"HeaderAlbumArtists": "Artistens album",
|
||||||
"HeaderContinueWatching": "Fortsätt kolla",
|
"HeaderContinueWatching": "Fortsätt kolla",
|
||||||
"HeaderFavoriteAlbums": "Favoritalbum",
|
"HeaderFavoriteAlbums": "Favoritalbum",
|
||||||
"HeaderFavoriteArtists": "Favoritartister",
|
"HeaderFavoriteArtists": "Favoritartister",
|
||||||
@ -25,7 +25,7 @@
|
|||||||
"HeaderLiveTV": "Live-TV",
|
"HeaderLiveTV": "Live-TV",
|
||||||
"HeaderNextUp": "Nästa",
|
"HeaderNextUp": "Nästa",
|
||||||
"HeaderRecordingGroups": "Inspelningsgrupper",
|
"HeaderRecordingGroups": "Inspelningsgrupper",
|
||||||
"HomeVideos": "Hemvideor",
|
"HomeVideos": "Hemmavideor",
|
||||||
"Inherit": "Ärv",
|
"Inherit": "Ärv",
|
||||||
"ItemAddedWithName": "{0} lades till i biblioteket",
|
"ItemAddedWithName": "{0} lades till i biblioteket",
|
||||||
"ItemRemovedWithName": "{0} togs bort från biblioteket",
|
"ItemRemovedWithName": "{0} togs bort från biblioteket",
|
||||||
@ -118,5 +118,7 @@
|
|||||||
"TaskCleanActivityLog": "Rensa Aktivitets Logg",
|
"TaskCleanActivityLog": "Rensa Aktivitets Logg",
|
||||||
"Undefined": "odefinierad",
|
"Undefined": "odefinierad",
|
||||||
"Forced": "Tvingad",
|
"Forced": "Tvingad",
|
||||||
"Default": "Standard"
|
"Default": "Standard",
|
||||||
|
"TaskOptimizeDatabase": "Optimera databasen",
|
||||||
|
"TaskOptimizeDatabaseDescription": "Komprimerar databasen och trunkerar ledigt utrymme. Prestandan kan förbättras genom att köra denna task efter att du har skannat biblioteket eller gjort andra förändringar som indikerar att databasen har modifierats."
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"HeaderLiveTV": "Canlı TV",
|
"HeaderLiveTV": "Canlı TV",
|
||||||
"HeaderNextUp": "Gelecek Hafta",
|
"HeaderNextUp": "Gelecek Hafta",
|
||||||
"HeaderRecordingGroups": "Kayıt Grupları",
|
"HeaderRecordingGroups": "Kayıt Grupları",
|
||||||
"HomeVideos": "Ev videoları",
|
"HomeVideos": "Ana sayfa videoları",
|
||||||
"Inherit": "Devral",
|
"Inherit": "Devral",
|
||||||
"ItemAddedWithName": "{0} kütüphaneye eklendi",
|
"ItemAddedWithName": "{0} kütüphaneye eklendi",
|
||||||
"ItemRemovedWithName": "{0} kütüphaneden silindi",
|
"ItemRemovedWithName": "{0} kütüphaneden silindi",
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"Favorites": "Yêu Thích",
|
"Favorites": "Yêu Thích",
|
||||||
"Folders": "Thư Mục",
|
"Folders": "Thư Mục",
|
||||||
"Genres": "Thể Loại",
|
"Genres": "Thể Loại",
|
||||||
"HeaderAlbumArtists": "Tuyển Tập Nghệ sĩ",
|
"HeaderAlbumArtists": "Album Nghệ sĩ",
|
||||||
"HeaderContinueWatching": "Xem Tiếp",
|
"HeaderContinueWatching": "Xem Tiếp",
|
||||||
"HeaderLiveTV": "TV Trực Tiếp",
|
"HeaderLiveTV": "TV Trực Tiếp",
|
||||||
"Movies": "Phim",
|
"Movies": "Phim",
|
||||||
@ -82,7 +82,7 @@
|
|||||||
"NameSeasonUnknown": "Không Rõ Mùa",
|
"NameSeasonUnknown": "Không Rõ Mùa",
|
||||||
"NameSeasonNumber": "Phần {0}",
|
"NameSeasonNumber": "Phần {0}",
|
||||||
"NameInstallFailed": "{0} cài đặt thất bại",
|
"NameInstallFailed": "{0} cài đặt thất bại",
|
||||||
"MusicVideos": "Video Nhạc",
|
"MusicVideos": "Videos Nhạc",
|
||||||
"Music": "Nhạc",
|
"Music": "Nhạc",
|
||||||
"MixedContent": "Nội dung hỗn hợp",
|
"MixedContent": "Nội dung hỗn hợp",
|
||||||
"MessageServerConfigurationUpdated": "Cấu hình máy chủ đã được cập nhật",
|
"MessageServerConfigurationUpdated": "Cấu hình máy chủ đã được cập nhật",
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"Books": "书籍",
|
"Books": "书籍",
|
||||||
"CameraImageUploadedFrom": "新的相机图像已从 {0} 上传",
|
"CameraImageUploadedFrom": "新的相机图像已从 {0} 上传",
|
||||||
"Channels": "频道",
|
"Channels": "频道",
|
||||||
"ChapterNameValue": "第 {0} 集",
|
"ChapterNameValue": "章节 {0}",
|
||||||
"Collections": "合集",
|
"Collections": "合集",
|
||||||
"DeviceOfflineWithName": "{0} 已断开",
|
"DeviceOfflineWithName": "{0} 已断开",
|
||||||
"DeviceOnlineWithName": "{0} 已连接",
|
"DeviceOnlineWithName": "{0} 已连接",
|
||||||
@ -15,8 +15,8 @@
|
|||||||
"Favorites": "我的最爱",
|
"Favorites": "我的最爱",
|
||||||
"Folders": "文件夹",
|
"Folders": "文件夹",
|
||||||
"Genres": "风格",
|
"Genres": "风格",
|
||||||
"HeaderAlbumArtists": "专辑作家",
|
"HeaderAlbumArtists": "专辑艺术家",
|
||||||
"HeaderContinueWatching": "继续观影",
|
"HeaderContinueWatching": "继续观看",
|
||||||
"HeaderFavoriteAlbums": "收藏的专辑",
|
"HeaderFavoriteAlbums": "收藏的专辑",
|
||||||
"HeaderFavoriteArtists": "最爱的艺术家",
|
"HeaderFavoriteArtists": "最爱的艺术家",
|
||||||
"HeaderFavoriteEpisodes": "最爱的剧集",
|
"HeaderFavoriteEpisodes": "最爱的剧集",
|
||||||
@ -108,8 +108,8 @@
|
|||||||
"TaskCleanLogs": "清理日志目录",
|
"TaskCleanLogs": "清理日志目录",
|
||||||
"TaskRefreshLibraryDescription": "扫描你的媒体库以获取新文件并刷新元数据。",
|
"TaskRefreshLibraryDescription": "扫描你的媒体库以获取新文件并刷新元数据。",
|
||||||
"TaskRefreshLibrary": "扫描媒体库",
|
"TaskRefreshLibrary": "扫描媒体库",
|
||||||
"TaskRefreshChapterImagesDescription": "为包含剧集的视频提取缩略图。",
|
"TaskRefreshChapterImagesDescription": "为包含章节的视频提取缩略图。",
|
||||||
"TaskRefreshChapterImages": "提取剧集图片",
|
"TaskRefreshChapterImages": "提取章节图片",
|
||||||
"TaskCleanCacheDescription": "删除系统不再需要的缓存文件。",
|
"TaskCleanCacheDescription": "删除系统不再需要的缓存文件。",
|
||||||
"TaskCleanCache": "清理缓存目录",
|
"TaskCleanCache": "清理缓存目录",
|
||||||
"TasksApplicationCategory": "应用程序",
|
"TasksApplicationCategory": "应用程序",
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
"DeviceOnlineWithName": "{0} 已經連接",
|
"DeviceOnlineWithName": "{0} 已經連接",
|
||||||
"FailedLoginAttemptWithUserName": "來自 {0} 的登入失敗",
|
"FailedLoginAttemptWithUserName": "來自 {0} 的登入失敗",
|
||||||
"Favorites": "我的最愛",
|
"Favorites": "我的最愛",
|
||||||
"Folders": "檔案夾",
|
"Folders": "資料夾",
|
||||||
"Genres": "風格",
|
"Genres": "風格",
|
||||||
"HeaderAlbumArtists": "專輯藝人",
|
"HeaderAlbumArtists": "專輯藝人",
|
||||||
"HeaderContinueWatching": "繼續觀看",
|
"HeaderContinueWatching": "繼續觀看",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"MixedContent": "混合內容",
|
"MixedContent": "混合內容",
|
||||||
"Movies": "電影",
|
"Movies": "電影",
|
||||||
"Music": "音樂",
|
"Music": "音樂",
|
||||||
"MusicVideos": "音樂視頻",
|
"MusicVideos": "音樂影片",
|
||||||
"NameInstallFailed": "{0} 安裝失敗",
|
"NameInstallFailed": "{0} 安裝失敗",
|
||||||
"NameSeasonNumber": "第 {0} 季",
|
"NameSeasonNumber": "第 {0} 季",
|
||||||
"NameSeasonUnknown": "未知季數",
|
"NameSeasonUnknown": "未知季數",
|
||||||
@ -117,5 +117,8 @@
|
|||||||
"TaskCleanActivityLog": "清理活動記錄",
|
"TaskCleanActivityLog": "清理活動記錄",
|
||||||
"Undefined": "未定義",
|
"Undefined": "未定義",
|
||||||
"Forced": "強制",
|
"Forced": "強制",
|
||||||
"Default": "預設"
|
"Default": "預設",
|
||||||
|
"TaskOptimizeDatabaseDescription": "壓縮數據庫並截斷可用空間。在掃描媒體庫或執行其他數據庫的修改後運行此任務可能會提高性能。",
|
||||||
|
"TaskOptimizeDatabase": "最佳化數據庫",
|
||||||
|
"TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。"
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
"HeaderFavoriteSongs": "最愛歌曲",
|
"HeaderFavoriteSongs": "最愛歌曲",
|
||||||
"HeaderLiveTV": "電視直播",
|
"HeaderLiveTV": "電視直播",
|
||||||
"HeaderNextUp": "接下來",
|
"HeaderNextUp": "接下來",
|
||||||
"HomeVideos": "自製影片",
|
"HomeVideos": "家庭影片",
|
||||||
"ItemAddedWithName": "{0} 已新增至媒體庫",
|
"ItemAddedWithName": "{0} 已新增至媒體庫",
|
||||||
"ItemRemovedWithName": "{0} 已從媒體庫移除",
|
"ItemRemovedWithName": "{0} 已從媒體庫移除",
|
||||||
"LabelIpAddressValue": "IP 位址:{0}",
|
"LabelIpAddressValue": "IP 位址:{0}",
|
||||||
@ -117,5 +117,7 @@
|
|||||||
"TaskCleanActivityLog": "清除活動紀錄",
|
"TaskCleanActivityLog": "清除活動紀錄",
|
||||||
"Undefined": "未定義的",
|
"Undefined": "未定義的",
|
||||||
"Forced": "強制",
|
"Forced": "強制",
|
||||||
"Default": "原本"
|
"Default": "原本",
|
||||||
|
"TaskOptimizeDatabaseDescription": "縮小資料庫並釋放可用空間。在掃描資料庫或進行資料庫相關的更動後使用此功能會增加效能。",
|
||||||
|
"TaskOptimizeDatabase": "最佳化資料庫"
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -38,10 +36,10 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries =
|
private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries =
|
||||||
new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
|
new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
private List<CultureDto> _cultures;
|
|
||||||
|
|
||||||
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
|
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
|
||||||
|
|
||||||
|
private List<CultureDto> _cultures = new List<CultureDto>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="LocalizationManager" /> class.
|
/// Initializes a new instance of the <see cref="LocalizationManager" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -72,8 +70,8 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
string countryCode = resource.Substring(RatingsPath.Length, 2);
|
string countryCode = resource.Substring(RatingsPath.Length, 2);
|
||||||
var dict = new Dictionary<string, ParentalRating>(StringComparer.OrdinalIgnoreCase);
|
var dict = new Dictionary<string, ParentalRating>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
await using var str = _assembly.GetManifestResourceStream(resource);
|
await using var stream = _assembly.GetManifestResourceStream(resource);
|
||||||
using var reader = new StreamReader(str);
|
using var reader = new StreamReader(stream!); // shouldn't be null here, we just got the resource path from Assembly.GetManifestResourceNames()
|
||||||
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
|
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(line))
|
if (string.IsNullOrWhiteSpace(line))
|
||||||
@ -113,7 +111,8 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
{
|
{
|
||||||
List<CultureDto> list = new List<CultureDto>();
|
List<CultureDto> list = new List<CultureDto>();
|
||||||
|
|
||||||
await using var stream = _assembly.GetManifestResourceStream(CulturesPath);
|
await using var stream = _assembly.GetManifestResourceStream(CulturesPath)
|
||||||
|
?? throw new InvalidOperationException($"Invalid resource path: '{CulturesPath}'");
|
||||||
using var reader = new StreamReader(stream);
|
using var reader = new StreamReader(stream);
|
||||||
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
|
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
@ -162,7 +161,7 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public CultureDto FindLanguageInfo(string language)
|
public CultureDto? FindLanguageInfo(string language)
|
||||||
{
|
{
|
||||||
// TODO language should ideally be a ReadOnlySpan but moq cannot mock ref structs
|
// TODO language should ideally be a ReadOnlySpan but moq cannot mock ref structs
|
||||||
for (var i = 0; i < _cultures.Count; i++)
|
for (var i = 0; i < _cultures.Count; i++)
|
||||||
@ -183,9 +182,10 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerable<CountryInfo> GetCountries()
|
public IEnumerable<CountryInfo> GetCountries()
|
||||||
{
|
{
|
||||||
using StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream(CountriesPath));
|
using StreamReader reader = new StreamReader(
|
||||||
|
_assembly.GetManifestResourceStream(CountriesPath) ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'"));
|
||||||
return JsonSerializer.Deserialize<IEnumerable<CountryInfo>>(reader.ReadToEnd(), _jsonOptions);
|
return JsonSerializer.Deserialize<IEnumerable<CountryInfo>>(reader.ReadToEnd(), _jsonOptions)
|
||||||
|
?? throw new InvalidOperationException($"Resource contains invalid data: '{CountriesPath}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -205,7 +205,9 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
countryCode = "us";
|
countryCode = "us";
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetRatings(countryCode) ?? GetRatings("us");
|
return GetRatings(countryCode)
|
||||||
|
?? GetRatings("us")
|
||||||
|
?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -213,7 +215,7 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="countryCode">The country code.</param>
|
/// <param name="countryCode">The country code.</param>
|
||||||
/// <returns>The ratings.</returns>
|
/// <returns>The ratings.</returns>
|
||||||
private Dictionary<string, ParentalRating> GetRatings(string countryCode)
|
private Dictionary<string, ParentalRating>? GetRatings(string countryCode)
|
||||||
{
|
{
|
||||||
_allParentalRatings.TryGetValue(countryCode, out var value);
|
_allParentalRatings.TryGetValue(countryCode, out var value);
|
||||||
|
|
||||||
@ -238,7 +240,7 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
|
|
||||||
var ratingsDictionary = GetParentalRatingsDictionary();
|
var ratingsDictionary = GetParentalRatingsDictionary();
|
||||||
|
|
||||||
if (ratingsDictionary.TryGetValue(rating, out ParentalRating value))
|
if (ratingsDictionary.TryGetValue(rating, out ParentalRating? value))
|
||||||
{
|
{
|
||||||
return value.Value;
|
return value.Value;
|
||||||
}
|
}
|
||||||
@ -268,20 +270,6 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool HasUnicodeCategory(string value, UnicodeCategory category)
|
|
||||||
{
|
|
||||||
foreach (var chr in value)
|
|
||||||
{
|
|
||||||
if (char.GetUnicodeCategory(chr) == category)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string GetLocalizedString(string phrase)
|
public string GetLocalizedString(string phrase)
|
||||||
{
|
{
|
||||||
@ -347,18 +335,21 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
{
|
{
|
||||||
await using var stream = _assembly.GetManifestResourceStream(resourcePath);
|
await using var stream = _assembly.GetManifestResourceStream(resourcePath);
|
||||||
// If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain
|
// If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain
|
||||||
if (stream != null)
|
if (stream == null)
|
||||||
{
|
|
||||||
var dict = await JsonSerializer.DeserializeAsync<Dictionary<string, string>>(stream, _jsonOptions).ConfigureAwait(false);
|
|
||||||
|
|
||||||
foreach (var key in dict.Keys)
|
|
||||||
{
|
|
||||||
dictionary[key] = dict[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
_logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath);
|
_logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dict = await JsonSerializer.DeserializeAsync<Dictionary<string, string>>(stream, _jsonOptions).ConfigureAwait(false);
|
||||||
|
if (dict == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Resource contains invalid data: '{stream}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var key in dict.Keys)
|
||||||
|
{
|
||||||
|
dictionary[key] = dict[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,5 +49,10 @@ namespace Emby.Server.Implementations.Playlists
|
|||||||
query.Parent = null;
|
query.Parent = null;
|
||||||
return LibraryManager.GetItemsResult(query);
|
return LibraryManager.GetItemsResult(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string GetClientTypeName()
|
||||||
|
{
|
||||||
|
return "ManualPlaylistsFolder";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user