mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-03-26 12:07:01 +00:00
add playback of in-progress recordings
This commit is contained in:
@@ -1577,97 +1577,5 @@ namespace MediaBrowser.Server.Implementations.Channels
|
||||
|
||||
return await _libraryManager.GetNamedView(name, "channels", "zz_" + name, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task DownloadChannelItem(BaseItem item, string destination,
|
||||
IProgress<double> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
var sources = await GetDynamicMediaSources(item, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var list = sources.Where(i => i.Protocol == MediaProtocol.Http).ToList();
|
||||
|
||||
foreach (var source in list)
|
||||
{
|
||||
await TryDownloadChannelItem(source, item, destination, progress, cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TryDownloadChannelItem(MediaSourceInfo source,
|
||||
BaseItem item,
|
||||
string destination,
|
||||
IProgress<double> progress,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var options = new HttpRequestOptions
|
||||
{
|
||||
CancellationToken = cancellationToken,
|
||||
Url = source.Path,
|
||||
Progress = new Progress<double>()
|
||||
};
|
||||
|
||||
var channel = GetChannel(item.ChannelId);
|
||||
var channelProvider = GetChannelProvider(channel);
|
||||
var features = channelProvider.GetChannelFeatures();
|
||||
|
||||
if (!features.SupportsContentDownloading)
|
||||
{
|
||||
throw new ArgumentException("The channel does not support downloading.");
|
||||
}
|
||||
|
||||
var limit = features.DailyDownloadLimit;
|
||||
|
||||
foreach (var header in source.RequiredHttpHeaders)
|
||||
{
|
||||
options.RequestHeaders[header.Key] = header.Value;
|
||||
}
|
||||
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(destination));
|
||||
|
||||
// Determine output extension
|
||||
var response = await _httpClient.GetTempFileResponse(options).ConfigureAwait(false);
|
||||
|
||||
if (response.ContentType.StartsWith("text/html"))
|
||||
{
|
||||
throw new HttpException("File not found")
|
||||
{
|
||||
StatusCode = HttpStatusCode.NotFound
|
||||
};
|
||||
}
|
||||
|
||||
if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase) && response.ContentType.StartsWith("video/", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var extension = response.ContentType.Split('/')
|
||||
.Last()
|
||||
.Replace("quicktime", "mov", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
destination += "." + extension;
|
||||
}
|
||||
else if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) && response.ContentType.StartsWith("audio/", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var extension = response.ContentType.Replace("audio/mpeg", "audio/mp3", StringComparison.OrdinalIgnoreCase)
|
||||
.Split('/')
|
||||
.Last();
|
||||
|
||||
destination += "." + extension;
|
||||
}
|
||||
else
|
||||
{
|
||||
_fileSystem.DeleteFile(response.TempFilePath);
|
||||
|
||||
throw new ApplicationException("Unexpected response type encountered: " + response.ContentType);
|
||||
}
|
||||
|
||||
_fileSystem.CopyFile(response.TempFilePath, destination, true);
|
||||
|
||||
try
|
||||
{
|
||||
_fileSystem.DeleteFile(response.TempFilePath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -907,15 +907,6 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||
dto.Keywords = item.Keywords;
|
||||
}
|
||||
|
||||
if (fields.Contains(ItemFields.PlaceOfBirth))
|
||||
{
|
||||
var person = item as Person;
|
||||
if (person != null)
|
||||
{
|
||||
dto.PlaceOfBirth = person.PlaceOfBirth;
|
||||
}
|
||||
}
|
||||
|
||||
var hasAspectRatio = item as IHasAspectRatio;
|
||||
if (hasAspectRatio != null)
|
||||
{
|
||||
@@ -1432,10 +1423,9 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||
SetBookProperties(dto, book);
|
||||
}
|
||||
|
||||
var movie = item as Movie;
|
||||
if (movie != null)
|
||||
if (item.ProductionLocations.Count > 0 || item is Movie)
|
||||
{
|
||||
dto.ProductionLocations = new string[] { };
|
||||
dto.ProductionLocations = item.ProductionLocations.ToArray();
|
||||
}
|
||||
|
||||
var photo = item as Photo;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
|
||||
public Container Container { get; set; }
|
||||
private readonly HttpListenerRequest request;
|
||||
private readonly IHttpResponse response;
|
||||
private IMemoryStreamProvider _memoryStreamProvider;
|
||||
private readonly IMemoryStreamProvider _memoryStreamProvider;
|
||||
|
||||
public WebSocketSharpRequest(HttpListenerContext httpContext, string operationName, RequestAttributes requestAttributes, ILogger logger, IMemoryStreamProvider memoryStreamProvider)
|
||||
{
|
||||
|
||||
@@ -335,15 +335,6 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the item in library cache.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
private void UpdateItemInLibraryCache(BaseItem item)
|
||||
{
|
||||
RegisterItem(item);
|
||||
}
|
||||
|
||||
public void RegisterItem(BaseItem item)
|
||||
{
|
||||
if (item == null)
|
||||
@@ -1777,7 +1768,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
foreach (var item in list)
|
||||
{
|
||||
UpdateItemInLibraryCache(item);
|
||||
RegisterItem(item);
|
||||
}
|
||||
|
||||
if (ItemAdded != null)
|
||||
@@ -1818,7 +1809,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
|
||||
await ItemRepository.SaveItem(item, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
UpdateItemInLibraryCache(item);
|
||||
RegisterItem(item);
|
||||
|
||||
if (ItemUpdated != null)
|
||||
{
|
||||
|
||||
@@ -54,8 +54,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
||||
if (!args.IsDirectory) return null;
|
||||
|
||||
// Avoid mis-identifying top folders
|
||||
if (args.Parent.IsRoot) return null;
|
||||
if (args.HasParent<MusicAlbum>()) return null;
|
||||
if (args.Parent.IsRoot) return null;
|
||||
|
||||
var collectionType = args.GetCollectionType();
|
||||
|
||||
|
||||
@@ -51,9 +51,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
||||
{
|
||||
if (!args.IsDirectory) return null;
|
||||
|
||||
// Avoid mis-identifying top folders
|
||||
if (args.Parent.IsRoot) return null;
|
||||
|
||||
// Don't allow nested artists
|
||||
if (args.HasParent<MusicArtist>() || args.HasParent<MusicAlbum>())
|
||||
{
|
||||
@@ -70,12 +67,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
||||
return null;
|
||||
}
|
||||
|
||||
if (args.IsDirectory)
|
||||
if (args.ContainsFileSystemEntryByName("artist.nfo"))
|
||||
{
|
||||
if (args.ContainsFileSystemEntryByName("artist.nfo"))
|
||||
{
|
||||
return new MusicArtist();
|
||||
}
|
||||
return new MusicArtist();
|
||||
}
|
||||
|
||||
if (_config.Configuration.EnableSimpleArtistDetection)
|
||||
@@ -83,6 +77,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
||||
return null;
|
||||
}
|
||||
|
||||
// Avoid mis-identifying top folders
|
||||
if (args.Parent.IsRoot) return null;
|
||||
|
||||
var directoryService = args.DirectoryService;
|
||||
|
||||
var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager);
|
||||
|
||||
@@ -51,7 +51,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
|
||||
base.SetInitialItemValues(item, args);
|
||||
|
||||
item.IsRoot = args.Parent == null;
|
||||
item.IsPhysicalRoot = args.IsPhysicalRoot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,15 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
|
||||
return null;
|
||||
}
|
||||
|
||||
if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
|
||||
{
|
||||
return new Series
|
||||
{
|
||||
Path = args.Path,
|
||||
Name = Path.GetFileName(args.Path)
|
||||
};
|
||||
}
|
||||
|
||||
var collectionType = args.GetCollectionType();
|
||||
if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@@ -72,23 +81,20 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (string.IsNullOrWhiteSpace(collectionType))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(collectionType))
|
||||
if (args.Parent.IsRoot)
|
||||
{
|
||||
if (args.Parent.IsRoot)
|
||||
return null;
|
||||
}
|
||||
|
||||
if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false))
|
||||
{
|
||||
return new Series
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false) ||
|
||||
args.ContainsFileSystemEntryByName("tvshow.nfo"))
|
||||
{
|
||||
return new Series
|
||||
{
|
||||
Path = args.Path,
|
||||
Name = Path.GetFileName(args.Path)
|
||||
};
|
||||
}
|
||||
Path = args.Path,
|
||||
Name = Path.GetFileName(args.Path)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
@@ -38,7 +39,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
{
|
||||
public class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable
|
||||
{
|
||||
private readonly IApplicationHost _appHpst;
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
@@ -64,11 +65,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings =
|
||||
new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder)
|
||||
public EmbyTV(IServerApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder)
|
||||
{
|
||||
Current = this;
|
||||
|
||||
_appHpst = appHost;
|
||||
_appHost = appHost;
|
||||
_logger = logger;
|
||||
_httpClient = httpClient;
|
||||
_config = config;
|
||||
@@ -293,7 +294,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
status.Tuners = list;
|
||||
status.Status = LiveTvServiceStatus.Ok;
|
||||
status.Version = _appHpst.ApplicationVersion.ToString();
|
||||
status.Version = _appHost.ApplicationVersion.ToString();
|
||||
status.IsVisible = false;
|
||||
return status;
|
||||
}
|
||||
@@ -659,7 +660,63 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
public async Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return new List<RecordingInfo>();
|
||||
return _activeRecordings.Values.ToList().Select(GetRecordingInfo).ToList();
|
||||
}
|
||||
|
||||
public string GetActiveRecordingPath(string id)
|
||||
{
|
||||
ActiveRecordingInfo info;
|
||||
|
||||
if (_activeRecordings.TryGetValue(id, out info))
|
||||
{
|
||||
return info.Path;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private RecordingInfo GetRecordingInfo(ActiveRecordingInfo info)
|
||||
{
|
||||
var timer = info.Timer;
|
||||
var program = info.Program;
|
||||
|
||||
var result = new RecordingInfo
|
||||
{
|
||||
ChannelId = timer.ChannelId,
|
||||
CommunityRating = timer.CommunityRating,
|
||||
DateLastUpdated = DateTime.UtcNow,
|
||||
EndDate = timer.EndDate,
|
||||
EpisodeTitle = timer.EpisodeTitle,
|
||||
Genres = timer.Genres,
|
||||
Id = "recording" + timer.Id,
|
||||
IsKids = timer.IsKids,
|
||||
IsMovie = timer.IsMovie,
|
||||
IsNews = timer.IsNews,
|
||||
IsRepeat = timer.IsRepeat,
|
||||
IsSeries = timer.IsProgramSeries,
|
||||
IsSports = timer.IsSports,
|
||||
Name = timer.Name,
|
||||
OfficialRating = timer.OfficialRating,
|
||||
OriginalAirDate = timer.OriginalAirDate,
|
||||
Overview = timer.Overview,
|
||||
ProgramId = timer.ProgramId,
|
||||
SeriesTimerId = timer.SeriesTimerId,
|
||||
StartDate = timer.StartDate,
|
||||
Status = RecordingStatus.InProgress,
|
||||
TimerId = timer.Id
|
||||
};
|
||||
|
||||
if (program != null)
|
||||
{
|
||||
result.Audio = program.Audio;
|
||||
result.ImagePath = program.ImagePath;
|
||||
result.ImageUrl = program.ImageUrl;
|
||||
result.IsHD = program.IsHD;
|
||||
result.IsLive = program.IsLive;
|
||||
result.IsPremiere = program.IsPremiere;
|
||||
result.ShowId = program.ShowId;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task<IEnumerable<TimerInfo>> GetTimersAsync(CancellationToken cancellationToken)
|
||||
@@ -954,7 +1011,31 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
public Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
ActiveRecordingInfo info;
|
||||
|
||||
recordingId = recordingId.Replace("recording", string.Empty);
|
||||
|
||||
if (_activeRecordings.TryGetValue(recordingId, out info))
|
||||
{
|
||||
return Task.FromResult(new List<MediaSourceInfo>
|
||||
{
|
||||
new MediaSourceInfo
|
||||
{
|
||||
Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveRecordings/" + recordingId + "/stream",
|
||||
Id = recordingId,
|
||||
SupportsDirectPlay = false,
|
||||
SupportsDirectStream = true,
|
||||
SupportsTranscoding = true,
|
||||
IsInfiniteStream = true,
|
||||
RequiresOpening = false,
|
||||
RequiresClosing = false,
|
||||
Protocol = Model.MediaInfo.MediaProtocol.Http,
|
||||
BufferMs = 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
|
||||
@@ -1031,7 +1112,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
var activeRecordingInfo = new ActiveRecordingInfo
|
||||
{
|
||||
CancellationTokenSource = new CancellationTokenSource(),
|
||||
TimerId = timer.Id
|
||||
Timer = timer
|
||||
};
|
||||
|
||||
if (_activeRecordings.TryAdd(timer.Id, activeRecordingInfo))
|
||||
@@ -1168,6 +1249,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
if (programInfo != null)
|
||||
{
|
||||
RecordingHelper.CopyProgramInfoToTimerInfo(programInfo, timer);
|
||||
activeRecordingInfo.Program = programInfo;
|
||||
}
|
||||
|
||||
string seriesPath = null;
|
||||
@@ -1394,7 +1476,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
return true;
|
||||
}
|
||||
|
||||
var hasRecordingAtPath = _activeRecordings.Values.ToList().Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.TimerId, timerId, StringComparison.OrdinalIgnoreCase));
|
||||
var hasRecordingAtPath = _activeRecordings
|
||||
.Values
|
||||
.ToList()
|
||||
.Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (hasRecordingAtPath)
|
||||
{
|
||||
@@ -1878,7 +1963,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
class ActiveRecordingInfo
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public string TimerId { get; set; }
|
||||
public TimerInfo Timer { get; set; }
|
||||
public ProgramInfo Program { get; set; }
|
||||
public CancellationTokenSource CancellationTokenSource { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using MediaBrowser.Model.LiveTv;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -125,6 +126,34 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
|
||||
dto.DayPattern = info.Days == null ? null : GetDayPattern(info.Days);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(info.SeriesId))
|
||||
{
|
||||
var program = _libraryManager.GetItemList(new InternalItemsQuery
|
||||
{
|
||||
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
|
||||
ExternalSeriesId = info.SeriesId,
|
||||
Limit = 1,
|
||||
ImageTypes = new ImageType[] { ImageType.Primary }
|
||||
|
||||
}).FirstOrDefault();
|
||||
|
||||
if (program != null)
|
||||
{
|
||||
var image = program.GetImageInfo(ImageType.Primary, 0);
|
||||
if (image != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image);
|
||||
dto.ParentPrimaryImageItemId = program.Id.ToString("N");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
|
||||
@@ -235,20 +235,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
return await GetLiveStream(id, mediaSourceId, true, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(string id, CancellationToken cancellationToken)
|
||||
public async Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
|
||||
{
|
||||
var item = await GetInternalRecording(id, cancellationToken).ConfigureAwait(false);
|
||||
var service = GetService(item);
|
||||
var baseItem = (BaseItem)item;
|
||||
var service = GetService(baseItem);
|
||||
|
||||
return await service.GetRecordingStreamMediaSources(id, cancellationToken).ConfigureAwait(false);
|
||||
return await service.GetRecordingStreamMediaSources(baseItem.ExternalId, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(string id, CancellationToken cancellationToken)
|
||||
public async Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
|
||||
{
|
||||
var item = GetInternalChannel(id);
|
||||
var service = GetService(item);
|
||||
var baseItem = (LiveTvChannel)item;
|
||||
var service = GetService(baseItem);
|
||||
|
||||
var sources = await service.GetChannelStreamMediaSources(item.ExternalId, cancellationToken).ConfigureAwait(false);
|
||||
var sources = await service.GetChannelStreamMediaSources(baseItem.ExternalId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (sources.Count == 0)
|
||||
{
|
||||
@@ -259,7 +259,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
|
||||
foreach (var source in list)
|
||||
{
|
||||
Normalize(source, service, item.ChannelType == ChannelType.TV);
|
||||
Normalize(source, service, baseItem.ChannelType == ChannelType.TV);
|
||||
}
|
||||
|
||||
return list;
|
||||
@@ -738,6 +738,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
recording.IsRepeat = info.IsRepeat;
|
||||
recording.IsSports = info.IsSports;
|
||||
recording.SeriesTimerId = info.SeriesTimerId;
|
||||
recording.TimerId = info.TimerId;
|
||||
recording.StartDate = info.StartDate;
|
||||
|
||||
if (!dataChanged)
|
||||
@@ -1083,10 +1084,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
|
||||
if (timer != null)
|
||||
{
|
||||
program.TimerId = _tvDtoService.GetInternalTimerId(serviceName, timer.Id)
|
||||
.ToString("N");
|
||||
if (timer.Status != RecordingStatus.Cancelled && timer.Status != RecordingStatus.Error)
|
||||
{
|
||||
program.TimerId = _tvDtoService.GetInternalTimerId(serviceName, timer.Id)
|
||||
.ToString("N");
|
||||
|
||||
program.TimerStatus = timer.Status;
|
||||
program.Status = timer.Status.ToString();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(timer.SeriesTimerId))
|
||||
{
|
||||
@@ -1432,7 +1436,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
private DateTime _lastRecordingRefreshTime;
|
||||
private async Task RefreshRecordings(CancellationToken cancellationToken)
|
||||
{
|
||||
const int cacheMinutes = 5;
|
||||
const int cacheMinutes = 3;
|
||||
|
||||
if ((DateTime.UtcNow - _lastRecordingRefreshTime).TotalMinutes < cacheMinutes)
|
||||
{
|
||||
@@ -1482,7 +1486,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
|
||||
private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, User user)
|
||||
{
|
||||
if (user == null || (query.IsInProgress ?? false))
|
||||
if (user == null)
|
||||
{
|
||||
return new QueryResult<BaseItem>();
|
||||
}
|
||||
|
||||
if ((query.IsInProgress ?? false))
|
||||
{
|
||||
return new QueryResult<BaseItem>();
|
||||
}
|
||||
@@ -1628,7 +1637,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
return new QueryResult<BaseItem>();
|
||||
}
|
||||
|
||||
if (_services.Count == 1)
|
||||
if (_services.Count == 1 && !(query.IsInProgress ?? false))
|
||||
{
|
||||
return GetEmbyRecordings(query, new DtoOptions(), user);
|
||||
}
|
||||
@@ -1824,6 +1833,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
? null
|
||||
: _tvDtoService.GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N");
|
||||
|
||||
dto.TimerId = string.IsNullOrEmpty(info.TimerId)
|
||||
? null
|
||||
: _tvDtoService.GetInternalTimerId(service.Name, info.TimerId).ToString("N");
|
||||
|
||||
dto.StartDate = info.StartDate;
|
||||
dto.RecordingStatus = info.Status;
|
||||
dto.IsRepeat = info.IsRepeat;
|
||||
|
||||
@@ -65,12 +65,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
{
|
||||
if (item is ILiveTvRecording)
|
||||
{
|
||||
sources = await _liveTvManager.GetRecordingMediaSources(item.Id.ToString("N"), cancellationToken)
|
||||
sources = await _liveTvManager.GetRecordingMediaSources(item, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
sources = await _liveTvManager.GetChannelMediaSources(item.Id.ToString("N"), cancellationToken)
|
||||
sources = await _liveTvManager.GetChannelMediaSources(item, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,13 @@ using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Collections;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Playlists;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
using MediaBrowser.Server.Implementations.Devices;
|
||||
using MediaBrowser.Server.Implementations.Playlists;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Persistence
|
||||
{
|
||||
@@ -279,6 +282,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
_connection.AddColumn(Logger, "TypedBaseItems", "Keywords", "Text");
|
||||
_connection.AddColumn(Logger, "TypedBaseItems", "ProviderIds", "Text");
|
||||
_connection.AddColumn(Logger, "TypedBaseItems", "Images", "Text");
|
||||
_connection.AddColumn(Logger, "TypedBaseItems", "ProductionLocations", "Text");
|
||||
_connection.AddColumn(Logger, "TypedBaseItems", "ThemeSongIds", "Text");
|
||||
_connection.AddColumn(Logger, "TypedBaseItems", "ThemeVideoIds", "Text");
|
||||
|
||||
_connection.AddColumn(Logger, "UserDataKeys", "Priority", "INT");
|
||||
_connection.AddColumn(Logger, "ItemValues", "CleanValue", "Text");
|
||||
@@ -428,7 +434,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
"Tagline",
|
||||
"Keywords",
|
||||
"ProviderIds",
|
||||
"Images"
|
||||
"Images",
|
||||
"ProductionLocations",
|
||||
"ThemeSongIds",
|
||||
"ThemeVideoIds"
|
||||
};
|
||||
|
||||
private readonly string[] _mediaStreamSaveColumns =
|
||||
@@ -556,7 +565,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
"Tagline",
|
||||
"Keywords",
|
||||
"ProviderIds",
|
||||
"Images"
|
||||
"Images",
|
||||
"ProductionLocations",
|
||||
"ThemeSongIds",
|
||||
"ThemeVideoIds"
|
||||
};
|
||||
_saveItemCommand = _connection.CreateCommand();
|
||||
_saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values (";
|
||||
@@ -1007,10 +1019,46 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
_saveItemCommand.GetParameter(index++).Value = item.ExternalSeriesId;
|
||||
_saveItemCommand.GetParameter(index++).Value = item.ShortOverview;
|
||||
_saveItemCommand.GetParameter(index++).Value = item.Tagline;
|
||||
_saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Keywords.ToArray());
|
||||
|
||||
if (item.Keywords.Count > 0)
|
||||
{
|
||||
_saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Keywords.ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
_saveItemCommand.GetParameter(index++).Value = null;
|
||||
}
|
||||
|
||||
_saveItemCommand.GetParameter(index++).Value = SerializeProviderIds(item);
|
||||
_saveItemCommand.GetParameter(index++).Value = SerializeImages(item);
|
||||
|
||||
if (item.ProductionLocations.Count > 0)
|
||||
{
|
||||
_saveItemCommand.GetParameter(index++).Value = string.Join("|", item.ProductionLocations.ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
_saveItemCommand.GetParameter(index++).Value = null;
|
||||
}
|
||||
|
||||
if (item.ThemeSongIds.Count > 0)
|
||||
{
|
||||
_saveItemCommand.GetParameter(index++).Value = string.Join("|", item.ThemeSongIds.Select(i => i.ToString("N")).ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
_saveItemCommand.GetParameter(index++).Value = null;
|
||||
}
|
||||
|
||||
if (item.ThemeVideoIds.Count > 0)
|
||||
{
|
||||
_saveItemCommand.GetParameter(index++).Value = string.Join("|", item.ThemeVideoIds.Select(i => i.ToString("N")).ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
_saveItemCommand.GetParameter(index++).Value = null;
|
||||
}
|
||||
|
||||
_saveItemCommand.Transaction = transaction;
|
||||
|
||||
_saveItemCommand.ExecuteNonQuery();
|
||||
@@ -1217,6 +1265,46 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (type == typeof(Person))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (type == typeof(RecordingGroup))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (type == typeof(Channel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (type == typeof(ManualCollectionsFolder))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (type == typeof(CameraUploadsFolder))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (type == typeof(PlaylistsFolder))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (type == typeof(UserRootFolder))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (type == typeof(PhotoAlbum))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (type == typeof(Season))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (type == typeof(MusicArtist))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (_config.Configuration.SkipDeserializationForPrograms)
|
||||
{
|
||||
@@ -1775,6 +1863,27 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
}
|
||||
index++;
|
||||
|
||||
if (query.HasField(ItemFields.ProductionLocations))
|
||||
{
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
item.ProductionLocations = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
item.ThemeSongIds = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToList();
|
||||
}
|
||||
index++;
|
||||
|
||||
if (!reader.IsDBNull(index))
|
||||
{
|
||||
item.ThemeVideoIds = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToList();
|
||||
}
|
||||
index++;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(item.Tagline))
|
||||
{
|
||||
var movie = item as Movie;
|
||||
@@ -1784,6 +1893,15 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||
}
|
||||
}
|
||||
|
||||
if (type == typeof(Person) && item.ProductionLocations.Count == 0)
|
||||
{
|
||||
var person = (Person)item;
|
||||
if (!string.IsNullOrWhiteSpace(person.PlaceOfBirth))
|
||||
{
|
||||
item.ProductionLocations = new List<string> { person.PlaceOfBirth };
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user