mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-05-31 04:05:50 -04:00
support track selection before playback
This commit is contained in:
parent
b9c1f61681
commit
5cb7469028
@ -554,7 +554,6 @@ namespace Emby.Dlna
|
|||||||
var list = new List<DeviceProfile>
|
var list = new List<DeviceProfile>
|
||||||
{
|
{
|
||||||
new SamsungSmartTvProfile(),
|
new SamsungSmartTvProfile(),
|
||||||
new Xbox360Profile(),
|
|
||||||
new XboxOneProfile(),
|
new XboxOneProfile(),
|
||||||
new SonyPs3Profile(),
|
new SonyPs3Profile(),
|
||||||
new SonyPs4Profile(),
|
new SonyPs4Profile(),
|
||||||
|
@ -105,7 +105,6 @@
|
|||||||
<Compile Include="Profiles\SonyPs3Profile.cs" />
|
<Compile Include="Profiles\SonyPs3Profile.cs" />
|
||||||
<Compile Include="Profiles\SonyPs4Profile.cs" />
|
<Compile Include="Profiles\SonyPs4Profile.cs" />
|
||||||
<Compile Include="Profiles\WdtvLiveProfile.cs" />
|
<Compile Include="Profiles\WdtvLiveProfile.cs" />
|
||||||
<Compile Include="Profiles\Xbox360Profile.cs" />
|
|
||||||
<Compile Include="Profiles\XboxOneProfile.cs" />
|
<Compile Include="Profiles\XboxOneProfile.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Server\DescriptionXmlBuilder.cs" />
|
<Compile Include="Server\DescriptionXmlBuilder.cs" />
|
||||||
@ -175,7 +174,6 @@
|
|||||||
<EmbeddedResource Include="Profiles\Xml\Sony PlayStation 3.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Sony PlayStation 3.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\Sony PlayStation 4.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Sony PlayStation 4.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\WDTV Live.xml" />
|
<EmbeddedResource Include="Profiles\Xml\WDTV Live.xml" />
|
||||||
<EmbeddedResource Include="Profiles\Xml\Xbox 360.xml" />
|
|
||||||
<EmbeddedResource Include="Profiles\Xml\Xbox One.xml" />
|
<EmbeddedResource Include="Profiles\Xml\Xbox One.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -65,13 +65,15 @@ namespace Emby.Dlna.Profiles
|
|||||||
{
|
{
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Container = "m4v,mpegts,ts,3gp,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,m2v,avi,mpg,mpeg,mp4,webm,wtv,m2ts,dvr-ms",
|
// play all
|
||||||
|
Container = "",
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
},
|
},
|
||||||
|
|
||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Container = "aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac,m4a",
|
// play all
|
||||||
|
Container = "",
|
||||||
Type = DlnaProfileType.Audio
|
Type = DlnaProfileType.Audio
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,326 +0,0 @@
|
|||||||
using MediaBrowser.Model.Dlna;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
|
|
||||||
namespace Emby.Dlna.Profiles
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Good info on xbox 360 requirements: https://code.google.com/p/jems/wiki/XBox360Notes
|
|
||||||
/// </summary>
|
|
||||||
[XmlRoot("Profile")]
|
|
||||||
public class Xbox360Profile : DefaultProfile
|
|
||||||
{
|
|
||||||
public Xbox360Profile()
|
|
||||||
{
|
|
||||||
Name = "Xbox 360";
|
|
||||||
|
|
||||||
// Required according to above
|
|
||||||
ModelName = "Windows Media Player Sharing";
|
|
||||||
|
|
||||||
ModelNumber = "12.0";
|
|
||||||
|
|
||||||
FriendlyName = "${HostName}: 1";
|
|
||||||
|
|
||||||
ModelUrl = "http://go.microsoft.com/fwlink/?LinkId=105926";
|
|
||||||
Manufacturer = "Microsoft Corporation";
|
|
||||||
ManufacturerUrl = "http://www.microsoft.com";
|
|
||||||
XDlnaDoc = "DMS-1.50";
|
|
||||||
ModelDescription = "Emby : UPnP Media Server";
|
|
||||||
|
|
||||||
TimelineOffsetSeconds = 40;
|
|
||||||
RequiresPlainFolders = true;
|
|
||||||
RequiresPlainVideoItems = true;
|
|
||||||
EnableMSMediaReceiverRegistrar = true;
|
|
||||||
|
|
||||||
Identification = new DeviceIdentification
|
|
||||||
{
|
|
||||||
ModelName = "Xbox 360",
|
|
||||||
|
|
||||||
Headers = new[]
|
|
||||||
{
|
|
||||||
new HttpHeaderInfo {Name = "User-Agent", Value = "Xbox", Match = HeaderMatchType.Substring},
|
|
||||||
new HttpHeaderInfo {Name = "User-Agent", Value = "Xenon", Match = HeaderMatchType.Substring}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TranscodingProfiles = new[]
|
|
||||||
{
|
|
||||||
new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "mp3",
|
|
||||||
AudioCodec = "mp3",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
},
|
|
||||||
new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "asf",
|
|
||||||
VideoCodec = "wmv2",
|
|
||||||
AudioCodec = "wmav2",
|
|
||||||
Type = DlnaProfileType.Video,
|
|
||||||
TranscodeSeekInfo = TranscodeSeekInfo.Bytes,
|
|
||||||
EstimateContentLength = true
|
|
||||||
},
|
|
||||||
new TranscodingProfile
|
|
||||||
{
|
|
||||||
Container = "jpeg",
|
|
||||||
Type = DlnaProfileType.Photo
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DirectPlayProfiles = new[]
|
|
||||||
{
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "avi",
|
|
||||||
VideoCodec = "mpeg4",
|
|
||||||
AudioCodec = "ac3,mp3",
|
|
||||||
Type = DlnaProfileType.Video
|
|
||||||
},
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "avi",
|
|
||||||
VideoCodec = "h264",
|
|
||||||
AudioCodec = "aac",
|
|
||||||
Type = DlnaProfileType.Video
|
|
||||||
},
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "mp4,mov",
|
|
||||||
VideoCodec = "h264,mpeg4",
|
|
||||||
AudioCodec = "aac,ac3",
|
|
||||||
Type = DlnaProfileType.Video
|
|
||||||
},
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "asf",
|
|
||||||
VideoCodec = "wmv2,wmv3,vc1",
|
|
||||||
AudioCodec = "wmav2,wmapro",
|
|
||||||
Type = DlnaProfileType.Video
|
|
||||||
},
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "asf",
|
|
||||||
AudioCodec = "wmav2,wmapro,wmavoice",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
},
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "mp3",
|
|
||||||
AudioCodec = "mp3",
|
|
||||||
Type = DlnaProfileType.Audio
|
|
||||||
},
|
|
||||||
new DirectPlayProfile
|
|
||||||
{
|
|
||||||
Container = "jpeg",
|
|
||||||
Type = DlnaProfileType.Photo
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ResponseProfiles = new[]
|
|
||||||
{
|
|
||||||
new ResponseProfile
|
|
||||||
{
|
|
||||||
Container = "avi",
|
|
||||||
MimeType = "video/avi",
|
|
||||||
Type = DlnaProfileType.Video
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ContainerProfiles = new[]
|
|
||||||
{
|
|
||||||
new ContainerProfile
|
|
||||||
{
|
|
||||||
Type = DlnaProfileType.Video,
|
|
||||||
Container = "mp4,mov",
|
|
||||||
|
|
||||||
Conditions = new []
|
|
||||||
{
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.Equals,
|
|
||||||
Property = ProfileConditionValue.Has64BitOffsets,
|
|
||||||
Value = "false",
|
|
||||||
IsRequired = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new ContainerProfile
|
|
||||||
{
|
|
||||||
Type = DlnaProfileType.Photo,
|
|
||||||
|
|
||||||
Conditions = new []
|
|
||||||
{
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.Width,
|
|
||||||
Value = "1920"
|
|
||||||
},
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.Height,
|
|
||||||
Value = "1080"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
CodecProfiles = new[]
|
|
||||||
{
|
|
||||||
new CodecProfile
|
|
||||||
{
|
|
||||||
Type = CodecType.Video,
|
|
||||||
Codec = "mpeg4",
|
|
||||||
Conditions = new []
|
|
||||||
{
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.Width,
|
|
||||||
Value = "1280"
|
|
||||||
},
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.Height,
|
|
||||||
Value = "720"
|
|
||||||
},
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.VideoFramerate,
|
|
||||||
Value = "30",
|
|
||||||
IsRequired = false
|
|
||||||
},
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.VideoBitrate,
|
|
||||||
Value = "5120000",
|
|
||||||
IsRequired = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new CodecProfile
|
|
||||||
{
|
|
||||||
Type = CodecType.Video,
|
|
||||||
Codec = "h264",
|
|
||||||
Conditions = new []
|
|
||||||
{
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.Width,
|
|
||||||
Value = "1920"
|
|
||||||
},
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.Height,
|
|
||||||
Value = "1080"
|
|
||||||
},
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.VideoLevel,
|
|
||||||
Value = "41",
|
|
||||||
IsRequired = false
|
|
||||||
},
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.VideoBitrate,
|
|
||||||
Value = "10240000",
|
|
||||||
IsRequired = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new CodecProfile
|
|
||||||
{
|
|
||||||
Type = CodecType.Video,
|
|
||||||
Codec = "wmv2,wmv3,vc1",
|
|
||||||
Conditions = new []
|
|
||||||
{
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.Width,
|
|
||||||
Value = "1920"
|
|
||||||
},
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.Height,
|
|
||||||
Value = "1080"
|
|
||||||
},
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.VideoFramerate,
|
|
||||||
Value = "30",
|
|
||||||
IsRequired = false
|
|
||||||
},
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.VideoBitrate,
|
|
||||||
Value = "15360000",
|
|
||||||
IsRequired = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new CodecProfile
|
|
||||||
{
|
|
||||||
Type = CodecType.VideoAudio,
|
|
||||||
Codec = "ac3,wmav2,wmapro",
|
|
||||||
Conditions = new []
|
|
||||||
{
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.AudioChannels,
|
|
||||||
Value = "6",
|
|
||||||
IsRequired = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new CodecProfile
|
|
||||||
{
|
|
||||||
Type = CodecType.VideoAudio,
|
|
||||||
Codec = "aac",
|
|
||||||
Conditions = new []
|
|
||||||
{
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.LessThanEqual,
|
|
||||||
Property = ProfileConditionValue.AudioChannels,
|
|
||||||
Value = "2",
|
|
||||||
IsRequired = false
|
|
||||||
},
|
|
||||||
new ProfileCondition
|
|
||||||
{
|
|
||||||
Condition = ProfileConditionType.Equals,
|
|
||||||
Property = ProfileConditionValue.AudioProfile,
|
|
||||||
Value = "lc",
|
|
||||||
IsRequired = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
SubtitleProfiles = new[]
|
|
||||||
{
|
|
||||||
new SubtitleProfile
|
|
||||||
{
|
|
||||||
Format = "srt",
|
|
||||||
Method = SubtitleDeliveryMethod.Embed
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -60,7 +60,7 @@ namespace Emby.Dlna.Profiles
|
|||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Container = "ts",
|
Container = "ts",
|
||||||
VideoCodec = "h264,mpeg2video",
|
VideoCodec = "h264,mpeg2video,hevc",
|
||||||
AudioCodec = "ac3,aac,mp3",
|
AudioCodec = "ac3,aac,mp3",
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
},
|
},
|
||||||
@ -81,7 +81,7 @@ namespace Emby.Dlna.Profiles
|
|||||||
new DirectPlayProfile
|
new DirectPlayProfile
|
||||||
{
|
{
|
||||||
Container = "mp4,mov,mkv,m4v",
|
Container = "mp4,mov,mkv,m4v",
|
||||||
VideoCodec = "h264,mpeg4,mpeg2video",
|
VideoCodec = "h264,mpeg4,mpeg2video,hevc",
|
||||||
AudioCodec = "aac,ac3",
|
AudioCodec = "aac,ac3",
|
||||||
Type = DlnaProfileType.Video
|
Type = DlnaProfileType.Video
|
||||||
},
|
},
|
||||||
|
@ -29,8 +29,8 @@
|
|||||||
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
|
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
|
||||||
<XmlRootAttributes />
|
<XmlRootAttributes />
|
||||||
<DirectPlayProfiles>
|
<DirectPlayProfiles>
|
||||||
<DirectPlayProfile container="m4v,mpegts,ts,3gp,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,m2v,avi,mpg,mpeg,mp4,webm,wtv,m2ts,dvr-ms" type="Video" />
|
<DirectPlayProfile container="" type="Video" />
|
||||||
<DirectPlayProfile container="aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac,m4a" type="Audio" />
|
<DirectPlayProfile container="" type="Audio" />
|
||||||
</DirectPlayProfiles>
|
</DirectPlayProfiles>
|
||||||
<TranscodingProfiles>
|
<TranscodingProfiles>
|
||||||
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
|
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
|
||||||
|
File diff suppressed because one or more lines are too long
@ -36,10 +36,10 @@
|
|||||||
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
|
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
|
||||||
<XmlRootAttributes />
|
<XmlRootAttributes />
|
||||||
<DirectPlayProfiles>
|
<DirectPlayProfiles>
|
||||||
<DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg2video" type="Video" />
|
<DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg2video,hevc" type="Video" />
|
||||||
<DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" />
|
<DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" />
|
||||||
<DirectPlayProfile container="avi" audioCodec="aac" videoCodec="h264" type="Video" />
|
<DirectPlayProfile container="avi" audioCodec="aac" videoCodec="h264" type="Video" />
|
||||||
<DirectPlayProfile container="mp4,mov,mkv,m4v" audioCodec="aac,ac3" videoCodec="h264,mpeg4,mpeg2video" type="Video" />
|
<DirectPlayProfile container="mp4,mov,mkv,m4v" audioCodec="aac,ac3" videoCodec="h264,mpeg4,mpeg2video,hevc" type="Video" />
|
||||||
<DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="wmv2,wmv3,vc1" type="Video" />
|
<DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="wmv2,wmv3,vc1" type="Video" />
|
||||||
<DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
|
<DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
|
||||||
<DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
|
<DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
|
||||||
|
@ -996,7 +996,7 @@ namespace Emby.Server.Implementations
|
|||||||
NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager);
|
NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager);
|
||||||
RegisterSingleInstance(NotificationManager);
|
RegisterSingleInstance(NotificationManager);
|
||||||
|
|
||||||
SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, MediaSourceManager);
|
SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, MediaSourceManager, ServerConfigurationManager);
|
||||||
RegisterSingleInstance(SubtitleManager);
|
RegisterSingleInstance(SubtitleManager);
|
||||||
|
|
||||||
RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LogManager.GetLogger("IDeviceDiscovery"), ServerConfigurationManager, SocketFactory, TimerFactory));
|
RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LogManager.GetLogger("IDeviceDiscovery"), ServerConfigurationManager, SocketFactory, TimerFactory));
|
||||||
|
@ -296,7 +296,7 @@ namespace Emby.Server.Implementations.Channels
|
|||||||
|
|
||||||
if (requiresCallback != null)
|
if (requiresCallback != null)
|
||||||
{
|
{
|
||||||
results = await GetChannelItemMediaSourcesInternal(requiresCallback, GetItemExternalId(item), cancellationToken)
|
results = await GetChannelItemMediaSourcesInternal(requiresCallback, item.ExternalId, cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -990,18 +990,6 @@ namespace Emby.Server.Implementations.Channels
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetItemExternalId(BaseItem item)
|
|
||||||
{
|
|
||||||
var externalId = item.ExternalId;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(externalId))
|
|
||||||
{
|
|
||||||
externalId = item.GetProviderId("ProviderExternalId");
|
|
||||||
}
|
|
||||||
|
|
||||||
return externalId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
|
private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
|
||||||
private async Task<ChannelItemResult> GetChannelItems(IChannel channel,
|
private async Task<ChannelItemResult> GetChannelItems(IChannel channel,
|
||||||
User user,
|
User user,
|
||||||
@ -1080,7 +1068,7 @@ namespace Emby.Server.Implementations.Channels
|
|||||||
{
|
{
|
||||||
var categoryItem = _libraryManager.GetItemById(new Guid(folderId));
|
var categoryItem = _libraryManager.GetItemById(new Guid(folderId));
|
||||||
|
|
||||||
query.FolderId = GetItemExternalId(categoryItem);
|
query.FolderId = categoryItem.ExternalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false);
|
var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false);
|
||||||
|
@ -373,6 +373,8 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
}
|
}
|
||||||
|
|
||||||
NormalizeMediaSourceContainers(dto);
|
NormalizeMediaSourceContainers(dto);
|
||||||
|
|
||||||
|
dto.SupportsMediaSourceSelection = hasMediaSources.SupportsMediaSourceSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,14 +487,17 @@ namespace Emby.Server.Implementations.IO
|
|||||||
AssertDirectoryExists(dir, path);
|
AssertDirectoryExists(dir, path);
|
||||||
|
|
||||||
var list = ListFiles(dir, recursive);
|
var list = ListFiles(dir, recursive);
|
||||||
|
var result = new List<FileSystemMetadata>();
|
||||||
|
|
||||||
foreach (var file in list)
|
foreach (var file in list)
|
||||||
{
|
{
|
||||||
if (file.IsDirectory())
|
if (file.IsDirectory())
|
||||||
{
|
{
|
||||||
yield return ToMetadata(file);
|
result.Add(ToMetadata(file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<FileSystemMetadata> GetFiles(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
|
public IEnumerable<FileSystemMetadata> GetFiles(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
|
||||||
@ -503,6 +506,7 @@ namespace Emby.Server.Implementations.IO
|
|||||||
AssertDirectoryExists(dir, path);
|
AssertDirectoryExists(dir, path);
|
||||||
|
|
||||||
var list = ListFiles(dir, recursive);
|
var list = ListFiles(dir, recursive);
|
||||||
|
var result = new List<FileSystemMetadata>();
|
||||||
|
|
||||||
foreach (var file in list)
|
foreach (var file in list)
|
||||||
{
|
{
|
||||||
@ -513,10 +517,12 @@ namespace Emby.Server.Implementations.IO
|
|||||||
|
|
||||||
if (extensions == null || extensions.Length == 0 || extensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase))
|
if (extensions == null || extensions.Length == 0 || extensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
yield return ToMetadata(file);
|
result.Add(ToMetadata(file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
|
public IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
|
||||||
@ -525,15 +531,19 @@ namespace Emby.Server.Implementations.IO
|
|||||||
AssertDirectoryExists(dir, path);
|
AssertDirectoryExists(dir, path);
|
||||||
|
|
||||||
var list = ListFiles(dir, recursive);
|
var list = ListFiles(dir, recursive);
|
||||||
|
var result = new List<FileSystemMetadata>();
|
||||||
|
|
||||||
foreach (var file in list)
|
foreach (var file in list)
|
||||||
{
|
{
|
||||||
yield return ToMetadata(file);
|
result.Add(ToMetadata(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false)
|
public List<string> GetFileSystemEntryPaths(string path, bool recursive = false)
|
||||||
{
|
{
|
||||||
|
var result = new List<string>();
|
||||||
var dir = CreateSmbDirectoryForListFiles(path);
|
var dir = CreateSmbDirectoryForListFiles(path);
|
||||||
AssertDirectoryExists(dir, path);
|
AssertDirectoryExists(dir, path);
|
||||||
|
|
||||||
@ -541,16 +551,18 @@ namespace Emby.Server.Implementations.IO
|
|||||||
|
|
||||||
foreach (var file in list)
|
foreach (var file in list)
|
||||||
{
|
{
|
||||||
yield return GetReturnPath(file);
|
result.Add(GetReturnPath(file));
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> GetFilePaths(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
|
public List<string> GetFilePaths(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
|
||||||
{
|
{
|
||||||
var dir = CreateSmbDirectoryForListFiles(path);
|
var dir = CreateSmbDirectoryForListFiles(path);
|
||||||
AssertDirectoryExists(dir, path);
|
AssertDirectoryExists(dir, path);
|
||||||
|
|
||||||
var list = ListFiles(dir, recursive);
|
var list = ListFiles(dir, recursive);
|
||||||
|
var result = new List<string>();
|
||||||
|
|
||||||
foreach (var file in list)
|
foreach (var file in list)
|
||||||
{
|
{
|
||||||
@ -561,44 +573,52 @@ namespace Emby.Server.Implementations.IO
|
|||||||
|
|
||||||
if (extensions == null || extensions.Length == 0 || extensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase))
|
if (extensions == null || extensions.Length == 0 || extensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
yield return filePath;
|
result.Add(filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
|
public List<string> GetDirectoryPaths(string path, bool recursive = false)
|
||||||
{
|
{
|
||||||
var dir = CreateSmbDirectoryForListFiles(path);
|
var dir = CreateSmbDirectoryForListFiles(path);
|
||||||
AssertDirectoryExists(dir, path);
|
AssertDirectoryExists(dir, path);
|
||||||
|
|
||||||
var list = ListFiles(dir, recursive);
|
var list = ListFiles(dir, recursive);
|
||||||
|
var result = new List<string>();
|
||||||
|
|
||||||
foreach (var file in list)
|
foreach (var file in list)
|
||||||
{
|
{
|
||||||
if (file.IsDirectory())
|
if (file.IsDirectory())
|
||||||
{
|
{
|
||||||
yield return GetReturnPath(file);
|
result.Add(GetReturnPath(file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<SmbFile> ListFiles(SmbFile dir, bool recursive)
|
private IEnumerable<SmbFile> ListFiles(SmbFile dir, bool recursive)
|
||||||
{
|
{
|
||||||
var list = dir.ListFiles();
|
var list = dir.ListFiles();
|
||||||
|
var result = new List<SmbFile>();
|
||||||
|
|
||||||
foreach (var file in list)
|
foreach (var file in list)
|
||||||
{
|
{
|
||||||
yield return file;
|
result.Add(file);
|
||||||
|
|
||||||
if (recursive && file.IsDirectory())
|
if (recursive && file.IsDirectory())
|
||||||
{
|
{
|
||||||
foreach (var subFile in ListFiles(file, recursive))
|
foreach (var subFile in ListFiles(file, recursive))
|
||||||
{
|
{
|
||||||
yield return subFile;
|
result.Add(subFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,7 +274,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileSystem.GetFilePaths(fullPath).Any(i => string.Equals(Path.GetExtension(i), ".vob", StringComparison.OrdinalIgnoreCase));
|
return directoryService.GetFilePaths(fullPath).Any(i => string.Equals(Path.GetExtension(i), ".vob", StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -2445,6 +2445,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
{
|
{
|
||||||
existingTimer.Status = RecordingStatus.Cancelled;
|
existingTimer.Status = RecordingStatus.Cancelled;
|
||||||
}
|
}
|
||||||
|
else if (!existingTimer.IsManual)
|
||||||
|
{
|
||||||
|
existingTimer.Status = RecordingStatus.New;
|
||||||
|
}
|
||||||
|
|
||||||
if (existingTimer.Status != RecordingStatus.Cancelled)
|
if (existingTimer.Status != RecordingStatus.Cancelled)
|
||||||
{
|
{
|
||||||
|
@ -1232,6 +1232,8 @@ namespace Emby.Server.Implementations.LiveTv
|
|||||||
var newChannelIdList = new List<Guid>();
|
var newChannelIdList = new List<Guid>();
|
||||||
var newProgramIdList = new List<Guid>();
|
var newProgramIdList = new List<Guid>();
|
||||||
|
|
||||||
|
var cleanDatabase = true;
|
||||||
|
|
||||||
foreach (var service in _services)
|
foreach (var service in _services)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
@ -1254,6 +1256,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
cleanDatabase = false;
|
||||||
_logger.ErrorException("Error refreshing channels for service", ex);
|
_logger.ErrorException("Error refreshing channels for service", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1264,8 +1267,11 @@ namespace Emby.Server.Implementations.LiveTv
|
|||||||
progress.Report(100 * percent);
|
progress.Report(100 * percent);
|
||||||
}
|
}
|
||||||
|
|
||||||
await CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken).ConfigureAwait(false);
|
if (cleanDatabase)
|
||||||
await CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken).ConfigureAwait(false);
|
{
|
||||||
|
await CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken).ConfigureAwait(false);
|
||||||
|
await CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
var coreService = _services.OfType<EmbyTV.EmbyTV>().FirstOrDefault();
|
var coreService = _services.OfType<EmbyTV.EmbyTV>().FirstOrDefault();
|
||||||
|
|
||||||
@ -1291,8 +1297,9 @@ namespace Emby.Server.Implementations.LiveTv
|
|||||||
{
|
{
|
||||||
progress.Report(10);
|
progress.Report(10);
|
||||||
|
|
||||||
var allChannels = await GetChannels(service, cancellationToken).ConfigureAwait(false);
|
var allChannelsList = (await service.GetChannelsAsync(cancellationToken).ConfigureAwait(false))
|
||||||
var allChannelsList = allChannels.ToList();
|
.Select(i => new Tuple<string, ChannelInfo>(service.Name, i))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
var list = new List<LiveTvChannel>();
|
var list = new List<LiveTvChannel>();
|
||||||
|
|
||||||
@ -1507,13 +1514,6 @@ namespace Emby.Server.Implementations.LiveTv
|
|||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<Tuple<string, ChannelInfo>>> GetChannels(ILiveTvService service, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var channels = await service.GetChannelsAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return channels.Select(i => new Tuple<string, ChannelInfo>(service.Name, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
private DateTime _lastRecordingRefreshTime;
|
private DateTime _lastRecordingRefreshTime;
|
||||||
private async Task RefreshRecordings(Guid internalLiveTvFolderId, CancellationToken cancellationToken)
|
private async Task RefreshRecordings(Guid internalLiveTvFolderId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
@ -73,9 +73,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
//OpenedMediaSource.SupportsTranscoding = true;
|
//OpenedMediaSource.SupportsTranscoding = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Close()
|
protected override void CloseInternal()
|
||||||
{
|
{
|
||||||
Logger.Info("Closing HDHR live stream");
|
|
||||||
LiveStreamCancellationTokenSource.Cancel();
|
LiveStreamCancellationTokenSource.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +105,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
{
|
{
|
||||||
Logger.ErrorException("Error copying live stream.", ex);
|
Logger.ErrorException("Error copying live stream.", ex);
|
||||||
}
|
}
|
||||||
|
EnableStreamSharing = false;
|
||||||
await DeleteTempFile(TempFilePath).ConfigureAwait(false);
|
await DeleteTempFile(TempFilePath).ConfigureAwait(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -105,9 +105,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
await taskCompletionSource.Task.ConfigureAwait(false);
|
await taskCompletionSource.Task.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Close()
|
protected override void CloseInternal()
|
||||||
{
|
{
|
||||||
Logger.Info("Closing HDHR UDP live stream");
|
|
||||||
LiveStreamCancellationTokenSource.Cancel();
|
LiveStreamCancellationTokenSource.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +133,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
openTaskCompletionSource.TrySetException(ex);
|
openTaskCompletionSource.TrySetException(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EnableStreamSharing = false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await hdHomerunManager.StopStreaming().ConfigureAwait(false);
|
await hdHomerunManager.StopStreaming().ConfigureAwait(false);
|
||||||
|
@ -52,7 +52,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
return Task.FromResult(true);
|
return Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Close()
|
public void Close()
|
||||||
|
{
|
||||||
|
EnableStreamSharing = false;
|
||||||
|
|
||||||
|
Logger.Info("Closing " + GetType().Name);
|
||||||
|
|
||||||
|
CloseInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void CloseInternal()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Latest": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438",
|
"Latest": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438",
|
||||||
"ValueSpecialEpisodeName": "\u0421\u043f\u0435\u0446\u0438\u0430\u043b\u043d\u0438 - {0}",
|
"ValueSpecialEpisodeName": "\u0421\u043f\u0435\u0446\u0438\u0430\u043b\u043d\u0438 - {0}",
|
||||||
"Inherit": "Inherit",
|
"Inherit": "\u041d\u0430\u0441\u043b\u0435\u0434\u044f\u0432\u0430\u043d\u0435",
|
||||||
"Books": "\u041a\u043d\u0438\u0433\u0438",
|
"Books": "\u041a\u043d\u0438\u0433\u0438",
|
||||||
"Music": "\u041c\u0443\u0437\u0438\u043a\u0430",
|
"Music": "\u041c\u0443\u0437\u0438\u043a\u0430",
|
||||||
"Games": "\u0418\u0433\u0440\u0438",
|
"Games": "\u0418\u0433\u0440\u0438",
|
||||||
@ -35,7 +35,7 @@
|
|||||||
"UserDownloadingItemWithValues": "{0} is downloading {1}",
|
"UserDownloadingItemWithValues": "{0} is downloading {1}",
|
||||||
"HeaderLiveTV": "\u0422\u0435\u043b\u0435\u0432\u0438\u0437\u0438\u044f \u043d\u0430 \u0436\u0438\u0432\u043e",
|
"HeaderLiveTV": "\u0422\u0435\u043b\u0435\u0432\u0438\u0437\u0438\u044f \u043d\u0430 \u0436\u0438\u0432\u043e",
|
||||||
"ChapterNameValue": "\u0413\u043b\u0430\u0432\u0430 {0}",
|
"ChapterNameValue": "\u0413\u043b\u0430\u0432\u0430 {0}",
|
||||||
"ScheduledTaskFailedWithName": "{0} failed",
|
"ScheduledTaskFailedWithName": "{0} \u0441\u0435 \u043f\u0440\u043e\u0432\u0430\u043b\u0438",
|
||||||
"LabelRunningTimeValue": "Running time: {0}",
|
"LabelRunningTimeValue": "Running time: {0}",
|
||||||
"ScheduledTaskStartedWithName": "{0} \u0437\u0430\u043f\u043e\u0447\u043d\u0430",
|
"ScheduledTaskStartedWithName": "{0} \u0437\u0430\u043f\u043e\u0447\u043d\u0430",
|
||||||
"VersionNumber": "\u0412\u0435\u0440\u0441\u0438\u044f {0}",
|
"VersionNumber": "\u0412\u0435\u0440\u0441\u0438\u044f {0}",
|
||||||
@ -49,40 +49,40 @@
|
|||||||
"DeviceOnlineWithName": "{0} \u0435 \u0441\u0432\u044a\u0440\u0437\u0430\u043d",
|
"DeviceOnlineWithName": "{0} \u0435 \u0441\u0432\u044a\u0440\u0437\u0430\u043d",
|
||||||
"UserOnlineFromDevice": "{0} \u0435 \u043d\u0430 \u043b\u0438\u043d\u0438\u044f \u043e\u0442 {1}",
|
"UserOnlineFromDevice": "{0} \u0435 \u043d\u0430 \u043b\u0438\u043d\u0438\u044f \u043e\u0442 {1}",
|
||||||
"ProviderValue": "\u0414\u043e\u0441\u0442\u0430\u0432\u0447\u0438\u043a: {0}",
|
"ProviderValue": "\u0414\u043e\u0441\u0442\u0430\u0432\u0447\u0438\u043a: {0}",
|
||||||
"SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
|
"SubtitlesDownloadedForItem": "\u0418\u0437\u0442\u0435\u0433\u043b\u0435\u043d\u0438 \u0441\u0430 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u0438 \u0437\u0430 {0}",
|
||||||
"UserCreatedWithName": "User {0} has been created",
|
"UserCreatedWithName": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f\u0442 {0} \u0435 \u0441\u044a\u0437\u0434\u0430\u0434\u0435\u043d",
|
||||||
"UserPasswordChangedWithName": "Password has been changed for user {0}",
|
"UserPasswordChangedWithName": "\u041f\u0430\u0440\u043e\u043b\u0430\u0442\u0430 \u043d\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f {0} \u0435 \u043f\u0440\u043e\u043c\u0435\u043d\u0435\u043d\u0430",
|
||||||
"UserDeletedWithName": "User {0} has been deleted",
|
"UserDeletedWithName": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f\u0442 {0} \u0435 \u0438\u0437\u0442\u0440\u0438\u0442",
|
||||||
"UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
|
"UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
|
||||||
"MessageServerConfigurationUpdated": "Server configuration has been updated",
|
"MessageServerConfigurationUpdated": "Server configuration has been updated",
|
||||||
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
|
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
|
||||||
"MessageApplicationUpdated": "Emby Server has been updated",
|
"MessageApplicationUpdated": "\u0421\u044a\u0440\u0432\u044a\u0440\u044a\u0442 \u0435 \u043e\u0431\u043d\u043e\u0432\u0435\u043d",
|
||||||
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
|
||||||
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
|
"AuthenticationSucceededWithUserName": "{0} \u0441\u0435 \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e",
|
||||||
"UserOfflineFromDevice": "{0} has disconnected from {1}",
|
"UserOfflineFromDevice": "{0} \u0441\u0435 \u0440\u0430\u0437\u043a\u0430\u0447\u0438 \u043e\u0442 {1}",
|
||||||
"DeviceOfflineWithName": "{0} has disconnected",
|
"DeviceOfflineWithName": "{0} \u0441\u0435 \u0440\u0430\u0437\u043a\u0430\u0447\u0438",
|
||||||
"UserStartedPlayingItemWithValues": "{0} has started playing {1}",
|
"UserStartedPlayingItemWithValues": "{0} has started playing {1}",
|
||||||
"UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
|
"UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
|
||||||
"NotificationOptionPluginError": "Plugin failure",
|
"NotificationOptionPluginError": "\u0413\u0440\u0435\u0448\u043a\u0430 \u0432 \u043f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430",
|
||||||
"NotificationOptionApplicationUpdateAvailable": "Application update available",
|
"NotificationOptionApplicationUpdateAvailable": "\u041d\u0430\u043b\u0438\u0447\u043d\u043e \u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u0430\u0442\u0430",
|
||||||
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
|
"NotificationOptionApplicationUpdateInstalled": "\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\u0442\u043e \u043d\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u0430\u0442\u0430 \u0435 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u043e",
|
||||||
"NotificationOptionPluginUpdateInstalled": "\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\u0442\u043e \u043d\u0430 \u043f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430\u0442\u0430 \u0435 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u043e",
|
"NotificationOptionPluginUpdateInstalled": "\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\u0442\u043e \u043d\u0430 \u043f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430\u0442\u0430 \u0435 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u043e",
|
||||||
"NotificationOptionPluginInstalled": "\u041f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430\u0442\u0430 \u0435 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0430",
|
"NotificationOptionPluginInstalled": "\u041f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430\u0442\u0430 \u0435 \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0430",
|
||||||
"NotificationOptionPluginUninstalled": "\u041f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430\u0442\u0430 \u0435 \u0434\u0435\u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0430",
|
"NotificationOptionPluginUninstalled": "\u041f\u0440\u0438\u0441\u0442\u0430\u0432\u043a\u0430\u0442\u0430 \u0435 \u0434\u0435\u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0430",
|
||||||
"NotificationOptionVideoPlayback": "Video playback started",
|
"NotificationOptionVideoPlayback": "\u0412\u044a\u0437\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u0437\u0430\u043f\u043e\u0447\u043d\u0430",
|
||||||
"NotificationOptionAudioPlayback": "Audio playback started",
|
"NotificationOptionAudioPlayback": "\u0412\u044a\u0437\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0437\u0432\u0443\u043a \u0437\u0430\u043f\u043e\u0447\u043d\u0430",
|
||||||
"NotificationOptionGamePlayback": "Game playback started",
|
"NotificationOptionGamePlayback": "\u0412\u044a\u0437\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0438\u0433\u0440\u0430\u0442\u0430 \u0437\u0430\u043f\u043e\u0447\u043d\u0430",
|
||||||
"NotificationOptionVideoPlaybackStopped": "Video playback stopped",
|
"NotificationOptionVideoPlaybackStopped": "\u0412\u044a\u0437\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u0435 \u0441\u043f\u0440\u044f\u043d\u043e",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
|
"NotificationOptionAudioPlaybackStopped": "\u0412\u044a\u0437\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0437\u0432\u0443\u043a \u0435 \u0441\u043f\u0440\u044f\u043d\u043e",
|
||||||
"NotificationOptionGamePlaybackStopped": "Game playback stopped",
|
"NotificationOptionGamePlaybackStopped": "\u0412\u044a\u0437\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0436\u0434\u0430\u043d\u0435\u0442\u043e \u043d\u0430 \u0438\u0433\u0440\u0430\u0442\u0430 \u0435 \u0441\u043f\u0440\u044f\u043d\u0430",
|
||||||
"NotificationOptionTaskFailed": "Scheduled task failure",
|
"NotificationOptionTaskFailed": "\u0413\u0440\u0435\u0448\u043a\u0430 \u0432 \u043f\u043b\u0430\u043d\u0438\u0440\u0430\u043d\u0430 \u0437\u0430\u0434\u0430\u0447\u0430",
|
||||||
"NotificationOptionInstallationFailed": "Installation failure",
|
"NotificationOptionInstallationFailed": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0438\u043d\u0441\u0442\u0430\u043b\u0438\u0440\u0430\u043d\u0435",
|
||||||
"NotificationOptionNewLibraryContent": "\u0414\u043e\u0431\u0430\u0432\u0435\u043d\u043e \u0435 \u043d\u043e\u0432\u043e \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435",
|
"NotificationOptionNewLibraryContent": "\u0414\u043e\u0431\u0430\u0432\u0435\u043d\u043e \u0435 \u043d\u043e\u0432\u043e \u0441\u044a\u0434\u044a\u0440\u0436\u0430\u043d\u0438\u0435",
|
||||||
"NotificationOptionCameraImageUploaded": "Camera image uploaded",
|
"NotificationOptionCameraImageUploaded": "\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u0442\u043e \u043e\u0442 \u0444\u043e\u0442\u043e\u0430\u043f\u0430\u0440\u0430\u0442\u0430 \u0435 \u043a\u0430\u0447\u0435\u043d\u043e",
|
||||||
"NotificationOptionUserLockedOut": "User locked out",
|
"NotificationOptionUserLockedOut": "User locked out",
|
||||||
"NotificationOptionServerRestartRequired": "\u041d\u0443\u0436\u043d\u043e \u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u043d\u0430 \u0441\u044a\u0440\u0432\u044a\u0440\u0430",
|
"NotificationOptionServerRestartRequired": "\u041d\u0443\u0436\u043d\u043e \u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e \u043f\u0443\u0441\u043a\u0430\u043d\u0435 \u043d\u0430 \u0441\u044a\u0440\u0432\u044a\u0440\u0430",
|
||||||
"UserLockedOutWithName": "User {0} has been locked out",
|
"UserLockedOutWithName": "User {0} has been locked out",
|
||||||
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
|
"SubtitleDownloadFailureForItem": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0438\u0437\u0442\u0435\u0433\u043b\u044f\u043d\u0435 \u043d\u0430 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u0438 \u0437\u0430 {0}",
|
||||||
"Sync": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u0430\u043d\u0435",
|
"Sync": "\u0421\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u0430\u043d\u0435",
|
||||||
"User": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b",
|
"User": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b",
|
||||||
"System": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430",
|
"System": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430",
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
"Latest": "Latest",
|
"Latest": "Latest",
|
||||||
"ValueSpecialEpisodeName": "Special - {0}",
|
"ValueSpecialEpisodeName": "Special - {0}",
|
||||||
"Inherit": "Inherit",
|
"Inherit": "Inherit",
|
||||||
"Books": "Books",
|
"Books": "Knihy",
|
||||||
"Music": "Music",
|
"Music": "Hudba",
|
||||||
"Games": "Games",
|
"Games": "Hry",
|
||||||
"Photos": "Photos",
|
"Photos": "Fotky",
|
||||||
"MixedContent": "Mixed content",
|
"MixedContent": "Zmie\u0161an\u00fd obsah",
|
||||||
"MusicVideos": "Music videos",
|
"MusicVideos": "Hudobn\u00e9 vide\u00e1",
|
||||||
"HomeVideos": "Home videos",
|
"HomeVideos": "Dom\u00e1ce vide\u00e1",
|
||||||
"Playlists": "Playlists",
|
"Playlists": "Playlists",
|
||||||
"HeaderRecordingGroups": "Recording Groups",
|
"HeaderRecordingGroups": "Recording Groups",
|
||||||
"HeaderContinueWatching": "Continue Watching",
|
"HeaderContinueWatching": "Continue Watching",
|
||||||
@ -16,20 +16,20 @@
|
|||||||
"HeaderFavoriteSongs": "Ob\u013e\u00faben\u00e9 pesni\u010dky",
|
"HeaderFavoriteSongs": "Ob\u013e\u00faben\u00e9 pesni\u010dky",
|
||||||
"HeaderAlbumArtists": "Album Artists",
|
"HeaderAlbumArtists": "Album Artists",
|
||||||
"HeaderFavoriteAlbums": "Favorite Albums",
|
"HeaderFavoriteAlbums": "Favorite Albums",
|
||||||
"HeaderFavoriteEpisodes": "Favorite Episodes",
|
"HeaderFavoriteEpisodes": "Ob\u013e\u00faben\u00e9 epiz\u00f3dy",
|
||||||
"HeaderFavoriteShows": "Ob\u013e\u00faben\u00e9 seri\u00e1ly",
|
"HeaderFavoriteShows": "Ob\u013e\u00faben\u00e9 seri\u00e1ly",
|
||||||
"HeaderNextUp": "Next Up",
|
"HeaderNextUp": "Nasleduje",
|
||||||
"Favorites": "Ob\u013e\u00faben\u00e9",
|
"Favorites": "Ob\u013e\u00faben\u00e9",
|
||||||
"Collections": "Collections",
|
"Collections": "Zbierky",
|
||||||
"Channels": "Channels",
|
"Channels": "Kan\u00e1ly",
|
||||||
"Movies": "Movies",
|
"Movies": "Filmy",
|
||||||
"Albums": "Albums",
|
"Albums": "Albumy",
|
||||||
"Artists": "Artists",
|
"Artists": "Umelci",
|
||||||
"Folders": "Folders",
|
"Folders": "Prie\u010dinky",
|
||||||
"Songs": "Songs",
|
"Songs": "Skladby",
|
||||||
"TvShows": "TV Shows",
|
"TvShows": "TV Shows",
|
||||||
"Shows": "Series",
|
"Shows": "Series",
|
||||||
"Genres": "Genres",
|
"Genres": "\u017d\u00e1nre",
|
||||||
"NameSeasonNumber": "Season {0}",
|
"NameSeasonNumber": "Season {0}",
|
||||||
"AppDeviceValues": "App: {0}, Device: {1}",
|
"AppDeviceValues": "App: {0}, Device: {1}",
|
||||||
"UserDownloadingItemWithValues": "{0} is downloading {1}",
|
"UserDownloadingItemWithValues": "{0} is downloading {1}",
|
||||||
@ -86,6 +86,6 @@
|
|||||||
"Sync": "Sync",
|
"Sync": "Sync",
|
||||||
"User": "User",
|
"User": "User",
|
||||||
"System": "System",
|
"System": "System",
|
||||||
"Application": "Application",
|
"Application": "Aplik\u00e1cia",
|
||||||
"Plugin": "Plugin"
|
"Plugin": "Plugin"
|
||||||
}
|
}
|
@ -27,7 +27,7 @@
|
|||||||
"Artists": "Artister",
|
"Artists": "Artister",
|
||||||
"Folders": "Mappar",
|
"Folders": "Mappar",
|
||||||
"Songs": "L\u00e5tar",
|
"Songs": "L\u00e5tar",
|
||||||
"TvShows": "TV Shows",
|
"TvShows": "TV-serier",
|
||||||
"Shows": "Serier",
|
"Shows": "Serier",
|
||||||
"Genres": "Genrer",
|
"Genres": "Genrer",
|
||||||
"NameSeasonNumber": "S\u00e4song {0}",
|
"NameSeasonNumber": "S\u00e4song {0}",
|
||||||
|
@ -64,7 +64,7 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
|
|
||||||
var localizationPath = LocalizationPath;
|
var localizationPath = LocalizationPath;
|
||||||
|
|
||||||
_fileSystem.CreateDirectory(localizationPath);
|
_fileSystem.CreateDirectory(localizationPath);
|
||||||
|
|
||||||
var existingFiles = GetRatingsFiles(localizationPath)
|
var existingFiles = GetRatingsFiles(localizationPath)
|
||||||
.Select(Path.GetFileName)
|
.Select(Path.GetFileName)
|
||||||
@ -91,7 +91,7 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var file in GetRatingsFiles(localizationPath))
|
foreach (var file in GetRatingsFiles(localizationPath))
|
||||||
{
|
{
|
||||||
LoadRatings(file);
|
LoadRatings(file);
|
||||||
@ -128,12 +128,20 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
return _textLocalizer.NormalizeFormKD(text);
|
return _textLocalizer.NormalizeFormKD(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CultureDto[] _cultures;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the cultures.
|
/// Gets the cultures.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>IEnumerable{CultureDto}.</returns>
|
/// <returns>IEnumerable{CultureDto}.</returns>
|
||||||
public CultureDto[] GetCultures()
|
public CultureDto[] GetCultures()
|
||||||
{
|
{
|
||||||
|
var result = _cultures;
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
var type = GetType();
|
var type = GetType();
|
||||||
var path = type.Namespace + ".iso6392.txt";
|
var path = type.Namespace + ".iso6392.txt";
|
||||||
|
|
||||||
@ -166,10 +174,14 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return list.Where(i => !string.IsNullOrWhiteSpace(i.Name) &&
|
result = list.Where(i => !string.IsNullOrWhiteSpace(i.Name) &&
|
||||||
!string.IsNullOrWhiteSpace(i.DisplayName) &&
|
!string.IsNullOrWhiteSpace(i.DisplayName) &&
|
||||||
!string.IsNullOrWhiteSpace(i.ThreeLetterISOLanguageName) &&
|
!string.IsNullOrWhiteSpace(i.ThreeLetterISOLanguageName) &&
|
||||||
!string.IsNullOrWhiteSpace(i.TwoLetterISOLanguageName)).ToArray();
|
!string.IsNullOrWhiteSpace(i.TwoLetterISOLanguageName)).ToArray();
|
||||||
|
|
||||||
|
_cultures = result;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -239,7 +251,7 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
/// <returns>Dictionary{System.StringParentalRating}.</returns>
|
/// <returns>Dictionary{System.StringParentalRating}.</returns>
|
||||||
private void LoadRatings(string file)
|
private void LoadRatings(string file)
|
||||||
{
|
{
|
||||||
var dict = _fileSystem.ReadAllLines(file).Select(i =>
|
var dict = _fileSystem.ReadAllLines(file).Select(i =>
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(i))
|
if (!string.IsNullOrWhiteSpace(i))
|
||||||
{
|
{
|
||||||
@ -269,7 +281,7 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
_allParentalRatings.TryAdd(countryCode, dict);
|
_allParentalRatings.TryAdd(countryCode, dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly string[] _unratedValues = {"n/a", "unrated", "not rated"};
|
private readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the rating level.
|
/// Gets the rating level.
|
||||||
|
@ -17,6 +17,7 @@ using System.Threading.Tasks;
|
|||||||
using MediaBrowser.Controller.IO;
|
using MediaBrowser.Controller.IO;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.MediaEncoder
|
namespace Emby.Server.Implementations.MediaEncoder
|
||||||
{
|
{
|
||||||
@ -89,7 +90,7 @@ namespace Emby.Server.Implementations.MediaEncoder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly long FirstChapterTicks = TimeSpan.FromSeconds(15).Ticks;
|
private static readonly long FirstChapterTicks = TimeSpan.FromSeconds(15).Ticks;
|
||||||
|
|
||||||
public async Task<bool> RefreshChapterImages(Video video, List<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken)
|
public async Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, List<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!IsEligibleForChapterImageExtraction(video))
|
if (!IsEligibleForChapterImageExtraction(video))
|
||||||
{
|
{
|
||||||
@ -101,7 +102,7 @@ namespace Emby.Server.Implementations.MediaEncoder
|
|||||||
|
|
||||||
var runtimeTicks = video.RunTimeTicks ?? 0;
|
var runtimeTicks = video.RunTimeTicks ?? 0;
|
||||||
|
|
||||||
var currentImages = GetSavedChapterImages(video);
|
var currentImages = GetSavedChapterImages(video, directoryService);
|
||||||
|
|
||||||
foreach (var chapter in chapters)
|
foreach (var chapter in chapters)
|
||||||
{
|
{
|
||||||
@ -194,13 +195,13 @@ namespace Emby.Server.Implementations.MediaEncoder
|
|||||||
return Path.Combine(GetChapterImagesPath(video), filename);
|
return Path.Combine(GetChapterImagesPath(video), filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<string> GetSavedChapterImages(Video video)
|
private List<string> GetSavedChapterImages(Video video, IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
var path = GetChapterImagesPath(video);
|
var path = GetChapterImagesPath(video);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return _fileSystem.GetFilePaths(path)
|
return directoryService.GetFilePaths(path)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
|
@ -16,6 +16,7 @@ using MediaBrowser.Model.IO;
|
|||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using MediaBrowser.Model.Extensions;
|
using MediaBrowser.Model.Extensions;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.ScheduledTasks
|
namespace Emby.Server.Implementations.ScheduledTasks
|
||||||
{
|
{
|
||||||
@ -120,6 +121,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
|
|||||||
previouslyFailedImages = new List<string>();
|
previouslyFailedImages = new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var directoryService = new DirectoryService(_fileSystem);
|
||||||
|
|
||||||
foreach (var video in videos)
|
foreach (var video in videos)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
@ -132,7 +135,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
|
|||||||
{
|
{
|
||||||
var chapters = _itemRepo.GetChapters(video.Id);
|
var chapters = _itemRepo.GetChapters(video.Id);
|
||||||
|
|
||||||
var success = await _encodingManager.RefreshChapterImages(video, chapters, extract, true, CancellationToken.None);
|
var success = await _encodingManager.RefreshChapterImages(video, directoryService, chapters, extract, true, CancellationToken.None);
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
|
@ -51,6 +51,11 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool SupportsMediaSourceSelection()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
[IgnoreDataMember]
|
[IgnoreDataMember]
|
||||||
public override bool SupportsPlayedStatus
|
public override bool SupportsPlayedStatus
|
||||||
{
|
{
|
||||||
|
@ -2040,7 +2040,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
.Where(i => i.IsLocalFile)
|
.Where(i => i.IsLocalFile)
|
||||||
.Select(i => FileSystem.GetDirectoryName(i.Path))
|
.Select(i => FileSystem.GetDirectoryName(i.Path))
|
||||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
.SelectMany(i => FileSystem.GetFilePaths(i))
|
.SelectMany(i => directoryService.GetFilePaths(i))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var deletedImages = ImageInfos
|
var deletedImages = ImageInfos
|
||||||
|
@ -13,5 +13,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <returns>Task{IEnumerable{MediaSourceInfo}}.</returns>
|
/// <returns>Task{IEnumerable{MediaSourceInfo}}.</returns>
|
||||||
List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution);
|
List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution);
|
||||||
List<MediaStream> GetMediaStreams();
|
List<MediaStream> GetMediaStreams();
|
||||||
|
bool SupportsMediaSourceSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,11 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
get { return true; }
|
get { return true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool SupportsMediaSourceSelection()
|
||||||
|
{
|
||||||
|
return SourceType == SourceType.Library;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the timestamp.
|
/// Gets or sets the timestamp.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -24,6 +24,11 @@ namespace MediaBrowser.Controller.LiveTv
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool SupportsMediaSourceSelection()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public override UnratedItem GetBlockUnratedType()
|
public override UnratedItem GetBlockUnratedType()
|
||||||
{
|
{
|
||||||
return UnratedItem.LiveTvChannel;
|
return UnratedItem.LiveTvChannel;
|
||||||
|
@ -1325,6 +1325,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue)
|
if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue)
|
||||||
{
|
{
|
||||||
videoSizeParam = string.Format("scale={0}:{1}", state.VideoStream.Width.Value.ToString(_usCulture), state.VideoStream.Height.Value.ToString(_usCulture));
|
videoSizeParam = string.Format("scale={0}:{1}", state.VideoStream.Width.Value.ToString(_usCulture), state.VideoStream.Height.Value.ToString(_usCulture));
|
||||||
|
|
||||||
|
videoSizeParam += ":force_original_aspect_ratio=decrease";
|
||||||
}
|
}
|
||||||
|
|
||||||
var mapPrefix = state.SubtitleStream.IsExternal ?
|
var mapPrefix = state.SubtitleStream.IsExternal ?
|
||||||
@ -1335,7 +1337,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
? 0
|
? 0
|
||||||
: state.SubtitleStream.Index;
|
: state.SubtitleStream.Index;
|
||||||
|
|
||||||
return string.Format(" -filter_complex \"[{0}:{1}]{4}[sub] ; [0:{2}] [sub] overlay{3}\"",
|
return string.Format(" -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay{3}\"",
|
||||||
mapPrefix.ToString(_usCulture),
|
mapPrefix.ToString(_usCulture),
|
||||||
subtitleStreamIndex.ToString(_usCulture),
|
subtitleStreamIndex.ToString(_usCulture),
|
||||||
state.VideoStream.Index.ToString(_usCulture),
|
state.VideoStream.Index.ToString(_usCulture),
|
||||||
@ -2094,6 +2096,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
args += " -avoid_negative_ts disabled -start_at_zero";
|
args += " -avoid_negative_ts disabled -start_at_zero";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is for internal graphical subs
|
||||||
|
if (hasGraphicalSubs)
|
||||||
|
{
|
||||||
|
args += GetGraphicalSubtitleParam(state, encodingOptions, videoCodec);
|
||||||
|
}
|
||||||
|
|
||||||
var qualityParam = GetVideoQualityParam(state, videoCodec, encodingOptions, defaultH264Preset);
|
var qualityParam = GetVideoQualityParam(state, videoCodec, encodingOptions, defaultH264Preset);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(qualityParam))
|
if (!string.IsNullOrEmpty(qualityParam))
|
||||||
@ -2101,12 +2109,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
args += " " + qualityParam.Trim();
|
args += " " + qualityParam.Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is for internal graphical subs
|
|
||||||
if (hasGraphicalSubs)
|
|
||||||
{
|
|
||||||
args += GetGraphicalSubtitleParam(state, encodingOptions, videoCodec);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!state.RunTimeTicks.HasValue)
|
if (!state.RunTimeTicks.HasValue)
|
||||||
{
|
{
|
||||||
args += " -flags -global_header";
|
args += " -flags -global_header";
|
||||||
|
@ -3,6 +3,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Controller.Providers;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.MediaEncoding
|
namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
@ -11,6 +12,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refreshes the chapter images.
|
/// Refreshes the chapter images.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<bool> RefreshChapterImages(Video video, List<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken);
|
Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, List<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,11 @@ namespace MediaBrowser.Controller.Providers
|
|||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, FileSystemMetadata[]> _cache =
|
private readonly Dictionary<string, FileSystemMetadata[]> _cache = new Dictionary<string, FileSystemMetadata[]>(StringComparer.OrdinalIgnoreCase);
|
||||||
new ConcurrentDictionary<string, FileSystemMetadata[]>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, FileSystemMetadata> _fileCache =
|
private readonly Dictionary<string, FileSystemMetadata> _fileCache = new Dictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
|
||||||
new ConcurrentDictionary<string, FileSystemMetadata>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
private readonly Dictionary<string, List<string>> _filePathCache = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
public DirectoryService(ILogger logger, IFileSystem fileSystem)
|
public DirectoryService(ILogger logger, IFileSystem fileSystem)
|
||||||
{
|
{
|
||||||
@ -32,11 +32,6 @@ namespace MediaBrowser.Controller.Providers
|
|||||||
}
|
}
|
||||||
|
|
||||||
public FileSystemMetadata[] GetFileSystemEntries(string path)
|
public FileSystemMetadata[] GetFileSystemEntries(string path)
|
||||||
{
|
|
||||||
return GetFileSystemEntries(path, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FileSystemMetadata[] GetFileSystemEntries(string path, bool clearCache)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(path))
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
{
|
{
|
||||||
@ -45,13 +40,6 @@ namespace MediaBrowser.Controller.Providers
|
|||||||
|
|
||||||
FileSystemMetadata[] entries;
|
FileSystemMetadata[] entries;
|
||||||
|
|
||||||
if (clearCache)
|
|
||||||
{
|
|
||||||
FileSystemMetadata[] removed;
|
|
||||||
|
|
||||||
_cache.TryRemove(path, out removed);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_cache.TryGetValue(path, out entries))
|
if (!_cache.TryGetValue(path, out entries))
|
||||||
{
|
{
|
||||||
//_logger.Debug("Getting files for " + path);
|
//_logger.Debug("Getting files for " + path);
|
||||||
@ -66,21 +54,17 @@ namespace MediaBrowser.Controller.Providers
|
|||||||
entries = new FileSystemMetadata[] { };
|
entries = new FileSystemMetadata[] { };
|
||||||
}
|
}
|
||||||
|
|
||||||
_cache.TryAdd(path, entries);
|
//_cache.TryAdd(path, entries);
|
||||||
|
_cache[path] = entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileSystemMetadata> GetFiles(string path)
|
public List<FileSystemMetadata> GetFiles(string path)
|
||||||
{
|
|
||||||
return GetFiles(path, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<FileSystemMetadata> GetFiles(string path, bool clearCache)
|
|
||||||
{
|
{
|
||||||
var list = new List<FileSystemMetadata>();
|
var list = new List<FileSystemMetadata>();
|
||||||
var items = GetFileSystemEntries(path, clearCache);
|
var items = GetFileSystemEntries(path);
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
if (!item.IsDirectory)
|
if (!item.IsDirectory)
|
||||||
@ -100,7 +84,8 @@ namespace MediaBrowser.Controller.Providers
|
|||||||
|
|
||||||
if (file != null && file.Exists)
|
if (file != null && file.Exists)
|
||||||
{
|
{
|
||||||
_fileCache.TryAdd(path, file);
|
//_fileCache.TryAdd(path, file);
|
||||||
|
_fileCache[path] = file;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -111,5 +96,24 @@ namespace MediaBrowser.Controller.Providers
|
|||||||
return file;
|
return file;
|
||||||
//return _fileSystem.GetFileInfo(path);
|
//return _fileSystem.GetFileInfo(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<string> GetFilePaths(string path)
|
||||||
|
{
|
||||||
|
return GetFilePaths(path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> GetFilePaths(string path, bool clearCache)
|
||||||
|
{
|
||||||
|
List<string> result;
|
||||||
|
if (clearCache || !_filePathCache.TryGetValue(path, out result))
|
||||||
|
{
|
||||||
|
result = _fileSystem.GetFilePaths(path).ToList();
|
||||||
|
|
||||||
|
_filePathCache[path] = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,5 +8,8 @@ namespace MediaBrowser.Controller.Providers
|
|||||||
FileSystemMetadata[] GetFileSystemEntries(string path);
|
FileSystemMetadata[] GetFileSystemEntries(string path);
|
||||||
List<FileSystemMetadata> GetFiles(string path);
|
List<FileSystemMetadata> GetFiles(string path);
|
||||||
FileSystemMetadata GetFile(string path);
|
FileSystemMetadata GetFile(string path);
|
||||||
|
|
||||||
|
List<string> GetFilePaths(string path);
|
||||||
|
List<string> GetFilePaths(string path, bool clearCache);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -75,7 +75,8 @@ namespace MediaBrowser.Model.Dto
|
|||||||
public bool? CanDownload { get; set; }
|
public bool? CanDownload { get; set; }
|
||||||
|
|
||||||
public bool? HasSubtitles { get; set; }
|
public bool? HasSubtitles { get; set; }
|
||||||
|
public bool? SupportsMediaSourceSelection { get; set; }
|
||||||
|
|
||||||
public string PreferredMetadataLanguage { get; set; }
|
public string PreferredMetadataLanguage { get; set; }
|
||||||
public string PreferredMetadataCountryCode { get; set; }
|
public string PreferredMetadataCountryCode { get; set; }
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ namespace MediaBrowser.Model.Providers
|
|||||||
public bool IsOpenSubtitleVipAccount { get; set; }
|
public bool IsOpenSubtitleVipAccount { get; set; }
|
||||||
|
|
||||||
public bool RequirePerfectMatch { get; set; }
|
public bool RequirePerfectMatch { get; set; }
|
||||||
|
public bool SaveSubtitlesInMediaFolders { get; set; }
|
||||||
|
|
||||||
public SubtitleOptions()
|
public SubtitleOptions()
|
||||||
{
|
{
|
||||||
@ -20,6 +21,7 @@ namespace MediaBrowser.Model.Providers
|
|||||||
|
|
||||||
SkipIfAudioTrackMatches = true;
|
SkipIfAudioTrackMatches = true;
|
||||||
RequirePerfectMatch = true;
|
RequirePerfectMatch = true;
|
||||||
|
SaveSubtitlesInMediaFolders = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -39,6 +39,7 @@
|
|||||||
ChannelDown = 31,
|
ChannelDown = 31,
|
||||||
SetMaxStreamingBitrate = 31,
|
SetMaxStreamingBitrate = 31,
|
||||||
Guide = 32,
|
Guide = 32,
|
||||||
ToggleStats = 33
|
ToggleStats = 33,
|
||||||
|
PlayMediaSource = 34
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -27,6 +27,7 @@ namespace MediaBrowser.Providers.Manager
|
|||||||
protected readonly IFileSystem FileSystem;
|
protected readonly IFileSystem FileSystem;
|
||||||
protected readonly IUserDataManager UserDataManager;
|
protected readonly IUserDataManager UserDataManager;
|
||||||
protected readonly ILibraryManager LibraryManager;
|
protected readonly ILibraryManager LibraryManager;
|
||||||
|
private readonly SubtitleResolver _subtitleResolver;
|
||||||
|
|
||||||
protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager)
|
protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager)
|
||||||
{
|
{
|
||||||
@ -36,6 +37,8 @@ namespace MediaBrowser.Providers.Manager
|
|||||||
FileSystem = fileSystem;
|
FileSystem = fileSystem;
|
||||||
UserDataManager = userDataManager;
|
UserDataManager = userDataManager;
|
||||||
LibraryManager = libraryManager;
|
LibraryManager = libraryManager;
|
||||||
|
|
||||||
|
_subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager, fileSystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ItemUpdateType> RefreshMetadata(IHasMetadata item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
|
public async Task<ItemUpdateType> RefreshMetadata(IHasMetadata item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
|
||||||
@ -76,8 +79,7 @@ namespace MediaBrowser.Providers.Manager
|
|||||||
if (video != null && !video.IsPlaceHolder)
|
if (video != null && !video.IsPlaceHolder)
|
||||||
{
|
{
|
||||||
requiresRefresh = !video.SubtitleFiles
|
requiresRefresh = !video.SubtitleFiles
|
||||||
.SequenceEqual(SubtitleResolver.GetSubtitleFiles(video, refreshOptions.DirectoryService, FileSystem, false)
|
.SequenceEqual(_subtitleResolver.GetExternalSubtitleFiles(video, refreshOptions.DirectoryService, false), StringComparer.Ordinal);
|
||||||
.OrderBy(i => i), StringComparer.OrdinalIgnoreCase);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,7 +229,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
extractDuringScan = libraryOptions.ExtractChapterImagesDuringLibraryScan;
|
extractDuringScan = libraryOptions.ExtractChapterImagesDuringLibraryScan;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _encodingManager.RefreshChapterImages(video, chapters, extractDuringScan, false, cancellationToken).ConfigureAwait(false);
|
await _encodingManager.RefreshChapterImages(video, options.DirectoryService, chapters, extractDuringScan, false, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
_chapterManager.SaveChapters(video.Id.ToString(), chapters);
|
_chapterManager.SaveChapters(video.Id.ToString(), chapters);
|
||||||
}
|
}
|
||||||
@ -472,7 +472,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
var subtitleResolver = new SubtitleResolver(_localization, _fileSystem);
|
var subtitleResolver = new SubtitleResolver(_localization, _fileSystem);
|
||||||
|
|
||||||
var startIndex = currentStreams.Count == 0 ? 0 : (currentStreams.Select(i => i.Index).Max() + 1);
|
var startIndex = currentStreams.Count == 0 ? 0 : (currentStreams.Select(i => i.Index).Max() + 1);
|
||||||
var externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, startIndex, options.DirectoryService, false).ToList();
|
var externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, startIndex, options.DirectoryService, false);
|
||||||
|
|
||||||
var enableSubtitleDownloading = options.MetadataRefreshMode == MetadataRefreshMode.Default ||
|
var enableSubtitleDownloading = options.MetadataRefreshMode == MetadataRefreshMode.Default ||
|
||||||
options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
|
options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
|
||||||
@ -497,7 +497,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
// Rescan
|
// Rescan
|
||||||
if (downloadedLanguages.Count > 0)
|
if (downloadedLanguages.Count > 0)
|
||||||
{
|
{
|
||||||
externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, startIndex, options.DirectoryService, true).ToList();
|
externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, startIndex, options.DirectoryService, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,29 +16,91 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
private readonly ILocalizationManager _localization;
|
private readonly ILocalizationManager _localization;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
|
private string[] SubtitleExtensions = new[]
|
||||||
|
{
|
||||||
|
".srt",
|
||||||
|
".ssa",
|
||||||
|
".ass",
|
||||||
|
".sub",
|
||||||
|
".smi",
|
||||||
|
".sami",
|
||||||
|
".vtt"
|
||||||
|
};
|
||||||
|
|
||||||
public SubtitleResolver(ILocalizationManager localization, IFileSystem fileSystem)
|
public SubtitleResolver(ILocalizationManager localization, IFileSystem fileSystem)
|
||||||
{
|
{
|
||||||
_localization = localization;
|
_localization = localization;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<MediaStream> GetExternalSubtitleStreams(Video video,
|
public List<MediaStream> GetExternalSubtitleStreams(Video video,
|
||||||
int startIndex,
|
int startIndex,
|
||||||
IDirectoryService directoryService,
|
IDirectoryService directoryService,
|
||||||
bool clearCache)
|
bool clearCache)
|
||||||
{
|
{
|
||||||
var files = GetSubtitleFiles(video, directoryService, _fileSystem, clearCache);
|
|
||||||
|
|
||||||
var streams = new List<MediaStream>();
|
var streams = new List<MediaStream>();
|
||||||
|
|
||||||
var videoFileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(video.Path);
|
GetExternalSubtitleStreams(streams, video.ContainingFolderPath, video.Path, startIndex, directoryService, clearCache);
|
||||||
|
|
||||||
|
startIndex += streams.Count;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GetExternalSubtitleStreams(streams, video.GetInternalMetadataPath(), video.Path, startIndex, directoryService, clearCache);
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return streams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> GetExternalSubtitleFiles(Video video,
|
||||||
|
IDirectoryService directoryService,
|
||||||
|
bool clearCache)
|
||||||
|
{
|
||||||
|
var streams = GetExternalSubtitleStreams(video, 0, directoryService, clearCache);
|
||||||
|
|
||||||
|
var list = new List<string>();
|
||||||
|
|
||||||
|
foreach (var stream in streams)
|
||||||
|
{
|
||||||
|
list.Add(stream.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetExternalSubtitleStreams(List<MediaStream> streams, string folder,
|
||||||
|
string videoPath,
|
||||||
|
int startIndex,
|
||||||
|
IDirectoryService directoryService,
|
||||||
|
bool clearCache)
|
||||||
|
{
|
||||||
|
var videoFileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(videoPath);
|
||||||
videoFileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(videoFileNameWithoutExtension);
|
videoFileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(videoFileNameWithoutExtension);
|
||||||
|
|
||||||
|
var files = directoryService.GetFilePaths(folder, clearCache);
|
||||||
|
|
||||||
foreach (var fullName in files)
|
foreach (var fullName in files)
|
||||||
{
|
{
|
||||||
|
var extension = Path.GetExtension(fullName);
|
||||||
|
|
||||||
|
if (!SubtitleExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var fileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(fullName);
|
var fileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(fullName);
|
||||||
fileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(fileNameWithoutExtension);
|
fileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(fileNameWithoutExtension);
|
||||||
|
|
||||||
|
if (!string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension + ".", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var codec = Path.GetExtension(fullName).ToLower().TrimStart('.');
|
var codec = Path.GetExtension(fullName).ToLower().TrimStart('.');
|
||||||
|
|
||||||
if (string.Equals(codec, "txt", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(codec, "txt", StringComparison.OrdinalIgnoreCase))
|
||||||
@ -98,8 +160,6 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return streams;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string NormalizeFilenameForSubtitleComparison(string filename)
|
private string NormalizeFilenameForSubtitleComparison(string filename)
|
||||||
@ -115,48 +175,5 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
|
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<string> SubtitleExtensions
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return new[] { ".srt", ".ssa", ".ass", ".sub", ".smi", ".sami", ".vtt" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<string> GetSubtitleFiles(Video video, IDirectoryService directoryService, IFileSystem fileSystem, bool clearCache)
|
|
||||||
{
|
|
||||||
var containingPath = video.ContainingFolderPath;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(containingPath))
|
|
||||||
{
|
|
||||||
throw new ArgumentException(string.Format("Cannot search for items that don't have a path: {0} {1}", video.Name, video.Id));
|
|
||||||
}
|
|
||||||
|
|
||||||
var files = fileSystem.GetFilePaths(containingPath, clearCache);
|
|
||||||
|
|
||||||
var videoFileNameWithoutExtension = fileSystem.GetFileNameWithoutExtension(video.Path);
|
|
||||||
|
|
||||||
return files.Where(i =>
|
|
||||||
{
|
|
||||||
var extension = Path.GetExtension(i);
|
|
||||||
|
|
||||||
if (SubtitleExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
var fileNameWithoutExtension = fileSystem.GetFileNameWithoutExtension(i);
|
|
||||||
|
|
||||||
if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension + ".", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Controller.IO;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.Subtitles
|
namespace MediaBrowser.Providers.Subtitles
|
||||||
@ -30,17 +30,19 @@ namespace MediaBrowser.Providers.Subtitles
|
|||||||
private readonly ILibraryMonitor _monitor;
|
private readonly ILibraryMonitor _monitor;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly IMediaSourceManager _mediaSourceManager;
|
private readonly IMediaSourceManager _mediaSourceManager;
|
||||||
|
private readonly IServerConfigurationManager _config;
|
||||||
|
|
||||||
public event EventHandler<SubtitleDownloadEventArgs> SubtitlesDownloaded;
|
public event EventHandler<SubtitleDownloadEventArgs> SubtitlesDownloaded;
|
||||||
public event EventHandler<SubtitleDownloadFailureEventArgs> SubtitleDownloadFailure;
|
public event EventHandler<SubtitleDownloadFailureEventArgs> SubtitleDownloadFailure;
|
||||||
|
|
||||||
public SubtitleManager(ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager)
|
public SubtitleManager(ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager, IServerConfigurationManager config)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_monitor = monitor;
|
_monitor = monitor;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_mediaSourceManager = mediaSourceManager;
|
_mediaSourceManager = mediaSourceManager;
|
||||||
|
_config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddParts(IEnumerable<ISubtitleProvider> subtitleProviders)
|
public void AddParts(IEnumerable<ISubtitleProvider> subtitleProviders)
|
||||||
@ -102,6 +104,11 @@ namespace MediaBrowser.Providers.Subtitles
|
|||||||
return results.SelectMany(i => i).ToArray();
|
return results.SelectMany(i => i).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SubtitleOptions GetOptions()
|
||||||
|
{
|
||||||
|
return _config.GetConfiguration<SubtitleOptions>("subtitles");
|
||||||
|
}
|
||||||
|
|
||||||
public async Task DownloadSubtitles(Video video,
|
public async Task DownloadSubtitles(Video video,
|
||||||
string subtitleId,
|
string subtitleId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
@ -109,49 +116,37 @@ namespace MediaBrowser.Providers.Subtitles
|
|||||||
var parts = subtitleId.Split(new[] { '_' }, 2);
|
var parts = subtitleId.Split(new[] { '_' }, 2);
|
||||||
var provider = GetProvider(parts.First());
|
var provider = GetProvider(parts.First());
|
||||||
|
|
||||||
|
var saveInMediaFolder = GetOptions().SaveSubtitlesInMediaFolders && video.SupportsLocalMetadata;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await GetRemoteSubtitles(subtitleId, cancellationToken).ConfigureAwait(false);
|
var response = await GetRemoteSubtitles(subtitleId, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
using (var stream = response.Stream)
|
using (var stream = response.Stream)
|
||||||
{
|
{
|
||||||
var savePath = Path.Combine(_fileSystem.GetDirectoryName(video.Path),
|
using (var memoryStream = new MemoryStream())
|
||||||
_fileSystem.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower());
|
|
||||||
|
|
||||||
if (response.IsForced)
|
|
||||||
{
|
{
|
||||||
savePath += ".forced";
|
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||||
}
|
memoryStream.Position = 0;
|
||||||
|
|
||||||
savePath += "." + response.Format.ToLower();
|
var savePaths = new List<string>();
|
||||||
|
var saveFileName = _fileSystem.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower();
|
||||||
|
|
||||||
_logger.Info("Saving subtitles to {0}", savePath);
|
if (response.IsForced)
|
||||||
|
|
||||||
_monitor.ReportFileSystemChangeBeginning(savePath);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//var isText = MediaStream.IsTextFormat(response.Format);
|
|
||||||
|
|
||||||
using (var fs = _fileSystem.GetFileStream(savePath, FileOpenMode.Create, FileAccessMode.Write,
|
|
||||||
FileShareMode.Read, true))
|
|
||||||
{
|
{
|
||||||
await stream.CopyToAsync(fs).ConfigureAwait(false);
|
saveFileName += ".forced";
|
||||||
}
|
}
|
||||||
|
|
||||||
EventHelper.FireEventIfNotNull(SubtitlesDownloaded, this, new SubtitleDownloadEventArgs
|
saveFileName += "." + response.Format.ToLower();
|
||||||
{
|
|
||||||
Item = video,
|
|
||||||
Format = response.Format,
|
|
||||||
Language = response.Language,
|
|
||||||
IsForced = response.IsForced,
|
|
||||||
Provider = provider.Name
|
|
||||||
|
|
||||||
}, _logger);
|
if (saveInMediaFolder)
|
||||||
}
|
{
|
||||||
finally
|
savePaths.Add(Path.Combine(video.ContainingFolderPath, saveFileName));
|
||||||
{
|
}
|
||||||
_monitor.ReportFileSystemChangeComplete(savePath, false);
|
|
||||||
|
savePaths.Add(Path.Combine(video.GetInternalMetadataPath(), saveFileName));
|
||||||
|
|
||||||
|
await TrySaveToFiles(memoryStream, savePaths).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,6 +168,46 @@ namespace MediaBrowser.Providers.Subtitles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task TrySaveToFiles(Stream stream, List<string> savePaths)
|
||||||
|
{
|
||||||
|
Exception exceptionToThrow = null;
|
||||||
|
|
||||||
|
foreach (var savePath in savePaths)
|
||||||
|
{
|
||||||
|
_logger.Info("Saving subtitles to {0}", savePath);
|
||||||
|
|
||||||
|
_monitor.ReportFileSystemChangeBeginning(savePath);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var fs = _fileSystem.GetFileStream(savePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
|
||||||
|
{
|
||||||
|
await stream.CopyToAsync(fs).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (exceptionToThrow == null)
|
||||||
|
{
|
||||||
|
exceptionToThrow = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_monitor.ReportFileSystemChangeComplete(savePath, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exceptionToThrow != null)
|
||||||
|
{
|
||||||
|
throw exceptionToThrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Task<RemoteSubtitleInfo[]> SearchSubtitles(Video video, string language, bool? isPerfectMatch, CancellationToken cancellationToken)
|
public Task<RemoteSubtitleInfo[]> SearchSubtitles(Video video, string language, bool? isPerfectMatch, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (video.LocationType != LocationType.FileSystem ||
|
if (video.LocationType != LocationType.FileSystem ||
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("3.2.36.1")]
|
[assembly: AssemblyVersion("3.2.36.2")]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user